diff --git a/CMake/GetPrerequisites.cmake b/CMake/GetPrerequisites.cmake index be5e8548cb..da4e1cad29 100644 --- a/CMake/GetPrerequisites.cmake +++ b/CMake/GetPrerequisites.cmake @@ -1,1095 +1,1095 @@ # Special version of GetPrerequisites that implements a caching mechanism. # Thank you to Daniel Maleike for the contribution message("Using MITK version of GetPrerequisites.cmake") # Distributed under the OSI-approved BSD 3-Clause License. See accompanying # file Copyright.txt or https://cmake.org/licensing for details. #.rst: # GetPrerequisites # ---------------- # # Functions to analyze and list executable file prerequisites. # # This module provides functions to list the .dll, .dylib or .so files # that an executable or shared library file depends on. (Its # prerequisites.) # # It uses various tools to obtain the list of required shared library # files: # # :: # # dumpbin (Windows) # ldd (Linux/Unix) # otool (macOS) # # The following functions are provided by this module: # # :: # # get_prerequisites # list_prerequisites # list_prerequisites_by_glob # gp_append_unique # is_file_executable # gp_item_default_embedded_path # (projects can override with gp_item_default_embedded_path_override) # gp_resolve_item # (projects can override with gp_resolve_item_override) # gp_resolved_file_type # (projects can override with gp_resolved_file_type_override) # gp_file_type # # Requires CMake 2.6 or greater because it uses function, break, return # and PARENT_SCOPE. # # :: # # GET_PREREQUISITES( # []) # # Get the list of shared library files required by . The list # in the variable named should be empty on first # entry to this function. On exit, will contain the # list of required shared library files. # # is the full path to an executable file. # is the name of a CMake variable to contain the results. # must be 0 or 1 indicating whether to include or # exclude "system" prerequisites. If is set to 1 all # prerequisites will be found recursively, if set to 0 only direct # prerequisites are listed. is the path to the top level -# executable used for @executable_path replacment on the Mac. is +# executable used for @executable_path replacement on the Mac. is # a list of paths where libraries might be found: these paths are # searched first when a target without any path info is given. Then # standard system locations are also searched: PATH, Framework # locations, /usr/lib... # # :: # # LIST_PREREQUISITES( [ [ []]]) # # Print a message listing the prerequisites of . # # is the name of a shared library or executable target or the # full path to a shared library or executable file. If is set # to 1 all prerequisites will be found recursively, if set to 0 only # direct prerequisites are listed. must be 0 or 1 # indicating whether to include or exclude "system" prerequisites. With # set to 0 only the full path names of the prerequisites are -# printed, set to 1 extra informatin will be displayed. +# printed, set to 1 extra information will be displayed. # # :: # # LIST_PREREQUISITES_BY_GLOB( ) # # Print the prerequisites of shared library and executable files # matching a globbing pattern. is GLOB or GLOB_RECURSE and # is a globbing expression used with "file(GLOB" or # "file(GLOB_RECURSE" to retrieve a list of matching files. If a # matching file is executable, its prerequisites are listed. # # Any additional (optional) arguments provided are passed along as the # optional arguments to the list_prerequisites calls. # # :: # # GP_APPEND_UNIQUE( ) # # Append to the list variable only if the value is # not already in the list. # # :: # # IS_FILE_EXECUTABLE( ) # # Return 1 in if is a binary executable, 0 # otherwise. # # :: # # GP_ITEM_DEFAULT_EMBEDDED_PATH( ) # # Return the path that others should refer to the item by when the item # is embedded inside a bundle. # # Override on a per-project basis by providing a project-specific # gp_item_default_embedded_path_override function. # # :: # # GP_RESOLVE_ITEM( # []) # # Resolve an item into an existing full path file. # # Override on a per-project basis by providing a project-specific # gp_resolve_item_override function. # # :: # # GP_RESOLVED_FILE_TYPE( # []) # # Return the type of with respect to . String # describing type of prerequisite is returned in variable named # . # # Use and if necessary to resolve non-absolute # values -- but only for non-embedded items. # # Possible types are: # # :: # # system # local # embedded # other # # Override on a per-project basis by providing a project-specific # gp_resolved_file_type_override function. # # :: # # GP_FILE_TYPE( ) # # Return the type of with respect to . String # describing type of prerequisite is returned in variable named # . # # Possible types are: # # :: # # system # local # embedded # other function(gp_append_unique list_var value) set(contains 0) foreach(item ${${list_var}}) if(item STREQUAL "${value}") set(contains 1) break() endif() endforeach() if(NOT contains) set(${list_var} ${${list_var}} "${value}" PARENT_SCOPE) endif() endfunction() function(is_file_executable file result_var) # # A file is not executable until proven otherwise: # set(${result_var} 0 PARENT_SCOPE) get_filename_component(file_full "${file}" ABSOLUTE) string(TOLOWER "${file_full}" file_full_lower) # If file name ends in .exe on Windows, *assume* executable: # if(WIN32 AND NOT UNIX) if("${file_full_lower}" MATCHES "\\.exe$") set(${result_var} 1 PARENT_SCOPE) return() endif() # A clause could be added here that uses output or return value of dumpbin # to determine ${result_var}. In 99%+? practical cases, the exe name # match will be sufficient... # endif() # Use the information returned from the Unix shell command "file" to # determine if ${file_full} should be considered an executable file... # # If the file command's output contains "executable" and does *not* contain # "text" then it is likely an executable suitable for prerequisite analysis # via the get_prerequisites macro. # if(UNIX) if(NOT file_cmd) find_program(file_cmd "file") mark_as_advanced(file_cmd) endif() if(file_cmd) execute_process(COMMAND "${file_cmd}" "${file_full}" RESULT_VARIABLE file_rv OUTPUT_VARIABLE file_ov ERROR_VARIABLE file_ev OUTPUT_STRIP_TRAILING_WHITESPACE ) if(NOT file_rv STREQUAL "0") message(FATAL_ERROR "${file_cmd} failed: ${file_rv}\n${file_ev}") endif() # Replace the name of the file in the output with a placeholder token # (the string " _file_full_ ") so that just in case the path name of # the file contains the word "text" or "executable" we are not fooled # into thinking "the wrong thing" because the file name matches the # other 'file' command output we are looking for... # string(REPLACE "${file_full}" " _file_full_ " file_ov "${file_ov}") string(TOLOWER "${file_ov}" file_ov) #message(STATUS "file_ov='${file_ov}'") if("${file_ov}" MATCHES "executable") #message(STATUS "executable!") if("${file_ov}" MATCHES "text") #message(STATUS "but text, so *not* a binary executable!") else() set(${result_var} 1 PARENT_SCOPE) return() endif() endif() # Also detect position independent executables on Linux, # where "file" gives "shared object ... (uses shared libraries)" if("${file_ov}" MATCHES "shared object.*\(uses shared libs\)") set(${result_var} 1 PARENT_SCOPE) return() endif() # "file" version 5.22 does not print "(used shared libraries)" # but uses "interpreter" if("${file_ov}" MATCHES "shared object.*interpreter") set(${result_var} 1 PARENT_SCOPE) return() endif() else() message(STATUS "warning: No 'file' command, skipping execute_process...") endif() endif() endfunction() function(gp_item_default_embedded_path item default_embedded_path_var) # On Windows and Linux, "embed" prerequisites in the same directory # as the executable by default: # set(path "@executable_path") set(overridden 0) # On the Mac, relative to the executable depending on the type # of the thing we are embedding: # if(APPLE) # # The assumption here is that all executables in the bundle will be # in same-level-directories inside the bundle. The parent directory # of an executable inside the bundle should be MacOS or a sibling of # MacOS and all embedded paths returned from here will begin with # "@executable_path/../" and will work from all executables in all # such same-level-directories inside the bundle. # # By default, embed things right next to the main bundle executable: # set(path "@executable_path/../../Contents/MacOS") # Embed .dylibs right next to the main bundle executable: # if(item MATCHES "\\.dylib$") set(path "@executable_path/../MacOS") set(overridden 1) endif() # Embed frameworks in the embedded "Frameworks" directory (sibling of MacOS): # if(NOT overridden) if(item MATCHES "[^/]+\\.framework/") set(path "@executable_path/../Frameworks") set(overridden 1) endif() endif() endif() # Provide a hook so that projects can override the default embedded location # of any given library by whatever logic they choose: # if(COMMAND gp_item_default_embedded_path_override) gp_item_default_embedded_path_override("${item}" path) endif() set(${default_embedded_path_var} "${path}" PARENT_SCOPE) endfunction() function(gp_resolve_item context item exepath dirs resolved_item_var) set(resolved 0) set(resolved_item "${item}") if(ARGC GREATER 5) set(rpaths "${ARGV5}") else() set(rpaths "") endif() # Is it already resolved? # if(IS_ABSOLUTE "${resolved_item}" AND EXISTS "${resolved_item}") set(resolved 1) endif() if(NOT resolved) if(item MATCHES "^@executable_path") # # @executable_path references are assumed relative to exepath # string(REPLACE "@executable_path" "${exepath}" ri "${item}") get_filename_component(ri "${ri}" ABSOLUTE) if(EXISTS "${ri}") #message(STATUS "info: embedded item exists (${ri})") set(resolved 1) set(resolved_item "${ri}") else() message(STATUS "warning: embedded item does not exist '${ri}'") endif() endif() endif() if(NOT resolved) if(item MATCHES "^@loader_path") # # @loader_path references are assumed relative to the # PATH of the given "context" (presumably another library) # get_filename_component(contextpath "${context}" PATH) string(REPLACE "@loader_path" "${contextpath}" ri "${item}") get_filename_component(ri "${ri}" ABSOLUTE) if(EXISTS "${ri}") #message(STATUS "info: embedded item exists (${ri})") set(resolved 1) set(resolved_item "${ri}") else() message(STATUS "warning: embedded item does not exist '${ri}'") endif() endif() endif() if(NOT resolved) if(item MATCHES "^@rpath") # # @rpath references are relative to the paths built into the binaries with -rpath # We handle this case like we do for other Unixes # string(REPLACE "@rpath/" "" norpath_item "${item}") set(ri "ri-NOTFOUND") find_file(ri "${norpath_item}" ${exepath} ${dirs} ${rpaths} NO_DEFAULT_PATH) if(ri) #message(STATUS "info: 'find_file' in exepath/dirs/rpaths (${ri})") set(resolved 1) set(resolved_item "${ri}") set(ri "ri-NOTFOUND") endif() endif() endif() if(NOT resolved) set(ri "ri-NOTFOUND") find_file(ri "${item}" ${exepath} ${dirs} NO_DEFAULT_PATH) find_file(ri "${item}" ${exepath} ${dirs} /usr/lib) get_filename_component(basename_item "${item}" NAME) find_file(ri "${basename_item}" PATHS ${exepath} ${dirs} NO_DEFAULT_PATH) find_file(ri "${basename_item}" PATHS /usr/lib) if(ri) #message(STATUS "info: 'find_file' in exepath/dirs (${ri})") set(resolved 1) set(resolved_item "${ri}") set(ri "ri-NOTFOUND") endif() endif() if(NOT resolved) if(item MATCHES "[^/]+\\.framework/") set(fw "fw-NOTFOUND") find_file(fw "${item}" "~/Library/Frameworks" "/Library/Frameworks" "/System/Library/Frameworks" ) if(fw) #message(STATUS "info: 'find_file' found framework (${fw})") set(resolved 1) set(resolved_item "${fw}") set(fw "fw-NOTFOUND") endif() endif() endif() # Using find_program on Windows will find dll files that are in the PATH. # (Converting simple file names into full path names if found.) # if(WIN32 AND NOT UNIX) if(NOT resolved) set(ri "ri-NOTFOUND") find_program(ri "${item}" PATHS ${exepath} ${dirs} NO_DEFAULT_PATH) find_program(ri "${item}" PATHS ${exepath} ${dirs}) if(ri) #message(STATUS "info: 'find_program' in exepath/dirs (${ri})") set(resolved 1) set(resolved_item "${ri}") set(ri "ri-NOTFOUND") endif() endif() endif() # Provide a hook so that projects can override item resolution # by whatever logic they choose: # if(COMMAND gp_resolve_item_override) gp_resolve_item_override("${context}" "${item}" "${exepath}" "${dirs}" resolved_item resolved) endif() if(NOT resolved) message(STATUS " warning: cannot resolve item '${item}' possible problems: need more directories? need to use InstallRequiredSystemLibraries? run in install tree instead of build tree? ") # message(STATUS " #****************************************************************************** #warning: cannot resolve item '${item}' # # possible problems: # need more directories? # need to use InstallRequiredSystemLibraries? # run in install tree instead of build tree? # # context='${context}' # item='${item}' # exepath='${exepath}' # dirs='${dirs}' # resolved_item_var='${resolved_item_var}' #****************************************************************************** #") endif() set(${resolved_item_var} "${resolved_item}" PARENT_SCOPE) endfunction() function(gp_resolved_file_type original_file file exepath dirs type_var) if(ARGC GREATER 5) set(rpaths "${ARGV5}") else() set(rpaths "") endif() #message(STATUS "**") if(NOT IS_ABSOLUTE "${original_file}") message(STATUS "warning: gp_resolved_file_type expects absolute full path for first arg original_file") endif() if(IS_ABSOLUTE "${original_file}") get_filename_component(original_file "${original_file}" ABSOLUTE) # canonicalize path endif() set(is_embedded 0) set(is_local 0) set(is_system 0) set(resolved_file "${file}") if("${file}" MATCHES "^@(executable|loader)_path") set(is_embedded 1) endif() if(NOT is_embedded) if(NOT IS_ABSOLUTE "${file}") gp_resolve_item("${original_file}" "${file}" "${exepath}" "${dirs}" resolved_file "${rpaths}") endif() if(IS_ABSOLUTE "${resolved_file}") get_filename_component(resolved_file "${resolved_file}" ABSOLUTE) # canonicalize path endif() string(TOLOWER "${original_file}" original_lower) string(TOLOWER "${resolved_file}" lower) if(UNIX) if(resolved_file MATCHES "^(/lib/|/lib32/|/libx32/|/lib64/|/usr/lib/|/usr/lib32/|/usr/libx32/|/usr/lib64/|/usr/X11R6/|/usr/bin/)") set(is_system 1) endif() endif() if(APPLE) if(resolved_file MATCHES "^(/System/Library/|/usr/lib/)") set(is_system 1) endif() endif() if(WIN32) string(TOLOWER "$ENV{SystemRoot}" sysroot) file(TO_CMAKE_PATH "${sysroot}" sysroot) string(TOLOWER "$ENV{windir}" windir) file(TO_CMAKE_PATH "${windir}" windir) if(lower MATCHES "^(${sysroot}/sys(tem|wow)|${windir}/sys(tem|wow)|(.*/)*(msvc|api-ms-win-)[^/]+dll)") set(is_system 1) endif() if(UNIX) # if cygwin, we can get the properly formed windows paths from cygpath find_program(CYGPATH_EXECUTABLE cygpath) if(CYGPATH_EXECUTABLE) execute_process(COMMAND ${CYGPATH_EXECUTABLE} -W RESULT_VARIABLE env_rv OUTPUT_VARIABLE env_windir ERROR_VARIABLE env_ev OUTPUT_STRIP_TRAILING_WHITESPACE) if(NOT env_rv STREQUAL "0") message(FATAL_ERROR "${CYGPATH_EXECUTABLE} -W failed: ${env_rv}\n${env_ev}") endif() execute_process(COMMAND ${CYGPATH_EXECUTABLE} -S RESULT_VARIABLE env_rv OUTPUT_VARIABLE env_sysdir ERROR_VARIABLE env_ev OUTPUT_STRIP_TRAILING_WHITESPACE) if(NOT env_rv STREQUAL "0") message(FATAL_ERROR "${CYGPATH_EXECUTABLE} -S failed: ${env_rv}\n${env_ev}") endif() string(TOLOWER "${env_windir}" windir) string(TOLOWER "${env_sysdir}" sysroot) if(lower MATCHES "^(${sysroot}/sys(tem|wow)|${windir}/sys(tem|wow)|(.*/)*(msvc|api-ms-win-)[^/]+dll)") set(is_system 1) endif() endif() endif() endif() if(NOT is_system) get_filename_component(original_path "${original_lower}" PATH) get_filename_component(path "${lower}" PATH) if(original_path STREQUAL path) set(is_local 1) else() string(LENGTH "${original_path}/" original_length) string(LENGTH "${lower}" path_length) if(${path_length} GREATER ${original_length}) string(SUBSTRING "${lower}" 0 ${original_length} path) if("${original_path}/" STREQUAL path) set(is_embedded 1) endif() endif() endif() endif() endif() # Return type string based on computed booleans: # set(type "other") if(is_system) set(type "system") elseif(is_embedded) set(type "embedded") elseif(is_local) set(type "local") endif() #message(STATUS "gp_resolved_file_type: '${file}' '${resolved_file}'") #message(STATUS " type: '${type}'") if(NOT is_embedded) if(NOT IS_ABSOLUTE "${resolved_file}") if(lower MATCHES "^(msvc|api-ms-win-)[^/]+dll" AND is_system) message(STATUS "info: non-absolute msvc file '${file}' returning type '${type}'") else() message(STATUS "warning: gp_resolved_file_type non-absolute file '${file}' returning type '${type}' -- possibly incorrect") endif() endif() endif() # Provide a hook so that projects can override the decision on whether a # library belongs to the system or not by whatever logic they choose: # if(COMMAND gp_resolved_file_type_override) gp_resolved_file_type_override("${resolved_file}" type) endif() set(${type_var} "${type}" PARENT_SCOPE) #message(STATUS "**") endfunction() function(gp_file_type original_file file type_var) if(NOT IS_ABSOLUTE "${original_file}") message(STATUS "warning: gp_file_type expects absolute full path for first arg original_file") endif() get_filename_component(exepath "${original_file}" PATH) set(type "") gp_resolved_file_type("${original_file}" "${file}" "${exepath}" "" type) set(${type_var} "${type}" PARENT_SCOPE) endfunction() function(get_prerequisites_clear_cache) set_property(GLOBAL PROPERTY prerequisites_cachevariables "") endfunction(get_prerequisites_clear_cache) function(get_prerequisites target prerequisites_var exclude_system recurse exepath dirs) # See if we know the answer from our cache. If so, we are done early # incorporate all parameters into cache key string(SHA1 param_hash "${exclude_system}_${recurse}_${exepath}_${dirs}") set(prerequisites_cache_var_name "prerequisites_cache_${target}_${param_hash}") string(TOLOWER "${prerequisites_cache_var_name}" prerequisites_cache_var_name) string(REGEX REPLACE "[:-\\/.]" "_" prerequisites_cache_var_name "${prerequisites_cache_var_name}") get_property(cache_value GLOBAL PROPERTY ${prerequisites_cache_var_name} SET) if (cache_value) get_property(cache_value GLOBAL PROPERTY ${prerequisites_cache_var_name}) # if defined, then get value if (cache_value) # already something non-empty -> append set(${prerequisites_var} "${${prerequisites_var}};${cache_value}" PARENT_SCOPE) endif() return() endif() set(verbose 0) set(eol_char "E") if(ARGC GREATER 6) set(rpaths "${ARGV6}") else() set(rpaths "") endif() if(NOT IS_ABSOLUTE "${target}") message("warning: target '${target}' is not absolute...") endif() if(NOT EXISTS "${target}") message("warning: target '${target}' does not exist...") set(${prerequisites_var} "" PARENT_SCOPE) return() endif() set(gp_cmd_paths ${gp_cmd_paths} "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\14.0;InstallDir]/../../VC/bin" "$ENV{VS140COMNTOOLS}/../../VC/bin" "C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/bin" "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\12.0;InstallDir]/../../VC/bin" "$ENV{VS120COMNTOOLS}/../../VC/bin" "C:/Program Files (x86)/Microsoft Visual Studio 12.0/VC/bin" "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\11.0;InstallDir]/../../VC/bin" "$ENV{VS110COMNTOOLS}/../../VC/bin" "C:/Program Files (x86)/Microsoft Visual Studio 11.0/VC/bin" "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\10.0;InstallDir]/../../VC/bin" "$ENV{VS100COMNTOOLS}/../../VC/bin" "C:/Program Files (x86)/Microsoft Visual Studio 10.0/VC/bin" "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\9.0;InstallDir]/../../VC/bin" "$ENV{VS90COMNTOOLS}/../../VC/bin" "C:/Program Files/Microsoft Visual Studio 9.0/VC/bin" "C:/Program Files (x86)/Microsoft Visual Studio 9.0/VC/bin" "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\8.0;InstallDir]/../../VC/bin" "$ENV{VS80COMNTOOLS}/../../VC/bin" "C:/Program Files/Microsoft Visual Studio 8/VC/BIN" "C:/Program Files (x86)/Microsoft Visual Studio 8/VC/BIN" "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\7.1;InstallDir]/../../VC7/bin" "$ENV{VS71COMNTOOLS}/../../VC7/bin" "C:/Program Files/Microsoft Visual Studio .NET 2003/VC7/BIN" "C:/Program Files (x86)/Microsoft Visual Studio .NET 2003/VC7/BIN" "/usr/local/bin" "/usr/bin" ) # # # Try to choose the right tool by default. Caller can set gp_tool prior to # calling this function to force using a different tool. # if(NOT gp_tool) set(gp_tool "ldd") if(APPLE) set(gp_tool "otool") endif() if(WIN32 AND NOT UNIX) # This is how to check for cygwin, har! find_program(gp_dumpbin "dumpbin" PATHS ${gp_cmd_paths}) if(gp_dumpbin) set(gp_tool "dumpbin") else() # Try harder. set(gp_tool "objdump") endif() endif() endif() find_program(gp_cmd ${gp_tool} PATHS ${gp_cmd_paths}) if(NOT gp_cmd) message(STATUS "warning: could not find '${gp_tool}' - cannot analyze prerequisites...") return() endif() set(gp_cmd_maybe_filter) # optional command to pre-filter gp_tool results if(gp_tool STREQUAL "ldd") set(gp_cmd_args "") set(gp_regex "^[\t ]*[^\t ]+ => ([^\t\(]+) .*${eol_char}$") set(gp_regex_error "not found${eol_char}$") set(gp_regex_fallback "^[\t ]*([^\t ]+) => ([^\t ]+).*${eol_char}$") set(gp_regex_cmp_count 1) elseif(gp_tool STREQUAL "otool") set(gp_cmd_args "-L") set(gp_regex "^\t([^\t]+) \\(compatibility version ([0-9]+.[0-9]+.[0-9]+), current version ([0-9]+.[0-9]+.[0-9]+)\\)${eol_char}$") set(gp_regex_error "") set(gp_regex_fallback "") set(gp_regex_cmp_count 3) elseif(gp_tool STREQUAL "dumpbin") set(gp_cmd_args "/dependents") set(gp_regex "^ ([^ ].*[Dd][Ll][Ll])${eol_char}$") set(gp_regex_error "") set(gp_regex_fallback "") set(gp_regex_cmp_count 1) elseif(gp_tool STREQUAL "objdump") set(gp_cmd_args "-p") set(gp_regex "^\t*DLL Name: (.*\\.[Dd][Ll][Ll])${eol_char}$") set(gp_regex_error "") set(gp_regex_fallback "") set(gp_regex_cmp_count 1) # objdump generates copious output so we create a grep filter to pre-filter results if(WIN32) find_program(gp_grep_cmd findstr) else() find_program(gp_grep_cmd grep) endif() if(gp_grep_cmd) set(gp_cmd_maybe_filter COMMAND ${gp_grep_cmd} "-a" "^[[:blank:]]*DLL Name: ") endif() else() message(STATUS "warning: gp_tool='${gp_tool}' is an unknown tool...") message(STATUS "CMake function get_prerequisites needs more code to handle '${gp_tool}'") message(STATUS "Valid gp_tool values are dumpbin, ldd, objdump and otool.") return() endif() if(gp_tool STREQUAL "dumpbin") # When running dumpbin, it also needs the "Common7/IDE" directory in the # PATH. It will already be in the PATH if being run from a Visual Studio # command prompt. Add it to the PATH here in case we are running from a # different command prompt. # get_filename_component(gp_cmd_dir "${gp_cmd}" PATH) get_filename_component(gp_cmd_dlls_dir "${gp_cmd_dir}/../../Common7/IDE" ABSOLUTE) # Use cmake paths as a user may have a PATH element ending with a backslash. # This will escape the list delimiter and create havoc! if(EXISTS "${gp_cmd_dlls_dir}") # only add to the path if it is not already in the path set(gp_found_cmd_dlls_dir 0) file(TO_CMAKE_PATH "$ENV{PATH}" env_path) foreach(gp_env_path_element ${env_path}) if(gp_env_path_element STREQUAL gp_cmd_dlls_dir) set(gp_found_cmd_dlls_dir 1) endif() endforeach() if(NOT gp_found_cmd_dlls_dir) file(TO_NATIVE_PATH "${gp_cmd_dlls_dir}" gp_cmd_dlls_dir) set(ENV{PATH} "$ENV{PATH};${gp_cmd_dlls_dir}") endif() endif() endif() # # if(gp_tool STREQUAL "ldd") set(old_ld_env "$ENV{LD_LIBRARY_PATH}") set(new_ld_env "${exepath}") foreach(dir ${dirs}) string(APPEND new_ld_env ":${dir}") endforeach() set(ENV{LD_LIBRARY_PATH} "${new_ld_env}:$ENV{LD_LIBRARY_PATH}") endif() # Track new prerequisites at each new level of recursion. Start with an # empty list at each level: # set(unseen_prereqs) # Run gp_cmd on the target: # execute_process( COMMAND ${gp_cmd} ${gp_cmd_args} ${target} ${gp_cmd_maybe_filter} RESULT_VARIABLE gp_rv OUTPUT_VARIABLE gp_cmd_ov ERROR_VARIABLE gp_ev ) if(gp_tool STREQUAL "dumpbin") # Exclude delay load dependencies under windows (they are listed in dumpbin output after the message below) string(FIND "${gp_cmd_ov}" "Image has the following delay load dependencies" gp_delayload_pos) if (${gp_delayload_pos} GREATER -1) string(SUBSTRING "${gp_cmd_ov}" 0 ${gp_delayload_pos} gp_cmd_ov_no_delayload_deps) string(SUBSTRING "${gp_cmd_ov}" ${gp_delayload_pos} -1 gp_cmd_ov_delayload_deps) if (verbose) message(STATUS "GetPrequisites(${target}) : ignoring the following delay load dependencies :\n ${gp_cmd_ov_delayload_deps}") endif() set(gp_cmd_ov ${gp_cmd_ov_no_delayload_deps}) endif() endif() if(NOT gp_rv STREQUAL "0") if(gp_tool STREQUAL "dumpbin") # dumpbin error messages seem to go to stdout message(FATAL_ERROR "${gp_cmd} failed: ${gp_rv}\n${gp_ev}\n${gp_cmd_ov}") else() message(FATAL_ERROR "${gp_cmd} failed: ${gp_rv}\n${gp_ev}") endif() endif() if(gp_tool STREQUAL "ldd") set(ENV{LD_LIBRARY_PATH} "${old_ld_env}") endif() if(verbose) message(STATUS "") message(STATUS "gp_cmd_ov='${gp_cmd_ov}'") message(STATUS "") endif() get_filename_component(target_dir "${target}" PATH) # Convert to a list of lines: # string(REPLACE ";" "\\;" candidates "${gp_cmd_ov}") string(REPLACE "\n" "${eol_char};" candidates "${candidates}") # check for install id and remove it from list, since otool -L can include a # reference to itself set(gp_install_id) if(gp_tool STREQUAL "otool") execute_process( COMMAND otool -D ${target} RESULT_VARIABLE otool_rv OUTPUT_VARIABLE gp_install_id_ov ERROR_VARIABLE otool_ev ) if(NOT otool_rv STREQUAL "0") message(FATAL_ERROR "otool -D failed: ${otool_rv}\n${otool_ev}") endif() # second line is install name string(REGEX REPLACE ".*:\n" "" gp_install_id "${gp_install_id_ov}") if(gp_install_id) # trim string(REGEX MATCH "[^\n ].*[^\n ]" gp_install_id "${gp_install_id}") #message("INSTALL ID is \"${gp_install_id}\"") endif() endif() # Analyze each line for file names that match the regular expression: # list(LENGTH ${prerequisites_var} list_length_before_candidates) set(targets_added "") foreach(candidate ${candidates}) if("${candidate}" MATCHES "${gp_regex}") # Extract information from each candidate: if(gp_regex_error AND "${candidate}" MATCHES "${gp_regex_error}") string(REGEX REPLACE "${gp_regex_fallback}" "\\1" raw_item "${candidate}") else() string(REGEX REPLACE "${gp_regex}" "\\1" raw_item "${candidate}") endif() if(gp_regex_cmp_count GREATER 1) string(REGEX REPLACE "${gp_regex}" "\\2" raw_compat_version "${candidate}") string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\1" compat_major_version "${raw_compat_version}") string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\2" compat_minor_version "${raw_compat_version}") string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\3" compat_patch_version "${raw_compat_version}") endif() if(gp_regex_cmp_count GREATER 2) string(REGEX REPLACE "${gp_regex}" "\\3" raw_current_version "${candidate}") string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\1" current_major_version "${raw_current_version}") string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\2" current_minor_version "${raw_current_version}") string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\3" current_patch_version "${raw_current_version}") endif() # Use the raw_item as the list entries returned by this function. Use the # gp_resolve_item function to resolve it to an actual full path file if # necessary. # set(item "${raw_item}") # Add each item unless it is excluded: # set(add_item 1) if(item STREQUAL gp_install_id) set(add_item 0) endif() if(add_item AND ${exclude_system}) set(type "") gp_resolved_file_type("${target}" "${item}" "${exepath}" "${dirs}" type "${rpaths}") if(type STREQUAL "system") set(add_item 0) endif() endif() if(add_item) list(LENGTH ${prerequisites_var} list_length_before_append) gp_append_unique(${prerequisites_var} "${item}") list(LENGTH ${prerequisites_var} list_length_after_append) if(${recurse}) # If item was really added, this is the first time we have seen it. # Add it to unseen_prereqs so that we can recursively add *its* # prerequisites... # # But first: resolve its name to an absolute full path name such # that the analysis tools can simply accept it as input. # if(NOT list_length_before_append EQUAL list_length_after_append) gp_resolve_item("${target}" "${item}" "${exepath}" "${dirs}" resolved_item "${rpaths}") if(EXISTS "${resolved_item}") # Recurse only if we could resolve the item. # Otherwise the prerequisites_var list will be cleared set(unseen_prereqs ${unseen_prereqs} "${resolved_item}") endif() endif() endif() endif() else() if(verbose) message(STATUS "ignoring non-matching line: '${candidate}'") endif() endif() endforeach() list(LENGTH ${prerequisites_var} prerequisites_var_length) if(prerequisites_var_length GREATER 0) list(SORT ${prerequisites_var}) endif() if(${recurse}) set(more_inputs ${unseen_prereqs}) foreach(input ${more_inputs}) get_prerequisites("${input}" ${prerequisites_var} ${exclude_system} ${recurse} "${exepath}" "${dirs}" "${rpaths}") endforeach() endif() # Make result visible to caller set(${prerequisites_var} ${${prerequisites_var}} PARENT_SCOPE) # See if we added anything list(LENGTH ${prerequisites_var} list_length_after_candidates) if(list_length_after_candidates GREATER ${list_length_before_candidates}) # Something has been added to prerequisites. Note this in cache set(targets_added "${${prerequisites_var}}") if (list_length_before_candidates GREATER 0) foreach(i RANGE 1 ${list_length_before_candidates}) # from 1 to old list length, remove first item, i.e. remove all pre-existing items list(REMOVE_AT targets_added 0) # not the most elegant way of cutting the list start. Simplifications welcome endforeach() endif() endif() # Update our cache set_property(GLOBAL PROPERTY ${prerequisites_cache_var_name} "${targets_added}") get_property(cache_variables GLOBAL PROPERTY prerequisites_cachevariables) if (cache_variables) list(APPEND cache_variables ${prerequisites_cache_var_name}) list(LENGTH cache_variables len) else() set(cache_variables ${prerequisites_cache_var_name}) endif() set_property(GLOBAL PROPERTY prerequisites_cachevariables ${cache_variables}) message("Analyzed prerequisites of ${target}") endfunction() function(list_prerequisites target) if(ARGC GREATER 1 AND NOT "${ARGV1}" STREQUAL "") set(all "${ARGV1}") else() set(all 1) endif() if(ARGC GREATER 2 AND NOT "${ARGV2}" STREQUAL "") set(exclude_system "${ARGV2}") else() set(exclude_system 0) endif() if(ARGC GREATER 3 AND NOT "${ARGV3}" STREQUAL "") set(verbose "${ARGV3}") else() set(verbose 0) endif() set(count 0) set(count_str "") set(print_count "${verbose}") set(print_prerequisite_type "${verbose}") set(print_target "${verbose}") set(type_str "") get_filename_component(exepath "${target}" PATH) set(prereqs "") get_prerequisites("${target}" prereqs ${exclude_system} ${all} "${exepath}" "") if(print_target) message(STATUS "File '${target}' depends on:") endif() foreach(d ${prereqs}) math(EXPR count "${count} + 1") if(print_count) set(count_str "${count}. ") endif() if(print_prerequisite_type) gp_file_type("${target}" "${d}" type) set(type_str " (${type})") endif() message(STATUS "${count_str}${d}${type_str}") endforeach() endfunction() function(list_prerequisites_by_glob glob_arg glob_exp) message(STATUS "=============================================================================") message(STATUS "List prerequisites of executables matching ${glob_arg} '${glob_exp}'") message(STATUS "") file(${glob_arg} file_list ${glob_exp}) foreach(f ${file_list}) is_file_executable("${f}" is_f_executable) if(is_f_executable) message(STATUS "=============================================================================") list_prerequisites("${f}" ${ARGN}) message(STATUS "") endif() endforeach() endfunction() diff --git a/CMake/NSIS.template.in b/CMake/NSIS.template.in index 619bfcd3f8..887a86485c 100644 --- a/CMake/NSIS.template.in +++ b/CMake/NSIS.template.in @@ -1,1014 +1,1014 @@ ; CPack install script designed for a nmake build ;-------------------------------- ; You must define these values !define VERSION "@CPACK_PACKAGE_VERSION@" !define PATCH "@CPACK_PACKAGE_VERSION_PATCH@" !define INST_DIR "@CPACK_TEMPORARY_DIRECTORY@" ;-------------------------------- ;Variables Var MUI_TEMP Var STARTMENU_FOLDER Var SV_ALLUSERS Var START_MENU Var DO_NOT_ADD_TO_PATH Var ADD_TO_PATH_ALL_USERS Var ADD_TO_PATH_CURRENT_USER Var INSTALL_DESKTOP Var IS_DEFAULT_INSTALLDIR ;-------------------------------- ;Include Modern UI !include "MUI.nsh" ;Default installation folder InstallDir "@CPACK_NSIS_INSTALL_ROOT@\@CPACK_PACKAGE_INSTALL_DIRECTORY@" ;Allow for very long title on the welcome page !define MUI_WELCOMEPAGE_TITLE_3LINES ;-------------------------------- ;General ;Name and file Name "@CPACK_NSIS_PACKAGE_NAME@" OutFile "@CPACK_TOPLEVEL_DIRECTORY@/@CPACK_OUTPUT_FILE_NAME@" ;Set compression SetCompressor @CPACK_NSIS_COMPRESSOR@ RequestExecutionLevel user @CPACK_NSIS_DEFINES@ !include Sections.nsh ;--- Component support macros: --- ; The code for the add/remove functionality is from: ; https://nsis.sourceforge.io/Add/Remove_Functionality ; It has been modified slightly and extended to provide ; inter-component dependencies. Var AR_SecFlags Var AR_RegFlags @CPACK_NSIS_SECTION_SELECTED_VARS@ ; Loads the "selected" flag for the section named SecName into the ; variable VarName. !macro LoadSectionSelectedIntoVar SecName VarName SectionGetFlags ${${SecName}} $${VarName} IntOp $${VarName} $${VarName} & ${SF_SELECTED} ;Turn off all other bits !macroend ; Loads the value of a variable... can we get around this? !macro LoadVar VarName IntOp $R0 0 + $${VarName} !macroend ; Sets the value of a variable !macro StoreVar VarName IntValue IntOp $${VarName} 0 + ${IntValue} !macroend !macro InitSection SecName ; This macro reads component installed flag from the registry and ;changes checked state of the section on the components page. ;Input: section index constant name specified in Section command. ClearErrors ;Reading component status from registry ReadRegDWORD $AR_RegFlags HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_NAME@\Components\${SecName}" "Installed" IfErrors "default_${SecName}" ;Status will stay default if registry value not found ;(component was never installed) IntOp $AR_RegFlags $AR_RegFlags & ${SF_SELECTED} ;Turn off all other bits SectionGetFlags ${${SecName}} $AR_SecFlags ;Reading default section flags IntOp $AR_SecFlags $AR_SecFlags & 0xFFFE ;Turn lowest (enabled) bit off IntOp $AR_SecFlags $AR_RegFlags | $AR_SecFlags ;Change lowest bit ; Note whether this component was installed before !insertmacro StoreVar ${SecName}_was_installed $AR_RegFlags IntOp $R0 $AR_RegFlags & $AR_RegFlags ;Writing modified flags SectionSetFlags ${${SecName}} $AR_SecFlags "default_${SecName}:" !insertmacro LoadSectionSelectedIntoVar ${SecName} ${SecName}_selected !macroend !macro FinishSection SecName ; This macro reads section flag set by user and removes the section ;if it is not selected. ;Then it writes component installed flag to registry ;Input: section index constant name specified in Section command. SectionGetFlags ${${SecName}} $AR_SecFlags ;Reading section flags ;Checking lowest bit: IntOp $AR_SecFlags $AR_SecFlags & ${SF_SELECTED} IntCmp $AR_SecFlags 1 "leave_${SecName}" ;Section is not selected: ;Calling Section uninstall macro and writing zero installed flag !insertmacro "Remove_${${SecName}}" WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_NAME@\Components\${SecName}" \ "Installed" 0 Goto "exit_${SecName}" "leave_${SecName}:" ;Section is selected: WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_NAME@\Components\${SecName}" \ "Installed" 1 "exit_${SecName}:" !macroend ; NSIS example code to only include some code if a file exists at compile time (whereas IfFileExists works at runtime) ; See https://nsis.sourceforge.io/Check_if_a_file_exists_at_compile_time for documentation !macro !defineifexist _VAR_NAME _FILE_NAME !tempfile _TEMPFILE !system 'if exist "${_FILE_NAME}" echo !define ${_VAR_NAME} > "${_TEMPFILE}"' !include '${_TEMPFILE}' !delfile '${_TEMPFILE}' !undef _TEMPFILE !macroend !define !defineifexist "!insertmacro !defineifexist" ; Determine whether the selection of SecName changed !macro MaybeSelectionChanged SecName !insertmacro LoadVar ${SecName}_selected SectionGetFlags ${${SecName}} $R1 IntOp $R1 $R1 & ${SF_SELECTED} ;Turn off all other bits ; See if the status has changed: IntCmp $R0 $R1 "${SecName}_unchanged" !insertmacro LoadSectionSelectedIntoVar ${SecName} ${SecName}_selected IntCmp $R1 ${SF_SELECTED} "${SecName}_was_selected" !insertmacro "Deselect_required_by_${SecName}" goto "${SecName}_unchanged" "${SecName}_was_selected:" !insertmacro "Select_${SecName}_depends" "${SecName}_unchanged:" !macroend ;--- End of Add/Remove macros --- ;-------------------------------- ;Interface Settings !define MUI_HEADERIMAGE !define MUI_ABORTWARNING ;-------------------------------- ; path functions !verbose 3 !include "WinMessages.NSH" !verbose 4 ;---------------------------------------- ; based upon a script of "Written by KiCHiK 2003-01-18 05:57:02" ;---------------------------------------- !verbose 3 !include "WinMessages.NSH" !verbose 4 ;==================================================== ; get_NT_environment ; Returns: the selected environment ; Output : head of the stack ;==================================================== !macro select_NT_profile UN Function ${UN}select_NT_profile StrCmp $ADD_TO_PATH_ALL_USERS "1" 0 environment_single DetailPrint "Selected environment for all users" Push "all" Return environment_single: DetailPrint "Selected environment for current user only." Push "current" Return FunctionEnd !macroend !insertmacro select_NT_profile "" !insertmacro select_NT_profile "un." ;---------------------------------------------------- !define NT_current_env 'HKCU "Environment"' !define NT_all_env 'HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"' !ifndef WriteEnvStr_RegKey !ifdef ALL_USERS !define WriteEnvStr_RegKey \ 'HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"' !else !define WriteEnvStr_RegKey 'HKCU "Environment"' !endif !endif ; AddToPath - Adds the given dir to the search path. ; Input - head of the stack ; Note - Win9x systems requires reboot Function AddToPath Exch $0 Push $1 Push $2 Push $3 # don't add if the path doesn't exist IfFileExists "$0\*.*" "" AddToPath_done ReadEnvStr $1 PATH ; if the path is too long for a NSIS variable NSIS will return a 0 ; length string. If we find that, then warn and skip any path ; modification as it will trash the existing path. StrLen $2 $1 IntCmp $2 0 CheckPathLength_ShowPathWarning CheckPathLength_Done CheckPathLength_Done CheckPathLength_ShowPathWarning: DetailPrint "Warning: Could not modify PATH variable - current PATH is too long.\n\ This does not impact the functionality of MITK itself, but you will not be able to start\ it from anywhere via command line." ; The message box is probably too much of an escalation, most users won't care or notice ;Messagebox MB_OK|MB_ICONEXCLAMATION "Warning! PATH too long installer unable to modify PATH!" Goto AddToPath_done CheckPathLength_Done: Push "$1;" Push "$0;" Call StrStr Pop $2 StrCmp $2 "" "" AddToPath_done Push "$1;" Push "$0\;" Call StrStr Pop $2 StrCmp $2 "" "" AddToPath_done GetFullPathName /SHORT $3 $0 Push "$1;" Push "$3;" Call StrStr Pop $2 StrCmp $2 "" "" AddToPath_done Push "$1;" Push "$3\;" Call StrStr Pop $2 StrCmp $2 "" "" AddToPath_done Call IsNT Pop $1 StrCmp $1 1 AddToPath_NT ; Not on NT StrCpy $1 $WINDIR 2 FileOpen $1 "$1\autoexec.bat" a FileSeek $1 -1 END FileReadByte $1 $2 IntCmp $2 26 0 +2 +2 # DOS EOF FileSeek $1 -1 END # write over EOF FileWrite $1 "$\r$\nSET PATH=%PATH%;$3$\r$\n" FileClose $1 SetRebootFlag true Goto AddToPath_done AddToPath_NT: StrCmp $ADD_TO_PATH_ALL_USERS "1" ReadAllKey ReadRegStr $1 ${NT_current_env} "PATH" Goto DoTrim ReadAllKey: ReadRegStr $1 ${NT_all_env} "PATH" DoTrim: StrCmp $1 "" AddToPath_NTdoIt Push $1 Call Trim Pop $1 StrCpy $0 "$1;$0" AddToPath_NTdoIt: StrCmp $ADD_TO_PATH_ALL_USERS "1" WriteAllKey WriteRegExpandStr ${NT_current_env} "PATH" $0 Goto DoSend WriteAllKey: WriteRegExpandStr ${NT_all_env} "PATH" $0 DoSend: SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000 AddToPath_done: Pop $3 Pop $2 Pop $1 Pop $0 FunctionEnd ; RemoveFromPath - Remove a given dir from the path ; Input: head of the stack Function un.RemoveFromPath Exch $0 Push $1 Push $2 Push $3 Push $4 Push $5 Push $6 IntFmt $6 "%c" 26 # DOS EOF Call un.IsNT Pop $1 StrCmp $1 1 unRemoveFromPath_NT ; Not on NT StrCpy $1 $WINDIR 2 FileOpen $1 "$1\autoexec.bat" r GetTempFileName $4 FileOpen $2 $4 w GetFullPathName /SHORT $0 $0 StrCpy $0 "SET PATH=%PATH%;$0" Goto unRemoveFromPath_dosLoop unRemoveFromPath_dosLoop: FileRead $1 $3 StrCpy $5 $3 1 -1 # read last char StrCmp $5 $6 0 +2 # if DOS EOF StrCpy $3 $3 -1 # remove DOS EOF so we can compare StrCmp $3 "$0$\r$\n" unRemoveFromPath_dosLoopRemoveLine StrCmp $3 "$0$\n" unRemoveFromPath_dosLoopRemoveLine StrCmp $3 "$0" unRemoveFromPath_dosLoopRemoveLine StrCmp $3 "" unRemoveFromPath_dosLoopEnd FileWrite $2 $3 Goto unRemoveFromPath_dosLoop unRemoveFromPath_dosLoopRemoveLine: SetRebootFlag true Goto unRemoveFromPath_dosLoop unRemoveFromPath_dosLoopEnd: FileClose $2 FileClose $1 StrCpy $1 $WINDIR 2 Delete "$1\autoexec.bat" CopyFiles /SILENT $4 "$1\autoexec.bat" Delete $4 Goto unRemoveFromPath_done unRemoveFromPath_NT: StrCmp $ADD_TO_PATH_ALL_USERS "1" unReadAllKey ReadRegStr $1 ${NT_current_env} "PATH" Goto unDoTrim unReadAllKey: ReadRegStr $1 ${NT_all_env} "PATH" unDoTrim: StrCpy $5 $1 1 -1 # copy last char StrCmp $5 ";" +2 # if last char != ; StrCpy $1 "$1;" # append ; Push $1 Push "$0;" Call un.StrStr ; Find `$0;` in $1 Pop $2 ; pos of our dir StrCmp $2 "" unRemoveFromPath_done ; else, it is in path # $0 - path to add # $1 - path var StrLen $3 "$0;" StrLen $4 $2 StrCpy $5 $1 -$4 # $5 is now the part before the path to remove StrCpy $6 $2 "" $3 # $6 is now the part after the path to remove StrCpy $3 $5$6 StrCpy $5 $3 1 -1 # copy last char StrCmp $5 ";" 0 +2 # if last char == ; StrCpy $3 $3 -1 # remove last char StrCmp $ADD_TO_PATH_ALL_USERS "1" unWriteAllKey WriteRegExpandStr ${NT_current_env} "PATH" $3 Goto unDoSend unWriteAllKey: WriteRegExpandStr ${NT_all_env} "PATH" $3 unDoSend: SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000 unRemoveFromPath_done: Pop $6 Pop $5 Pop $4 Pop $3 Pop $2 Pop $1 Pop $0 FunctionEnd ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Uninstall stuff ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ########################################### # Utility Functions # ########################################### ;==================================================== ; IsNT - Returns 1 if the current system is NT, 0 ; otherwise. ; Output: head of the stack ;==================================================== ; IsNT ; no input ; output, top of the stack = 1 if NT or 0 if not ; ; Usage: ; Call IsNT ; Pop $R0 ; ($R0 at this point is 1 or 0) !macro IsNT un Function ${un}IsNT Push $0 ReadRegStr $0 HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion" CurrentVersion StrCmp $0 "" 0 IsNT_yes ; we are not NT. Pop $0 Push 0 Return IsNT_yes: ; NT!!! Pop $0 Push 1 FunctionEnd !macroend !insertmacro IsNT "" !insertmacro IsNT "un." ; StrStr ; input, top of stack = string to search for ; top of stack-1 = string to search in ; output, top of stack (replaces with the portion of the string remaining) ; modifies no other variables. ; ; Usage: ; Push "this is a long ass string" ; Push "ass" ; Call StrStr ; Pop $R0 ; ($R0 at this point is "ass string") !macro StrStr un Function ${un}StrStr Exch $R1 ; st=haystack,old$R1, $R1=needle Exch ; st=old$R1,haystack Exch $R2 ; st=old$R1,old$R2, $R2=haystack Push $R3 Push $R4 Push $R5 StrLen $R3 $R1 StrCpy $R4 0 ; $R1=needle ; $R2=haystack ; $R3=len(needle) ; $R4=cnt ; $R5=tmp loop: StrCpy $R5 $R2 $R3 $R4 StrCmp $R5 $R1 done StrCmp $R5 "" done IntOp $R4 $R4 + 1 Goto loop done: StrCpy $R1 $R2 "" $R4 Pop $R5 Pop $R4 Pop $R3 Pop $R2 Exch $R1 FunctionEnd !macroend !insertmacro StrStr "" !insertmacro StrStr "un." Function Trim ; Added by Pelaca Exch $R1 Push $R2 Loop: StrCpy $R2 "$R1" 1 -1 StrCmp "$R2" " " RTrim StrCmp "$R2" "$\n" RTrim StrCmp "$R2" "$\r" RTrim StrCmp "$R2" ";" RTrim GoTo Done RTrim: StrCpy $R1 "$R1" -1 Goto Loop Done: Pop $R2 Exch $R1 FunctionEnd Function ConditionalAddToRegisty Pop $0 Pop $1 StrCmp "$0" "" ConditionalAddToRegisty_EmptyString WriteRegStr SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_NAME@" \ "$1" "$0" ;MessageBox MB_OK "Set Registry: '$1' to '$0'" DetailPrint "Set install registry entry: '$1' to '$0'" ConditionalAddToRegisty_EmptyString: FunctionEnd ;-------------------------------- !ifdef CPACK_USES_DOWNLOAD Function DownloadFile IfFileExists $INSTDIR\* +2 CreateDirectory $INSTDIR Pop $0 ; Skip if already downloaded IfFileExists $INSTDIR\$0 0 +2 Return StrCpy $1 "@CPACK_DOWNLOAD_SITE@" try_again: NSISdl::download "$1/$0" "$INSTDIR\$0" Pop $1 StrCmp $1 "success" success StrCmp $1 "Cancelled" cancel MessageBox MB_OK "Download failed: $1" cancel: Return success: FunctionEnd !endif ;-------------------------------- ; Installation types @CPACK_NSIS_INSTALLATION_TYPES@ ;-------------------------------- ; Component sections @CPACK_NSIS_COMPONENT_SECTIONS@ ;-------------------------------- ; Define some macro setting for the gui @CPACK_NSIS_INSTALLER_MUI_ICON_CODE@ @CPACK_NSIS_INSTALLER_ICON_CODE@ @CPACK_NSIS_INSTALLER_MUI_COMPONENTS_DESC@ @CPACK_NSIS_INSTALLER_MUI_FINISHPAGE_RUN_CODE@ ;-------------------------------- ;Pages !insertmacro MUI_PAGE_WELCOME !insertmacro MUI_PAGE_LICENSE "@CPACK_RESOURCE_FILE_LICENSE@" Page custom InstallOptionsPage !insertmacro MUI_PAGE_DIRECTORY ;Start Menu Folder Page Configuration !define MUI_STARTMENUPAGE_REGISTRY_ROOT "SHCTX" !define MUI_STARTMENUPAGE_REGISTRY_KEY "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" !define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "Start Menu Folder" !insertmacro MUI_PAGE_STARTMENU Application $STARTMENU_FOLDER @CPACK_NSIS_PAGE_COMPONENTS@ !insertmacro MUI_PAGE_INSTFILES !insertmacro MUI_PAGE_FINISH !insertmacro MUI_UNPAGE_CONFIRM !insertmacro MUI_UNPAGE_INSTFILES ;-------------------------------- ;Languages !insertmacro MUI_LANGUAGE "English" ;first language is the default language !insertmacro MUI_LANGUAGE "Albanian" !insertmacro MUI_LANGUAGE "Arabic" !insertmacro MUI_LANGUAGE "Basque" !insertmacro MUI_LANGUAGE "Belarusian" !insertmacro MUI_LANGUAGE "Bosnian" !insertmacro MUI_LANGUAGE "Breton" !insertmacro MUI_LANGUAGE "Bulgarian" !insertmacro MUI_LANGUAGE "Croatian" !insertmacro MUI_LANGUAGE "Czech" !insertmacro MUI_LANGUAGE "Danish" !insertmacro MUI_LANGUAGE "Dutch" !insertmacro MUI_LANGUAGE "Estonian" !insertmacro MUI_LANGUAGE "Farsi" !insertmacro MUI_LANGUAGE "Finnish" !insertmacro MUI_LANGUAGE "French" !insertmacro MUI_LANGUAGE "German" !insertmacro MUI_LANGUAGE "Greek" !insertmacro MUI_LANGUAGE "Hebrew" !insertmacro MUI_LANGUAGE "Hungarian" !insertmacro MUI_LANGUAGE "Icelandic" !insertmacro MUI_LANGUAGE "Indonesian" !insertmacro MUI_LANGUAGE "Irish" !insertmacro MUI_LANGUAGE "Italian" !insertmacro MUI_LANGUAGE "Japanese" !insertmacro MUI_LANGUAGE "Korean" !insertmacro MUI_LANGUAGE "Kurdish" !insertmacro MUI_LANGUAGE "Latvian" !insertmacro MUI_LANGUAGE "Lithuanian" !insertmacro MUI_LANGUAGE "Luxembourgish" !insertmacro MUI_LANGUAGE "Macedonian" !insertmacro MUI_LANGUAGE "Malay" !insertmacro MUI_LANGUAGE "Mongolian" !insertmacro MUI_LANGUAGE "Norwegian" !insertmacro MUI_LANGUAGE "Polish" !insertmacro MUI_LANGUAGE "Portuguese" !insertmacro MUI_LANGUAGE "PortugueseBR" !insertmacro MUI_LANGUAGE "Romanian" !insertmacro MUI_LANGUAGE "Russian" !insertmacro MUI_LANGUAGE "Serbian" !insertmacro MUI_LANGUAGE "SerbianLatin" !insertmacro MUI_LANGUAGE "SimpChinese" !insertmacro MUI_LANGUAGE "Slovak" !insertmacro MUI_LANGUAGE "Slovenian" !insertmacro MUI_LANGUAGE "Spanish" !insertmacro MUI_LANGUAGE "Swedish" !insertmacro MUI_LANGUAGE "Thai" !insertmacro MUI_LANGUAGE "TradChinese" !insertmacro MUI_LANGUAGE "Turkish" !insertmacro MUI_LANGUAGE "Ukrainian" !insertmacro MUI_LANGUAGE "Welsh" ;-------------------------------- ;Reserve Files ;These files should be inserted before other files in the data block ;Keep these lines before any File command ;Only for solid compression (by default, solid compression is enabled for BZIP2 and LZMA) ReserveFile "NSIS.InstallOptions.ini" !insertmacro MUI_RESERVEFILE_INSTALLOPTIONS ;-------------------------------- ;Installer Sections Section "-Core installation" ;Use the entire tree produced by the INSTALL target. Keep the ;list of directories here in sync with the RMDir commands below. SetOutPath "$INSTDIR" @CPACK_NSIS_FULL_INSTALL@ ;Store installation folder WriteRegStr SHCTX "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "" $INSTDIR ;Create uninstaller WriteUninstaller "$INSTDIR\Uninstall.exe" Push "DisplayName" Push "@CPACK_NSIS_DISPLAY_NAME@" Call ConditionalAddToRegisty Push "DisplayVersion" Push "@CPACK_PACKAGE_VERSION@" Call ConditionalAddToRegisty Push "Publisher" Push "@CPACK_PACKAGE_VENDOR@" Call ConditionalAddToRegisty Push "UninstallString" Push "$INSTDIR\Uninstall.exe" Call ConditionalAddToRegisty Push "NoRepair" Push "1" Call ConditionalAddToRegisty !ifdef CPACK_NSIS_ADD_REMOVE ;Create add/remove functionality Push "ModifyPath" Push "$INSTDIR\AddRemove.exe" Call ConditionalAddToRegisty !else Push "NoModify" Push "1" Call ConditionalAddToRegisty !endif ; Optional registration Push "DisplayIcon" Push "$INSTDIR\@CPACK_NSIS_INSTALLED_ICON_NAME@" Call ConditionalAddToRegisty Push "HelpLink" Push "@CPACK_NSIS_HELP_LINK@" Call ConditionalAddToRegisty Push "URLInfoAbout" Push "@CPACK_NSIS_URL_INFO_ABOUT@" Call ConditionalAddToRegisty Push "Contact" Push "@CPACK_NSIS_CONTACT@" Call ConditionalAddToRegisty !insertmacro MUI_INSTALLOPTIONS_READ $INSTALL_DESKTOP "NSIS.InstallOptions.ini" "Field 5" "State" !insertmacro MUI_STARTMENU_WRITE_BEGIN Application ;Create shortcuts CreateDirectory "$SMPROGRAMS\$STARTMENU_FOLDER" @CPACK_NSIS_CREATE_ICONS@ @CPACK_NSIS_CREATE_ICONS_EXTRA@ CreateShortCut "$SMPROGRAMS\$STARTMENU_FOLDER\Uninstall.lnk" "$INSTDIR\Uninstall.exe" ;Read a value from an InstallOptions INI file !insertmacro MUI_INSTALLOPTIONS_READ $DO_NOT_ADD_TO_PATH "NSIS.InstallOptions.ini" "Field 2" "State" !insertmacro MUI_INSTALLOPTIONS_READ $ADD_TO_PATH_ALL_USERS "NSIS.InstallOptions.ini" "Field 3" "State" !insertmacro MUI_INSTALLOPTIONS_READ $ADD_TO_PATH_CURRENT_USER "NSIS.InstallOptions.ini" "Field 4" "State" ; Write special uninstall registry entries Push "StartMenu" Push "$STARTMENU_FOLDER" Call ConditionalAddToRegisty Push "DoNotAddToPath" Push "$DO_NOT_ADD_TO_PATH" Call ConditionalAddToRegisty Push "AddToPathAllUsers" Push "$ADD_TO_PATH_ALL_USERS" Call ConditionalAddToRegisty Push "AddToPathCurrentUser" Push "$ADD_TO_PATH_CURRENT_USER" Call ConditionalAddToRegisty Push "InstallToDesktop" Push "$INSTALL_DESKTOP" Call ConditionalAddToRegisty !insertmacro MUI_STARTMENU_WRITE_END @CPACK_NSIS_EXTRA_INSTALL_COMMANDS@ SectionEnd Section "-Add to path" Push $INSTDIR\bin StrCmp "@CPACK_NSIS_MODIFY_PATH@" "ON" 0 doNotAddToPath StrCmp $DO_NOT_ADD_TO_PATH "1" doNotAddToPath 0 Call AddToPath doNotAddToPath: SectionEnd ;-------------------------------- ; Create custom pages Function InstallOptionsPage !insertmacro MUI_HEADER_TEXT "Install Options" "Choose options for installing @CPACK_NSIS_PACKAGE_NAME@" !insertmacro MUI_INSTALLOPTIONS_DISPLAY "NSIS.InstallOptions.ini" FunctionEnd ; Author: Lilla (lilla@earthlink.net) 2003-06-13 ; function IsUserAdmin uses plugin \NSIS\PlusgIns\UserInfo.dll ; This function is based upon code in \NSIS\Contrib\UserInfo\UserInfo.nsi ; This function was tested under NSIS 2 beta 4 (latest CVS as of this writing). ; ; Usage: ; Call IsUserAdmin ; Pop $R0 ; at this point $R0 is "true" or "false" ; Function IsUserAdmin Push $R0 Push $R1 Push $R2 ClearErrors UserInfo::GetName IfErrors Win9x Pop $R1 UserInfo::GetAccountType Pop $R2 StrCmp $R2 "Admin" 0 Continue ; Observation: I get here when running Win98SE. (Lilla) ; The functions UserInfo.dll looks for are there on Win98 too, ; but just don't work. So UserInfo.dll, knowing that admin isn't required ; on Win98, returns admin anyway. (per kichik) ; MessageBox MB_OK 'User "$R1" is in the Administrators group' StrCpy $R0 "true" Goto Done Continue: ; You should still check for an empty string because the functions ; UserInfo.dll looks for may not be present on Windows 95. (per kichik) StrCmp $R2 "" Win9x StrCpy $R0 "false" ;MessageBox MB_OK 'User "$R1" is in the "$R2" group' Goto Done Win9x: ; comment/message below is by UserInfo.nsi author: ; This one means you don't need to care about admin or ; not admin because Windows 9x doesn't either ;MessageBox MB_OK "Error! This DLL can't run under Windows 9x!" StrCpy $R0 "true" Done: ;MessageBox MB_OK 'User= "$R1" AccountType= "$R2" IsUserAdmin= "$R0"' Pop $R2 Pop $R1 Exch $R0 FunctionEnd ;-------------------------------- ; determine admin versus local install Function un.onInit ClearErrors UserInfo::GetName IfErrors noLM Pop $0 UserInfo::GetAccountType Pop $1 StrCmp $1 "Admin" 0 +3 SetShellVarContext all ;MessageBox MB_OK 'User "$0" is in the Admin group' Goto done StrCmp $1 "Power" 0 +3 SetShellVarContext all ;MessageBox MB_OK 'User "$0" is in the Power Users group' Goto done noLM: ;Get installation folder from registry if available done: FunctionEnd ;--- Add/Remove callback functions: --- !macro SectionList MacroName ;This macro used to perform operation on multiple sections. ;List all of your components in following manner here. @CPACK_NSIS_COMPONENT_SECTION_LIST@ !macroend Section -FinishComponents ;Removes unselected components and writes component status to registry !insertmacro SectionList "FinishSection" !ifdef CPACK_NSIS_ADD_REMOVE ; Get the name of the installer executable System::Call 'kernel32::GetModuleFileNameA(i 0, t .R0, i 1024) i r1' StrCpy $R3 $R0 ; Strip off the last 13 characters, to see if we have AddRemove.exe StrLen $R1 $R0 IntOp $R1 $R0 - 13 StrCpy $R2 $R0 13 $R1 StrCmp $R2 "AddRemove.exe" addremove_installed ; We're not running AddRemove.exe, so install it CopyFiles $R3 $INSTDIR\AddRemove.exe addremove_installed: !endif SectionEnd ;--- End of Add/Remove callback functions --- ;-------------------------------- ; Component dependencies Function .onSelChange !insertmacro SectionList MaybeSelectionChanged FunctionEnd ;-------------------------------- ;Uninstaller Section Section "Uninstall" ReadRegStr $START_MENU SHCTX \ "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_NAME@" "StartMenu" ;MessageBox MB_OK "Start menu is in: $START_MENU" ReadRegStr $DO_NOT_ADD_TO_PATH SHCTX \ "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_NAME@" "DoNotAddToPath" ReadRegStr $ADD_TO_PATH_ALL_USERS SHCTX \ "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_NAME@" "AddToPathAllUsers" ReadRegStr $ADD_TO_PATH_CURRENT_USER SHCTX \ "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_NAME@" "AddToPathCurrentUser" ;MessageBox MB_OK "Add to path: $DO_NOT_ADD_TO_PATH all users: $ADD_TO_PATH_ALL_USERS" ReadRegStr $INSTALL_DESKTOP SHCTX \ "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_NAME@" "InstallToDesktop" ;MessageBox MB_OK "Install to desktop: $INSTALL_DESKTOP " @CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS@ ;Remove files we installed. ;Keep the list of directories here in sync with the File commands above. @CPACK_NSIS_DELETE_FILES@ @CPACK_NSIS_DELETE_DIRECTORIES@ !ifdef CPACK_NSIS_ADD_REMOVE ;Remove the add/remove program Delete "$INSTDIR\AddRemove.exe" !endif ;Remove the uninstaller itself. Delete "$INSTDIR\Uninstall.exe" DeleteRegKey SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_NAME@" ;Remove the installation directory if it is empty. RMDir "$INSTDIR" ; Remove the registry entries. DeleteRegKey SHCTX "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" ; Removes all optional components !insertmacro SectionList "RemoveSection" !insertmacro MUI_STARTMENU_GETFOLDER Application $MUI_TEMP Delete "$SMPROGRAMS\$MUI_TEMP\Uninstall.lnk" @CPACK_NSIS_DELETE_ICONS@ @CPACK_NSIS_DELETE_ICONS_EXTRA@ - ;Delete empty start menu parent diretories + ;Delete empty start menu parent directories StrCpy $MUI_TEMP "$SMPROGRAMS\$MUI_TEMP" startMenuDeleteLoop: ClearErrors RMDir $MUI_TEMP GetFullPathName $MUI_TEMP "$MUI_TEMP\.." IfErrors startMenuDeleteLoopDone StrCmp "$MUI_TEMP" "$SMPROGRAMS" startMenuDeleteLoopDone startMenuDeleteLoop startMenuDeleteLoopDone: ; If the user changed the shortcut, then untinstall may not work. This should ; try to fix it. StrCpy $MUI_TEMP "$START_MENU" Delete "$SMPROGRAMS\$MUI_TEMP\Uninstall.lnk" @CPACK_NSIS_DELETE_ICONS_EXTRA@ - ;Delete empty start menu parent diretories + ;Delete empty start menu parent directories StrCpy $MUI_TEMP "$SMPROGRAMS\$MUI_TEMP" secondStartMenuDeleteLoop: ClearErrors RMDir $MUI_TEMP GetFullPathName $MUI_TEMP "$MUI_TEMP\.." IfErrors secondStartMenuDeleteLoopDone StrCmp "$MUI_TEMP" "$SMPROGRAMS" secondStartMenuDeleteLoopDone secondStartMenuDeleteLoop secondStartMenuDeleteLoopDone: DeleteRegKey /ifempty SHCTX "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" Push $INSTDIR\bin StrCmp $DO_NOT_ADD_TO_PATH_ "1" doNotRemoveFromPath 0 Call un.RemoveFromPath doNotRemoveFromPath: SectionEnd ;-------------------------------- ; determine admin versus local install ; Is install for "AllUsers" or "JustMe"? ; Default to "JustMe" - set to "AllUsers" if admin or on Win9x ; This function is used for the very first "custom page" of the installer. ; This custom page does not show up visibly, but it executes prior to the ; first visible page and sets up $INSTDIR properly... ; Choose different default installation folder based on SV_ALLUSERS... ; "Program Files" for AllUsers, "My Documents" for JustMe... Function .onInit ; Reads components status for registry !insertmacro SectionList "InitSection" ; check to see if /D has been used to change ; the install directory by comparing it to the ; install directory that is expected to be the ; default StrCpy $IS_DEFAULT_INSTALLDIR 0 StrCmp "$INSTDIR" "@CPACK_NSIS_INSTALL_ROOT@\@CPACK_PACKAGE_INSTALL_DIRECTORY@" 0 +2 StrCpy $IS_DEFAULT_INSTALLDIR 1 StrCpy $SV_ALLUSERS "JustMe" ; if default install dir then change the default ; if it is installed for JustMe StrCmp "$IS_DEFAULT_INSTALLDIR" "1" 0 +2 StrCpy $INSTDIR "$DOCUMENTS\@CPACK_PACKAGE_INSTALL_DIRECTORY@" ClearErrors UserInfo::GetName IfErrors noLM Pop $0 UserInfo::GetAccountType Pop $1 StrCmp $1 "Admin" 0 +3 SetShellVarContext all ;MessageBox MB_OK 'User "$0" is in the Admin group' StrCpy $SV_ALLUSERS "AllUsers" Goto done StrCmp $1 "Power" 0 +3 SetShellVarContext all ;MessageBox MB_OK 'User "$0" is in the Power Users group' StrCpy $SV_ALLUSERS "AllUsers" Goto done noLM: StrCpy $SV_ALLUSERS "AllUsers" ;Get installation folder from registry if available done: StrCmp $SV_ALLUSERS "AllUsers" 0 +3 StrCmp "$IS_DEFAULT_INSTALLDIR" "1" 0 +2 StrCpy $INSTDIR "@CPACK_NSIS_INSTALL_ROOT@\@CPACK_PACKAGE_INSTALL_DIRECTORY@" StrCmp "@CPACK_NSIS_MODIFY_PATH@" "ON" 0 noOptionsPage !insertmacro MUI_INSTALLOPTIONS_EXTRACT "NSIS.InstallOptions.ini" noOptionsPage: FunctionEnd diff --git a/CMake/mitkFunctionCheckCompilerFlags.cmake b/CMake/mitkFunctionCheckCompilerFlags.cmake index e1cade7ec0..65b616845b 100644 --- a/CMake/mitkFunctionCheckCompilerFlags.cmake +++ b/CMake/mitkFunctionCheckCompilerFlags.cmake @@ -1,113 +1,113 @@ # # Helper macro allowing to check if the given flags are supported # by the underlying build tool # # If the flag(s) is/are supported, they will be appended to the string identified by RESULT_VAR # # Usage: # mitkFunctionCheckCompilerFlags(FLAGS_TO_CHECK VALID_FLAGS_VAR) # # The above example uses the C++ compiler to check the flags. To individually check with # the C and C++ compiler, use: # # mitkFunctionCheckCompilerFlags2(FLAGS_TO_CHECK VALID_C_FLAGS_VAR VALID_CXX_FLAGS_VAR) # # Example: # # set(myflags) # mitkFunctionCheckCompilerFlags("-fprofile-arcs" myflags) # message(1-myflags:${myflags}) # mitkFunctionCheckCompilerFlags("-fauto-bugfix" myflags) # message(2-myflags:${myflags}) # mitkFunctionCheckCompilerFlags("-Wall" myflags) # message(1-myflags:${myflags}) # # The output will be: # 1-myflags: -fprofile-arcs # 2-myflags: -fprofile-arcs # 3-myflags: -fprofile-arcs -Wall include(CheckCCompilerFlag) include(CheckCXXCompilerFlag) function(mitkFunctionCheckCompilerFlags CXX_FLAG_TO_TEST RESULT_VAR) if(CXX_FLAG_TO_TEST STREQUAL "") message(FATAL_ERROR "CXX_FLAG_TO_TEST shouldn't be empty") endif() # Internally, the macro CMAKE_CXX_ACCEPTS_FLAG calls TRY_COMPILE. To avoid # the cost of compiling the test each time the project is configured, the variable set by # the macro is added to the cache so that following invocation of the macro with # the same variable name skip the compilation step. # For that same reason, the mitkFunctionCheckCompilerFlags function appends a unique suffix to # the HAS_FLAG variable. This suffix is created using a 'clean version' of the flag to test. string(REGEX REPLACE "[, \\$\\+\\*\\{\\}\\(\\)\\#]" "" suffix ${CXX_FLAG_TO_TEST}) CHECK_CXX_COMPILER_FLAG(${CXX_FLAG_TO_TEST} HAS_FLAG_${suffix}) if(HAS_FLAG_${suffix}) set(${RESULT_VAR} "${${RESULT_VAR}} ${CXX_FLAG_TO_TEST}") string(STRIP ${${RESULT_VAR}} ${RESULT_VAR}) set(${RESULT_VAR} "${${RESULT_VAR}}" PARENT_SCOPE) endif() endfunction() function(mitkFunctionCheckCAndCXXCompilerFlags FLAG_TO_TEST C_RESULT_VAR CXX_RESULT_VAR) if(FLAG_TO_TEST STREQUAL "") message(FATAL_ERROR "FLAG_TO_TEST shouldn't be empty") endif() # Save the contents of CXX_RESULT_VAR temporarily. # This is needed of ${CXX_RESULT_VAR} is one of the CMAKE__FLAGS_* variables. set(_saved_c_result_var ${${C_RESULT_VAR}}) set(_saved_cxx_result_var ${${CXX_RESULT_VAR}}) # Clear all flags. If not, existing flags triggering warnings might lead to # false-negatives when checking for certain compiler flags. set(CMAKE_C_FLAGS ) set(CMAKE_C_FLAGS_DEBUG ) set(CMAKE_C_FLAGS_MINSIZEREL ) set(CMAKE_C_FLAGS_RELEASE ) set(CMAKE_C_FLAGS_RELWITHDEBINFO ) set(CMAKE_CXX_FLAGS ) set(CMAKE_CXX_FLAGS_DEBUG ) set(CMAKE_CXX_FLAGS_MINSIZEREL ) set(CMAKE_CXX_FLAGS_RELEASE ) set(CMAKE_CXX_FLAGS_RELWITHDEBINFO ) # Internally, the macro CMAKE_CXX_COMPILER_FLAG calls TRY_COMPILE. To avoid # the cost of compiling the test each time the project is configured, the variable set by # the macro is added to the cache so that following invocation of the macro with # the same variable name skip the compilation step. # For that same reason, the mitkFunctionCheckCompilerFlags2 function appends a unique suffix to # the HAS_CXX_FLAG and HAS_C_FLAG variable. This suffix is created using a 'clean version' of the - # flag to test. The value of HAS_C(XX)_FLAG_${suffix} additonally needs to be a valid + # flag to test. The value of HAS_C(XX)_FLAG_${suffix} additionally needs to be a valid # pre-processor token because CHECK_CXX_COMPILER_FLAG adds it as a definition to the compiler # arguments. An invalid token triggers compiler warnings, which in case of the "-Werror" flag # leads to false-negative checks. string(REGEX REPLACE "[/-]" "_" suffix ${FLAG_TO_TEST}) string(REGEX REPLACE "[, \\$\\+\\*\\{\\}\\(\\)\\#]" "" suffix ${suffix}) # workaround for gcc's strange behaviour on -Wno-... options in combination with -Werror # we test the flag without the "no-" prefix because that is more reliable string(REGEX REPLACE "^-Wno-" "-W" FLAG_TO_TEST_FIXED ${FLAG_TO_TEST}) CHECK_CXX_COMPILER_FLAG(${FLAG_TO_TEST_FIXED} HAS_CXX_FLAG_${suffix}) if(HAS_CXX_FLAG_${suffix}) set(${CXX_RESULT_VAR} "${_saved_cxx_result_var} ${FLAG_TO_TEST}") string(STRIP ${${CXX_RESULT_VAR}} ${CXX_RESULT_VAR}) set(${CXX_RESULT_VAR} "${${CXX_RESULT_VAR}}" PARENT_SCOPE) endif() CHECK_C_COMPILER_FLAG(${FLAG_TO_TEST_FIXED} HAS_C_FLAG_${suffix}) if(HAS_C_FLAG_${suffix}) set(${C_RESULT_VAR} "${_saved_c_result_var} ${FLAG_TO_TEST}") string(STRIP ${${C_RESULT_VAR}} ${C_RESULT_VAR}) set(${C_RESULT_VAR} "${${C_RESULT_VAR}}" PARENT_SCOPE) endif() endfunction() diff --git a/CMake/mitkFunctionCleanExternalProject.cmake b/CMake/mitkFunctionCleanExternalProject.cmake index c7d34c24d8..bea2ff615c 100644 --- a/CMake/mitkFunctionCleanExternalProject.cmake +++ b/CMake/mitkFunctionCleanExternalProject.cmake @@ -1,53 +1,53 @@ #[[ mitkCleanExternalProject() The MITK superbuild script stores hashes of all external project scripts and compares them with previous hashes, if they exist. This comparison potentially detects changes in an external project that may require cleaning of previous build artifacts to prevent MITK from referring to mixed versions of the same external project. - This function tries to mimic the manual cleaning of old build articafts + This function tries to mimic the manual cleaning of old build artifacts by removing the source, build, and stamp directory of the external project as well as all files in the external project's install manifest, if found. The motivation for this function is to improve build times in continuous integration and to make non-nightly build clients more robust against external project updates. ]] function(mitkCleanExternalProject ep_name) message(STATUS "Cleaning '${ep_name}'") set(source_dir "${ep_prefix}/src/${ep_name}") set(binary_dir "${source_dir}-build") set(stamp_dir "${source_dir}-stamp") set(install_manifest_file "${binary_dir}/install_manifest.txt") if(EXISTS ${install_manifest_file}) message(STATUS " Validating install manifest") string(LENGTH ${CMAKE_BINARY_DIR} cmake_binary_dir_length) file(STRINGS ${install_manifest_file} installed_files) foreach(f ${installed_files}) string(SUBSTRING "${f}" 0 ${cmake_binary_dir_length} f_prefix) if(NOT f_prefix STREQUAL CMAKE_BINARY_DIR) message(FATAL_ERROR "'${f}' in ${install_manifest_file}' refers to a file outside of '${CMAKE_BINARY_DIR}'!") endif() endforeach() message(STATUS " Uninstalling") file(REMOVE ${installed_files}) endif() if(source_dir) message(STATUS " Removing source directory") file(REMOVE_RECURSE ${source_dir}) endif() if(binary_dir) message(STATUS " Removing build directory") file(REMOVE_RECURSE ${binary_dir}) endif() if(stamp_dir) message(STATUS " Removing stamp directory") file(REMOVE_RECURSE ${stamp_dir}) endif() endfunction() diff --git a/CMake/mitkFunctionCreateModule.cmake b/CMake/mitkFunctionCreateModule.cmake index ce0c15267b..d20c6c61cd 100644 --- a/CMake/mitkFunctionCreateModule.cmake +++ b/CMake/mitkFunctionCreateModule.cmake @@ -1,647 +1,647 @@ ################################################################## # # mitk_create_module # #! Creates a module for the automatic module dependency system within MITK. #! #! Example: #! #! \code #! mitk_create_module( #! DEPENDS PUBLIC MitkCore #! PACKAGE_DEPENDS #! PRIVATE Qt6|Xml+Network #! PUBLIC ITK|Watersheds #! \endcode #! #! The parameter specifies the name of the module which is used #! to create a logical target name. The parameter is optional in case the #! MITK_MODULE_NAME_DEFAULTS_TO_DIRECTORY_NAME variable evaluates to TRUE. The #! module name will then be derived from the directory name in which this #! function is called. #! #! If set, the following variables will be used to validate the module name: #! #! MITK_MODULE_NAME_REGEX_MATCH The module name must match this regular expression. #! MITK_MODULE_NAME_REGEX_NOT_MATCH The module name must not match this regular expression. #! #! If the MITK_MODULE_NAME_PREFIX variable is set, the module name will be prefixed #! with its contents. #! #! A modules source files are specified in a separate CMake file usually #! called files.cmake, located in the module root directory. The #! mitk_create_module() macro evaluates the following CMake variables #! from the files.cmake file: #! #! - CPP_FILES A list of .cpp files #! - H_FILES A list of .h files without a corresponding .cpp file #! - TXX_FILES A list of .txx files #! - RESOURCE_FILES A list of files (resources) which are embedded into the module #! - MOC_H_FILES A list of Qt header files which should be processed by the MOC #! - UI_FILES A list of .ui Qt UI files #! - QRC_FILES A list of .qrc Qt resource files #! - DOX_FILES A list of .dox Doxygen files #! #! List of variables available after the function is called: #! - MODULE_NAME #! - MODULE_TARGET #! - MODULE_IS_ENABLED #! #! \sa mitk_create_executable #! #! Parameters (all optional): #! #! \param The module name (also used as target name) #! \param FILES_CMAKE File name of a CMake file setting source list variables #! (defaults to files.cmake) #! \param VERSION Module version number, e.g. "1.2.0" #! \param AUTOLOAD_WITH A module target name identifying the module which will #! trigger the automatic loading of this module #! \param DEPRECATED_SINCE Marks this modules as deprecated since #! \param DESCRIPTION A description for this module #! #! Multi-value Parameters (all optional): #! #! \param INCLUDE_DIRS Include directories for this module: #! \verbatim #! [[PUBLIC|PRIVATE|INTERFACE] ...]... #! \endverbatim #! The default scope for include directories is PUBLIC. #! \param DEPENDS List of module dependencies: #! \verbatim #! [[PUBLIC|PRIVATE|INTERFACE] ...]... #! \endverbatim #! The default scope for module dependencies is PUBLIC. #! \param PACKAGE_DEPENDS List of public packages dependencies (e.g. Qt, VTK, etc.). #! Package dependencies have the following syntax: #! \verbatim #! [PUBLIC|PRIVATE|INTERFACE] PACKAGE[|COMPONENT1[+COMPONENT2]...] #! \endverbatim #! The default scope for package dependencies is PRIVATE. #! \param ADDITIONAL_LIBS List of additional private libraries linked to this module. #! The folder containing the library will be added to the global list of library search paths. #! \param CPP_FILES List of source files for this module. If the list is non-empty, #! the module does not need to provide a files.cmake file or FILES_CMAKE argument. #! \param H_FILES List of public header files for this module. It is recommended to use #! a files.cmake file instead. #! #! Options (optional) #! #! \param FORCE_STATIC Force building this module as a static library #! \param GCC_DEFAULT_VISIBILITY Do not use gcc visibility flags - all #! symbols will be exported #! \param NO_INIT Do not create CppMicroServices initialization code #! \param NO_FEATURE_INFO Do not create a feature info by calling add_feature_info() #! \param WARNINGS_NO_ERRORS Do not treat compiler warnings as errors # ################################################################## function(mitk_create_module) set(_macro_params VERSION # module version number, e.g. "1.2.0" EXPORT_DEFINE # export macro name for public symbols of this module (DEPRECATED) AUTOLOAD_WITH # a module target name identifying the module which will trigger the # automatic loading of this module FILES_CMAKE # file name of a CMake file setting source list variables # (defaults to files.cmake) DEPRECATED_SINCE # marks this modules as deprecated DESCRIPTION # a description for this module ) set(_macro_multiparams SUBPROJECTS # list of CDash labels (deprecated) INCLUDE_DIRS # include directories: [PUBLIC|PRIVATE|INTERFACE] INTERNAL_INCLUDE_DIRS # include dirs internal to this module (DEPRECATED) DEPENDS # list of modules this module depends on: [PUBLIC|PRIVATE|INTERFACE] DEPENDS_INTERNAL # list of modules this module internally depends on (DEPRECATED) PACKAGE_DEPENDS # list of "packages this module depends on (e.g. Qt, VTK, etc.): [PUBLIC|PRIVATE|INTERFACE] TARGET_DEPENDS # list of CMake targets this module should depend on: [PUBLIC|PRIVATE|INTERFACE] ADDITIONAL_LIBS # list of addidtional private libraries linked to this module. CPP_FILES # list of cpp files H_FILES # list of header files: [PUBLIC|PRIVATE] ) set(_macro_options FORCE_STATIC # force building this module as a static library HEADERS_ONLY # this module is a headers-only library GCC_DEFAULT_VISIBILITY # do not use gcc visibility flags - all symbols will be exported NO_DEFAULT_INCLUDE_DIRS # do not add default include directories like "include" or "." NO_INIT # do not create CppMicroServices initialization code NO_FEATURE_INFO # do not create a feature info by calling add_feature_info() WARNINGS_NO_ERRORS # do not treat compiler warnings as errors EXECUTABLE # create an executable; do not use directly, use mitk_create_executable() instead C_MODULE # compile all source files as C sources CXX_MODULE # compile all source files as C++ sources ) cmake_parse_arguments(MODULE "${_macro_options}" "${_macro_params}" "${_macro_multiparams}" ${ARGN}) set(MODULE_NAME ${MODULE_UNPARSED_ARGUMENTS}) # ----------------------------------------------------------------- # Sanity checks if(NOT MODULE_NAME) if(MITK_MODULE_NAME_DEFAULTS_TO_DIRECTORY_NAME) get_filename_component(MODULE_NAME ${CMAKE_CURRENT_SOURCE_DIR} NAME) else() message(SEND_ERROR "The module name must not be empty") endif() endif() set(_deprecated_args INTERNAL_INCLUDE_DIRS DEPENDS_INTERNAL EXPORT_DEFINE HEADERS_ONLY) foreach(_deprecated_arg ${_deprecated_args}) if(MODULE_${_deprecated_arg}) message(WARNING "The ${_deprecated_arg} argument is deprecated") endif() endforeach() set(_module_type module) set(_Module_type Module) if(MODULE_EXECUTABLE) set(_module_type executable) set(_Module_type Executable) endif() if(MITK_MODULE_NAME_REGEX_MATCH) if(NOT ${MODULE_NAME} MATCHES ${MITK_MODULE_NAME_REGEX_MATCH}) message(SEND_ERROR "The ${_module_type} name \"${MODULE_NAME}\" does not match the regular expression \"${MITK_MODULE_NAME_REGEX_MATCH}\".") endif() endif() if(MITK_MODULE_NAME_REGEX_NOT_MATCH) if(${MODULE_NAME} MATCHES ${MITK_MODULE_NAME_REGEX_NOT_MATCH}) message(SEND_ERROR "The ${_module_type} name \"${MODULE_NAME}\" must not match the regular expression \"${MITK_MODULE_NAME_REGEX_NOT_MATCH}\".") endif() endif() if(MITK_MODULE_NAME_PREFIX AND NOT MODULE_NAME MATCHES "^${MITK_MODULE_NAME_PREFIX}.*$") set(MODULE_NAME "${MITK_MODULE_NAME_PREFIX}${MODULE_NAME}") endif() if(NOT MODULE_FILES_CMAKE) set(MODULE_FILES_CMAKE files.cmake) endif() if(NOT IS_ABSOLUTE ${MODULE_FILES_CMAKE}) set(MODULE_FILES_CMAKE ${CMAKE_CURRENT_SOURCE_DIR}/${MODULE_FILES_CMAKE}) endif() # ----------------------------------------------------------------- # Check if module should be build set(MODULE_TARGET ${MODULE_NAME}) # assume worst case set(MODULE_IS_ENABLED 0) # first we check if we have an explicit module build list if(MITK_MODULES_TO_BUILD) list(FIND MITK_MODULES_TO_BUILD ${MODULE_NAME} _MOD_INDEX) if(_MOD_INDEX EQUAL -1) set(MODULE_IS_EXCLUDED 1) endif() endif() if(NOT MODULE_IS_EXCLUDED) # first of all we check for the dependencies _mitk_parse_package_args(${MODULE_PACKAGE_DEPENDS}) mitk_check_module_dependencies(MODULES ${MODULE_DEPENDS} PACKAGES ${PACKAGE_NAMES} MISSING_DEPENDENCIES_VAR _MISSING_DEP PACKAGE_DEPENDENCIES_VAR PACKAGE_NAMES) if(_MISSING_DEP) if(MODULE_NO_FEATURE_INFO) message("${_Module_type} ${MODULE_NAME} won't be built, missing dependency: ${_MISSING_DEP}") endif() set(MODULE_IS_ENABLED 0) else() foreach(dep ${MODULE_DEPENDS}) if(TARGET ${dep}) get_target_property(AUTLOAD_DEP ${dep} MITK_AUTOLOAD_DIRECTORY) if (AUTLOAD_DEP) message(SEND_ERROR "Module \"${MODULE_NAME}\" has an invalid dependency on autoload module \"${dep}\". Check MITK_CREATE_MODULE usage for \"${MODULE_NAME}\".") endif() endif() endforeach(dep) set(MODULE_IS_ENABLED 1) # now check for every package if it is enabled. This overlaps a bit with # MITK_CHECK_MODULE ... foreach(_package ${PACKAGE_NAMES}) if((DEFINED MITK_USE_${_package}) AND NOT (MITK_USE_${_package})) message("${_Module_type} ${MODULE_NAME} won't be built. Turn on MITK_USE_${_package} if you want to use it.") set(MODULE_IS_ENABLED 0) break() endif() endforeach() endif() endif() # ----------------------------------------------------------------- # Start creating the module if(MODULE_IS_ENABLED) # clear variables defined in files.cmake set(RESOURCE_FILES ) set(CPP_FILES ) set(H_FILES ) set(TXX_FILES ) set(DOX_FILES ) set(UI_FILES ) set(MOC_H_FILES ) set(QRC_FILES ) # clear other variables set(GENERATED_CPP ) set(GENERATED_MOC_CPP ) set(GENERATED_QRC_CPP ) set(GENERATED_UI_CPP ) # check and set-up auto-loading if(MODULE_AUTOLOAD_WITH) if(NOT TARGET "${MODULE_AUTOLOAD_WITH}") message(SEND_ERROR "The module target \"${MODULE_AUTOLOAD_WITH}\" specified as the auto-loading module for \"${MODULE_NAME}\" does not exist") endif() endif() set(_module_autoload_meta_target "${CMAKE_PROJECT_NAME}-autoload") # create a meta-target if it does not already exist if(NOT TARGET ${_module_autoload_meta_target}) add_custom_target(${_module_autoload_meta_target}) set_property(TARGET ${_module_autoload_meta_target} PROPERTY FOLDER "${MITK_ROOT_FOLDER}/Modules/Autoload") endif() if(NOT MODULE_EXPORT_DEFINE) set(MODULE_EXPORT_DEFINE ${MODULE_NAME}_EXPORT) endif() if(MITK_GENERATE_MODULE_DOT) message("MODULEDOTNAME ${MODULE_NAME}") foreach(dep ${MODULE_DEPENDS}) message("MODULEDOT \"${MODULE_NAME}\" -> \"${dep}\" ; ") endforeach(dep) endif(MITK_GENERATE_MODULE_DOT) if (EXISTS ${MODULE_FILES_CMAKE}) include(${MODULE_FILES_CMAKE}) endif() if(MODULE_CPP_FILES) list(APPEND CPP_FILES ${MODULE_CPP_FILES}) endif() if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src") - # Preprend the "src" directory to the cpp file list + # Prepend the "src" directory to the cpp file list set(_cpp_files ${CPP_FILES}) set(CPP_FILES ) foreach(_cpp_file ${_cpp_files}) list(APPEND CPP_FILES "src/${_cpp_file}") endforeach() endif() if(CPP_FILES OR RESOURCE_FILES OR UI_FILES OR MOC_H_FILES OR QRC_FILES) set(MODULE_HEADERS_ONLY 0) if(MODULE_C_MODULE) set_source_files_properties(${CPP_FILES} PROPERTIES LANGUAGE C) elseif(MODULE_CXX_MODULE) set_source_files_properties(${CPP_FILES} PROPERTIES LANGUAGE CXX) endif() else() set(MODULE_HEADERS_ONLY 1) if(MODULE_AUTOLOAD_WITH) message(SEND_ERROR "A headers only module cannot be auto-loaded") endif() endif() set(module_c_flags ) set(module_c_flags_debug ) set(module_c_flags_release ) set(module_cxx_flags ) set(module_cxx_flags_debug ) set(module_cxx_flags_release ) if(MODULE_GCC_DEFAULT_VISIBILITY OR NOT CMAKE_COMPILER_IS_GNUCXX) # We only support hidden visibility for gcc for now. Clang still has troubles with # correctly marking template declarations and explicit template instantiations as exported. set(CMAKE_CXX_VISIBILITY_PRESET default) set(CMAKE_VISIBILITY_INLINES_HIDDEN 0) else() set(CMAKE_CXX_VISIBILITY_PRESET hidden) set(CMAKE_VISIBILITY_INLINES_HIDDEN 1) endif() if(NOT MODULE_WARNINGS_NO_ERRORS) if(MSVC_VERSION) mitkFunctionCheckCAndCXXCompilerFlags("/WX" module_c_flags module_cxx_flags) # this would turn on unused parameter warnings, but unfortunately MSVC cannot # distinguish yet between internal and external headers so this would be triggered # a lot by external code. There is support for it on the way so this line could be # reactivated after https://gitlab.kitware.com/cmake/cmake/issues/17904 has been fixed. # mitkFunctionCheckCAndCXXCompilerFlags("/w34100" module_c_flags module_cxx_flags) else() mitkFunctionCheckCAndCXXCompilerFlags(-Werror module_c_flags module_cxx_flags) # The flag "c++0x-static-nonintegral-init" has been renamed in newer Clang # versions to "static-member-init" # # Also, older Clang and seemingly all gcc versions do not warn if unknown # "-no-*" flags are used, so CMake will happily append any -Wno-* flag to the # command line. This may get confusing if unrelated compiler errors happen and # the error output then additionally contains errors about unknown flags (which # is not the case if there were no compile errors). # # So instead of using -Wno-* we use -Wno-error=*, which will be properly rejected by # the compiler and if applicable, prints the specific warning as a real warning and # not as an error (although -Werror was given). mitkFunctionCheckCAndCXXCompilerFlags("-Wno-error=c++0x-static-nonintegral-init" module_c_flags module_cxx_flags) mitkFunctionCheckCAndCXXCompilerFlags("-Wno-error=static-member-init" module_c_flags module_cxx_flags) mitkFunctionCheckCAndCXXCompilerFlags("-Wno-error=unknown-warning" module_c_flags module_cxx_flags) mitkFunctionCheckCAndCXXCompilerFlags("-Wno-error=gnu" module_c_flags module_cxx_flags) mitkFunctionCheckCAndCXXCompilerFlags("-Wno-error=class-memaccess" module_c_flags module_cxx_flags) mitkFunctionCheckCAndCXXCompilerFlags("-Wno-error=inconsistent-missing-override" module_c_flags module_cxx_flags) mitkFunctionCheckCAndCXXCompilerFlags("-Wno-error=deprecated-copy" module_c_flags module_cxx_flags) mitkFunctionCheckCAndCXXCompilerFlags("-Wno-error=cast-function-type" module_c_flags module_cxx_flags) mitkFunctionCheckCAndCXXCompilerFlags("-Wno-error=deprecated-declarations" module_c_flags module_cxx_flags) mitkFunctionCheckCAndCXXCompilerFlags("-Wno-error=type-limits" module_c_flags module_cxx_flags) endif() endif() if(MODULE_FORCE_STATIC) set(_STATIC STATIC) else() set(_STATIC ) endif(MODULE_FORCE_STATIC) if(NOT MODULE_HEADERS_ONLY) if(NOT MODULE_NO_INIT OR RESOURCE_FILES) find_package(CppMicroServices QUIET NO_MODULE REQUIRED) endif() if(NOT MODULE_NO_INIT) usFunctionGenerateModuleInit(CPP_FILES) endif() set(binary_res_files ) set(source_res_files ) if(RESOURCE_FILES) if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/resource") set(res_dir resource) elseif(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/Resources") set(res_dir Resources) else() message(SEND_ERROR "Resources specified but ${CMAKE_CURRENT_SOURCE_DIR}/resource directory not found.") endif() foreach(res_file ${RESOURCE_FILES}) if(EXISTS ${CMAKE_CURRENT_BINARY_DIR}/${res_dir}/${res_file}) list(APPEND binary_res_files "${res_file}") else() list(APPEND source_res_files "${res_file}") endif() endforeach() # Add a source level dependencies on resource files usFunctionGetResourceSource(TARGET ${MODULE_TARGET} OUT CPP_FILES) endif() endif() if(MITK_USE_Qt6) if(UI_FILES) qt_wrap_ui(GENERATED_UI_CPP ${UI_FILES}) endif() if(MOC_H_FILES) qt_wrap_cpp(GENERATED_MOC_CPP ${MOC_H_FILES} OPTIONS -DBOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION) endif() if(QRC_FILES) qt_add_resources(GENERATED_QRC_CPP ${QRC_FILES}) endif() endif() set(GENERATED_CPP ${GENERATED_CPP} ${GENERATED_UI_CPP} ${GENERATED_MOC_CPP} ${GENERATED_QRC_CPP}) mitkFunctionOrganizeSources( SOURCE ${CPP_FILES} HEADER ${H_FILES} TXX ${TXX_FILES} DOC ${DOX_FILES} UI ${UI_FILES} QRC ${QRC_FILES} MOC ${GENERATED_MOC_CPP} GEN_QRC ${GENERATED_QRC_CPP} GEN_UI ${GENERATED_UI_CPP} ) set(coverage_sources ${CPP_FILES} ${H_FILES} ${GLOBBED__H_FILES} ${CORRESPONDING__H_FILES} ${TXX_FILES} ${TOOL_CPPS} ${TOOL_GUI_CPPS}) # --------------------------------------------------------------- # Create the actual module target if(MODULE_HEADERS_ONLY) add_library(${MODULE_TARGET} INTERFACE) # INTERFACE_LIBRARY targets may only have whitelisted properties. The property "FOLDER" is not allowed. # set_property(TARGET ${MODULE_TARGET} PROPERTY FOLDER "${MITK_ROOT_FOLDER}/Modules") else() if(MODULE_EXECUTABLE) if(MITK_SHOW_CONSOLE_WINDOW) set(_SHOW_CONSOLE_OPTION "") else() set(_SHOW_CONSOLE_OPTION WIN32) endif() add_executable(${MODULE_TARGET} ${_SHOW_CONSOLE_OPTION} ${MODULE_CPP_FILES} ${coverage_sources} ${CPP_FILES_GENERATED} ${GENERATED_CPP} ${DOX_FILES} ${UI_FILES} ${QRC_FILES} ${WINDOWS_ICON_RESOURCE_FILE}) if(WIN32) mitk_add_manifest(${MODULE_TARGET}) endif() set_property(TARGET ${MODULE_TARGET} PROPERTY FOLDER "${MITK_ROOT_FOLDER}/Modules/Executables") set(_us_module_name main) else() add_library(${MODULE_TARGET} ${_STATIC} ${coverage_sources} ${CPP_FILES_GENERATED} ${GENERATED_CPP} ${DOX_FILES} ${UI_FILES} ${QRC_FILES}) set_property(TARGET ${MODULE_TARGET} PROPERTY FOLDER "${MITK_ROOT_FOLDER}/Modules") set(_us_module_name ${MODULE_TARGET}) endif() # Apply properties to the module target. target_compile_definitions(${MODULE_TARGET} PRIVATE US_MODULE_NAME=${_us_module_name}) if(MODULE_C_MODULE) if(module_c_flags) string(REPLACE " " ";" module_c_flags "${module_c_flags}") target_compile_options(${MODULE_TARGET} PRIVATE ${module_c_flags}) endif() if(module_c_flags_debug) string(REPLACE " " ";" module_c_flags_debug "${module_c_flags_debug}") target_compile_options(${MODULE_TARGET} PRIVATE $<$:${module_c_flags_debug}>) endif() if(module_c_flags_release) string(REPLACE " " ";" module_c_flags_release "${module_c_flags_release}") target_compile_options(${MODULE_TARGET} PRIVATE $<$:${module_c_flags_release}>) endif() else() if(module_cxx_flags) string(REPLACE " " ";" module_cxx_flags "${module_cxx_flags}") target_compile_options(${MODULE_TARGET} PRIVATE ${module_cxx_flags}) endif() if(module_cxx_flags_debug) string(REPLACE " " ";" module_cxx_flags_debug "${module_cxx_flags_debug}") target_compile_options(${MODULE_TARGET} PRIVATE $<$:${module_cxx_flags_debug}>) endif() if(module_cxx_flags_release) string(REPLACE " " ";" module_cxx_flags_release "${module_cxx_flags_release}") target_compile_options(${MODULE_TARGET} PRIVATE $<$:${module_cxx_flags_release}>) endif() endif() set_property(TARGET ${MODULE_TARGET} PROPERTY US_MODULE_NAME ${_us_module_name}) # Add additional library search directories to a global property which # can be evaluated by other CMake macros, e.g. our install scripts. if(MODULE_ADDITIONAL_LIBS) target_link_libraries(${MODULE_TARGET} PRIVATE ${MODULE_ADDITIONAL_LIBS}) get_property(_mitk_additional_library_search_paths GLOBAL PROPERTY MITK_ADDITIONAL_LIBRARY_SEARCH_PATHS) foreach(_lib_filepath ${MODULE_ADDITIONAL_LIBS}) get_filename_component(_search_path "${_lib_filepath}" PATH) if(_search_path) list(APPEND _mitk_additional_library_search_paths "${_search_path}") endif() endforeach() if(_mitk_additional_library_search_paths) list(REMOVE_DUPLICATES _mitk_additional_library_search_paths) set_property(GLOBAL PROPERTY MITK_ADDITIONAL_LIBRARY_SEARCH_PATHS ${_mitk_additional_library_search_paths}) endif() endif() # add the target name to a global property which is used in the top-level # CMakeLists.txt file to export the target set_property(GLOBAL APPEND PROPERTY MITK_MODULE_TARGETS ${MODULE_TARGET}) if(MODULE_AUTOLOAD_WITH) # for auto-loaded modules, adapt the output directory add_dependencies(${_module_autoload_meta_target} ${MODULE_TARGET}) if(WIN32) set(_module_output_prop RUNTIME_OUTPUT_DIRECTORY) else() set(_module_output_prop LIBRARY_OUTPUT_DIRECTORY) endif() set(_module_output_dir ${CMAKE_${_module_output_prop}}/${MODULE_AUTOLOAD_WITH}) get_target_property(_module_is_imported ${MODULE_AUTOLOAD_WITH} IMPORTED) if(NOT _module_is_imported) # if the auto-loading module is not imported, get its location # and put the auto-load module relative to it. get_target_property(_module_output_dir ${MODULE_AUTOLOAD_WITH} ${_module_output_prop}) set_target_properties(${MODULE_TARGET} PROPERTIES ${_module_output_prop} ${_module_output_dir}/${MODULE_AUTOLOAD_WITH}) else() set_target_properties(${MODULE_TARGET} PROPERTIES ${_module_output_prop} ${CMAKE_${_module_output_prop}}/${MODULE_AUTOLOAD_WITH}) endif() set_target_properties(${MODULE_TARGET} PROPERTIES MITK_AUTOLOAD_DIRECTORY ${MODULE_AUTOLOAD_WITH}) # add the auto-load module name as a property set_property(TARGET ${MODULE_AUTOLOAD_WITH} APPEND PROPERTY MITK_AUTOLOAD_TARGETS ${MODULE_TARGET}) endif() if(binary_res_files) usFunctionAddResources(TARGET ${MODULE_TARGET} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${res_dir} FILES ${binary_res_files}) endif() if(source_res_files) usFunctionAddResources(TARGET ${MODULE_TARGET} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/${res_dir} FILES ${source_res_files}) endif() if(binary_res_files OR source_res_files) usFunctionEmbedResources(TARGET ${MODULE_TARGET}) endif() if(MODULE_DEPRECATED_SINCE) set_property(TARGET ${MODULE_TARGET} PROPERTY MITK_MODULE_DEPRECATED_SINCE ${MODULE_DEPRECATED_SINCE}) endif() # create export macros if (NOT MODULE_EXECUTABLE) set(_export_macro_name ) if(MITK_LEGACY_EXPORT_MACRO_NAME) set(_export_macro_names EXPORT_MACRO_NAME ${MODULE_EXPORT_DEFINE} NO_EXPORT_MACRO_NAME ${MODULE_NAME}_NO_EXPORT DEPRECATED_MACRO_NAME ${MODULE_NAME}_DEPRECATED NO_DEPRECATED_MACRO_NAME ${MODULE_NAME}_NO_DEPRECATED ) endif() generate_export_header(${MODULE_NAME} ${_export_macro_names} EXPORT_FILE_NAME ${MODULE_NAME}Exports.h ) endif() target_include_directories(${MODULE_TARGET} PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) endif() # --------------------------------------------------------------- # Properties for both header-only and compiled modules if(MODULE_HEADERS_ONLY) set(_module_property_type INTERFACE) else() set(_module_property_type PUBLIC) endif() if(MODULE_TARGET_DEPENDS) target_link_libraries(${MODULE_TARGET} ${MODULE_TARGET_DEPENDS}) endif() set(DEPENDS "${MODULE_DEPENDS}") if(NOT MODULE_NO_INIT AND NOT MODULE_HEADERS_ONLY) # Add a CppMicroServices dependency implicitly, since it is # needed for the generated "module initialization" code. set(DEPENDS "CppMicroServices;${DEPENDS}") endif() if(DEPENDS OR MODULE_PACKAGE_DEPENDS) mitk_use_modules(TARGET ${MODULE_TARGET} MODULES ${DEPENDS} PACKAGES ${MODULE_PACKAGE_DEPENDS} ) endif() # add include directories if(MODULE_INTERNAL_INCLUDE_DIRS) target_include_directories(${MODULE_TARGET} PRIVATE ${MODULE_INTERNAL_INCLUDE_DIRS}) endif() if(NOT MODULE_NO_DEFAULT_INCLUDE_DIRS) if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/include) target_include_directories(${MODULE_TARGET} ${_module_property_type} include) else() target_include_directories(${MODULE_TARGET} ${_module_property_type} .) endif() if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/src) target_include_directories(${MODULE_TARGET} PRIVATE src) endif() endif() target_include_directories(${MODULE_TARGET} ${_module_property_type} ${MODULE_INCLUDE_DIRS}) endif() # ----------------------------------------------------------------- # Record missing dependency information if(_MISSING_DEP) if(MODULE_DESCRIPTION) set(MODULE_DESCRIPTION "${MODULE_DESCRIPTION} (missing dependencies: ${_MISSING_DEP})") else() set(MODULE_DESCRIPTION "(missing dependencies: ${_MISSING_DEP})") endif() endif() if(NOT MODULE_NO_FEATURE_INFO) add_feature_info(${MODULE_NAME} MODULE_IS_ENABLED "${MODULE_DESCRIPTION}") endif() set(MODULE_NAME ${MODULE_NAME} PARENT_SCOPE) set(MODULE_TARGET ${MODULE_TARGET} PARENT_SCOPE) set(MODULE_IS_ENABLED ${MODULE_IS_ENABLED} PARENT_SCOPE) endfunction() diff --git a/CMake/mitkFunctionOrganizeSources.cmake b/CMake/mitkFunctionOrganizeSources.cmake index 5a76c8d841..e3f7029358 100644 --- a/CMake/mitkFunctionOrganizeSources.cmake +++ b/CMake/mitkFunctionOrganizeSources.cmake @@ -1,85 +1,85 @@ function(mitkFunctionOrganizeSources) # this functions gets a filelist as input and looks # for corresponding h-files to add them to the project. # additionally files are grouped in source-groups. # No parameters explicitly declared here, because # we want to allow for variable argument lists, which # are later accessed by the keyword foreach(MYFILE ${ARGV}) # output: after calling the macro, files that were found - # correspondigly to the given files are stored in the + # correspondingly to the given files are stored in the # variable: # ${CORRESPONDING_H_FILES} # ${CORRESPONDING_TXX_FILES} # ${CORRESPONDING_UI_H_FILES} # Globbed can be found in the variables # ${GLOBBED_TXX_FILES} (CURRENTLY COMMENTED OUT) # ${GLOBBED_DOX_FILES} cmake_parse_arguments(_ORG "" "" "HEADER;SOURCE;TXX;DOC;MOC;GEN_QRC;GEN_UI;META;UI;QRC" ${ARGN}) set(CORRESPONDING__H_FILES "") set(GLOBBED__H_FILES "") if(_ORG_SOURCE) foreach(_file ${_ORG_SOURCE}) string(REGEX REPLACE "(.*)\\.(txx|cpp|c|cxx)$" "\\1.h" H_FILE ${_file}) if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${H_FILE}") list(APPEND CORRESPONDING__H_FILES "${H_FILE}") endif() endforeach() else() file(GLOB_RECURSE GLOBBED__H_FILES *.h) endif() set(CORRESPONDING__H_FILES ${CORRESPONDING__H_FILES} PARENT_SCOPE) set(GLOBBED__H_FILES ${GLOBBED__H_FILES} PARENT_SCOPE) if(_ORG_GEN_QRC OR _ORG_GEN_UI OR _ORG_MOC) source_group("Generated\\Qt QRC Source Files" FILES ${_ORG_GEN_QRC}) source_group("Generated\\Qt UI Header Files" FILES ${_ORG_GEN_UI}) source_group("Generated\\Qt MOC Source Files" FILES ${_ORG_MOC}) endif() #_MACRO_APPEND_TO_LIST(_ORG_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/") source_group("== Source Files ==" FILES ${_ORG_SOURCE}) #_MACRO_APPEND_TO_LIST(_ORG_TXX "${CMAKE_CURRENT_SOURCE_DIR}/") source_group("== Template Files ==" FILES ${_ORG_TXX}) #_MACRO_APPEND_TO_LIST(_ORG_HEADER "${CMAKE_CURRENT_SOURCE_DIR}/") source_group("== Header Files ==" FILES ${_ORG_HEADER} ${CORRESPONDING__H_FILES} ${GLOBBED__H_FILES}) if(_ORG_UI) #_MACRO_APPEND_TO_LIST(_ORG_UI "${CMAKE_CURRENT_SOURCE_DIR}/") source_group("QT UI Files" FILES ${_ORG_UI}) endif() if(_ORG_DOC) #_MACRO_APPEND_TO_LIST(_ORG_DOC "${CMAKE_CURRENT_SOURCE_DIR}/") source_group("Doxygen Files" FILES ${_ORG_DOC}) endif() if(_ORG_QRC) #_MACRO_APPEND_TO_LIST(_ORG_QRC "${CMAKE_CURRENT_SOURCE_DIR}/") source_group("Qt Resource Files" FILES ${_ORG_QRC}) endif() if(_ORG_META) source_group("Plugin META Files" FILES ${_ORG_META}) endif() endfunction() macro(_MACRO_APPEND_TO_LIST _list _value) set(_origlist ${${_list}}) set(${_list} ) foreach(_element ${_origlist}) list(APPEND ${_list} "${_value}${_element}") endforeach() endmacro() diff --git a/CMake/mitkFunctionWhitelists.cmake b/CMake/mitkFunctionWhitelists.cmake index d956918f09..8e102ec54e 100644 --- a/CMake/mitkFunctionWhitelists.cmake +++ b/CMake/mitkFunctionWhitelists.cmake @@ -1,244 +1,244 @@ ############################################################################### # # mitkFunctionCreateWhitelistPaths # #! Creates advanced cache variables for setting the internal and external #! whitelist directories. #! #! USAGE: #! #! \code #! mitkFunctionCreateWhitelistPaths() #! \endcode #! #! The parameter specifies the prefix used for the created variables #! _WHITELISTS_INTERNAL_PATH and _WHITELISTS_EXTERNAL_PATH. #! #! Default values: #! _WHITELISTS_INTERNAL_PATH = _SOURCE_DIR/CMake/Whitelists #! _WHITELISTS_EXTERNAL_PATH = %HOME%/.mitk/Whitelists #! #! List of variables available after the function is called: #! - _WHITELISTS_INTERNAL_PATH #! - _WHITELISTS_EXTERNAL_PATH #! #! Parameters: #! \param The prefix of the created cache variables. # ############################################################################### function(mitkFunctionCreateWhitelistPaths) set(${ARGV0}_WHITELISTS_INTERNAL_PATH "${${ARGV0}_SOURCE_DIR}/CMake/Whitelists" CACHE PATH "") set(${ARGV0}_WHITELISTS_EXTERNAL_PATH ".mitk/Whitelists") if(WIN32) set(${ARGV0}_WHITELISTS_EXTERNAL_PATH "$ENV{HOMEDRIVE}$ENV{HOMEPATH}/${${ARGV0}_WHITELISTS_EXTERNAL_PATH}") else() set(${ARGV0}_WHITELISTS_EXTERNAL_PATH "$ENV{HOME}/${${ARGV0}_WHITELISTS_EXTERNAL_PATH}") endif() FILE(TO_CMAKE_PATH "${${ARGV0}_WHITELISTS_EXTERNAL_PATH}" ${ARGV0}_WHITELISTS_EXTERNAL_PATH) set(${ARGV0}_WHITELISTS_EXTERNAL_PATH "${${ARGV0}_WHITELISTS_EXTERNAL_PATH}" CACHE PATH "") mark_as_advanced( ${ARGV0}_WHITELISTS_INTERNAL_PATH ${ARGV0}_WHITELISTS_EXTERNAL_PATH ) endfunction() ############################################################################### # # mitkFunctionFindWhitelists # -#! Adds all whitelists found in specfied whitelist paths to the advanced cache +#! Adds all whitelists found in specified whitelist paths to the advanced cache #! variable _WHITELIST as enumeration entries. #! #! USAGE: #! #! \code #! mitkFunctionFindWhitelists() #! \endcode #! #! The parameter specifies the prefix used for the created #! cache variable _WHITELIST. Its default value is "None". #! The function mitkFunctionCreateWhitelistPaths must be called #! with the same prior to this function. #! #! Whitelists are *.cmake files which set the two list variables #! enabled_modules and enabled_plugins. #! #! List of variables available after the function is called: #! - _WHITELIST #! #! \sa mitkFunctionCreateWhitelistPaths #! #! Parameters: #! \param The prefix of the created cache variable. # ############################################################################### function(mitkFunctionFindWhitelists) set(whitelists "None") file(GLOB internalWhitelistFiles "${${ARGV0}_WHITELISTS_INTERNAL_PATH}/*.cmake") foreach(whitelistFile ${internalWhitelistFiles}) get_filename_component(whitelistFile "${whitelistFile}" NAME_WE) list(APPEND whitelists "${whitelistFile}") endforeach() file(GLOB externalWhitelistFiles "${${ARGV0}_WHITELISTS_EXTERNAL_PATH}/*.cmake") foreach(whitelistFile ${externalWhitelistFiles}) get_filename_component(whitelistFile "${whitelistFile}" NAME_WE) list(APPEND whitelists "${whitelistFile} (external)") endforeach() set(${ARGV0}_WHITELIST "None" CACHE STRING "") set_property(CACHE ${ARGV0}_WHITELIST PROPERTY STRINGS ${whitelists}) mark_as_advanced(${ARGV0}_WHITELIST) endfunction() ############################################################################### # # mitkFunctionWhitelistModules # #! Only enables modules which are present in the currently set whitelist or #! all modules if no whitelist is specified at all. #! #! USAGE: #! #! \code #! set( #! ModuleDir #! AnotherModuleDir #! ... #! ) #! mitkFunctionWhitelistModules( ) #! \endcode #! #! The parameter specifies the prefix used to get the #! currently set whitelist from _WHITELIST. Both functions #! mitkFunctionCreateWhitelistPaths and mitkFunctionFindWhitelists #! must be called with the same prior to this function. #! The list must contain the module directory names instead #! of the module names itself, as the entries are used in #! add_directory calls. #! #! \sa mitkFunctionCreateWhitelistPaths #! \sa mitkFunctionFindWhitelists #! #! Parameters: #! \param The prefix of the white list cache variable. #! \param The module directory list variable. # ############################################################################### function(mitkFunctionWhitelistModules) if(${ARGV0}_WHITELIST STREQUAL "None") foreach(module ${${ARGV1}}) add_subdirectory(Modules/${module}) endforeach() else() string(FIND "${${ARGV0}_WHITELIST}" " (external)" index REVERSE) if(${index} EQUAL -1) set(whitelistFile "${${ARGV0}_WHITELISTS_INTERNAL_PATH}/${${ARGV0}_WHITELIST}.cmake") else() string(SUBSTRING "${${ARGV0}_WHITELIST}" 0 ${index} whitelistFile) set(whitelistFile "${${ARGV0}_WHITELISTS_EXTERNAL_PATH}/${whitelistFile}.cmake") endif() include(${whitelistFile}) if(NOT DEFINED enabled_modules) message(FATAL_ERROR "Variable 'enabled_modules' not set in whitelist file '${whitelistFile}'!") endif() foreach(module ${${ARGV1}}) list(FIND enabled_modules ${module} index) if(NOT index EQUAL -1) add_subdirectory(Modules/${module}) endif() endforeach() endif() endfunction() ############################################################################### # # mitkFunctionWhitelistPlugins # #! Only enables plugins which are present in the currently set whitelist or #! all plugins if no whitelist is specified at all. #! #! USAGE: #! #! \code #! set( #! org.example.plugin:OFF #! org.example.another.plugin:ON #! ... #! ) #! mitkFunctionWhitelistPlugins( ) #! \endcode #! #! The parameter specifies the prefix used to get the #! currently set whitelist from _WHITELIST. Both functions #! mitkFunctionCreateWhitelistPaths and mitkFunctionFindWhitelists #! must be called with the same prior to this function. #! The list must contain the plugin names. This function #! removes plugins not found in the currently set whitelist from #! the variable. Note that plugins which are OFF by #! default are not switched on. #! #! \sa mitkFunctionCreateWhitelistPaths #! \sa mitkFunctionFindWhitelists #! #! Parameters: #! \param The prefix of the white list cache variable. #! \param The plugin list variable to be modified. # ############################################################################### function(mitkFunctionWhitelistPlugins) if(${ARGV0}_WHITELIST STREQUAL "None") return() endif() string(FIND "${${ARGV0}_WHITELIST}" " (external)" index REVERSE) if(${index} EQUAL -1) set(whitelistFile "${${ARGV0}_WHITELISTS_INTERNAL_PATH}/${${ARGV0}_WHITELIST}.cmake") else() string(SUBSTRING "${${ARGV0}_WHITELIST}" 0 ${index} whitelistFile) set(whitelistFile "${${ARGV0}_WHITELISTS_EXTERNAL_PATH}/${whitelistFile}.cmake") endif() include(${whitelistFile}) if(NOT DEFINED enabled_plugins) message(FATAL_ERROR "Variable 'enabled_plugins' not set in whitelist file '${whitelistFile}'!") endif() set(plugins "") foreach(plugin ${${ARGV1}}) string(FIND ${plugin} ":" index REVERSE) if (NOT index EQUAL -1) string(SUBSTRING ${plugin} 0 ${index} _plugin) else() set(_plugin ${plugin}) endif() list(FIND enabled_plugins ${_plugin} index) if(NOT index EQUAL -1) list(APPEND plugins ${plugin}) endif() endforeach() set(${ARGV1} ${plugins} PARENT_SCOPE) endfunction() diff --git a/CMake/mitkMacroInstallHelperApp.cmake b/CMake/mitkMacroInstallHelperApp.cmake index e27c3db629..e10d07b09e 100644 --- a/CMake/mitkMacroInstallHelperApp.cmake +++ b/CMake/mitkMacroInstallHelperApp.cmake @@ -1,74 +1,74 @@ -#! MITK specific cross plattform install macro +#! MITK specific cross platform install macro #! #! Usage: MITK_INSTALL_HELPER_APP(target1 [target2] ....) #! macro(MITK_INSTALL_HELPER_APP) cmake_parse_arguments(_install "GLOB_PLUGINS" "" "TARGETS;EXECUTABLES;PLUGINS;LIBRARY_DIRS" ${ARGN}) list(APPEND _install_TARGETS ${_install_DEFAULT_ARGS}) # TODO: how to supply to correct intermediate directory?? # CMAKE_CFG_INTDIR is not expanded to actual values inside the install(CODE "...") macro ... set(intermediate_dir .) if(WIN32) set(intermediate_dir Release) endif() mitkFunctionGetLibrarySearchPaths(DIRS ${intermediate_dir}) if(APPLE) list(APPEND DIRS "/usr/lib") endif(APPLE) foreach(_target ${_install_EXECUTABLES}) set(_qt_conf_install_dirs "") set(_target_locations "") get_filename_component(_target_name ${_target} NAME) if(APPLE) if(NOT MACOSX_BUNDLE_NAMES) set(_qt_conf_install_dirs bin) set(_target_locations bin/${_target_name}) install(PROGRAMS ${_target} 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/${_target_name}) list(APPEND _target_locations ${_current_target_location}) install(PROGRAMS ${_target} DESTINATION ${bundle_name}.app/Contents/MacOS/) endforeach() endif(NOT MACOSX_BUNDLE_NAMES) else() set(_target_location bin/${_target_name}) set(_qt_conf_install_dirs bin) install(PROGRAMS ${_target} DESTINATION bin) if(UNIX AND NOT WIN32) # Remove the rpath from helper applications. We assume that all dependencies # are installed into the same location as the helper application. install(CODE "file(RPATH_REMOVE FILE \"\${CMAKE_INSTALL_PREFIX}/${_target_location}\")") endif() endif() foreach(_target_location ${_target_locations}) _fixup_target() endforeach(_target_location) #-------------------------------------------------------------------------------- # install a qt.conf file # this inserts some cmake code into the install script to write the file if(MITK_USE_Qt6) set(_qt_conf_plugin_install_prefix .) if(APPLE) set(_qt_conf_plugin_install_prefix ./MacOS) endif() foreach(_qt_conf_install_dir ${_qt_conf_install_dirs}) install(CODE "file(WRITE \"\${CMAKE_INSTALL_PREFIX}/${_qt_conf_install_dir}/qt.conf\" \" [Paths] Prefix=${_qt_conf_plugin_install_prefix} \")") endforeach() endif() endforeach() endmacro(MITK_INSTALL_HELPER_APP) diff --git a/CMake/mitkMacroInstallTargets.cmake b/CMake/mitkMacroInstallTargets.cmake index c20837aae8..371c0218ef 100644 --- a/CMake/mitkMacroInstallTargets.cmake +++ b/CMake/mitkMacroInstallTargets.cmake @@ -1,71 +1,71 @@ # -# MITK specific cross plattform install macro +# MITK specific cross platform install macro # # Usage: MITK_INSTALL_TARGETS(target1 [target2] ....) # macro(MITK_INSTALL_TARGETS) cmake_parse_arguments(_install "GLOB_PLUGINS" "" "TARGETS;EXECUTABLES;PLUGINS;LIBRARY_DIRS" ${ARGN}) list(APPEND _install_TARGETS ${_install_DEFAULT_ARGS}) # TODO: how to supply the correct intermediate directory?? # CMAKE_CFG_INTDIR is not expanded to actual values inside the install(CODE "...") macro ... set(intermediate_dir .) if(WIN32) set(intermediate_dir Release) endif() foreach(_target ${_install_EXECUTABLES}) get_target_property(_is_bundle ${_target} MACOSX_BUNDLE) set(_qt_conf_install_dirs "") set(_target_locations "") if(APPLE) if(_is_bundle) set(_target_locations ${_target}.app) set(${_target_locations}_qt_plugins_install_dir ${_target}.app/Contents/MacOS) set(_bundle_dest_dir ${_target}.app/Contents/MacOS) set(_qt_conf_install_dirs ${_target}.app/Contents/Resources) install(TARGETS ${_target} BUNDLE DESTINATION . ) else() if(NOT MACOSX_BUNDLE_NAMES) set(_qt_conf_install_dirs bin) set(_target_locations bin/${_target}) install(TARGETS ${_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/${_target}) list(APPEND _target_locations ${_current_target_location}) install(TARGETS ${_target} RUNTIME DESTINATION ${bundle_name}.app/Contents/MacOS/) endforeach() endif() endif() else() set(_target_locations bin/${_target}${CMAKE_EXECUTABLE_SUFFIX}) set(_qt_conf_install_dirs bin) install(TARGETS ${_target} RUNTIME DESTINATION bin) endif() foreach(_target_location ${_target_locations}) _fixup_target() endforeach() #-------------------------------------------------------------------------------- # install a qt.conf file # this inserts some cmake code into the install script to write the file if(MITK_USE_Qt6) set(_qt_conf_plugin_install_prefix .) if(APPLE) set(_qt_conf_plugin_install_prefix ./MacOS) endif() foreach(_qt_conf_install_dir ${_qt_conf_install_dirs}) install(CODE "file(WRITE \"\${CMAKE_INSTALL_PREFIX}/${_qt_conf_install_dir}/qt.conf\" \" [Paths] Prefix=${_qt_conf_plugin_install_prefix} \")") endforeach() endif() endforeach() endmacro() diff --git a/CMake/mitkMacroMultiplexPicType.cmake b/CMake/mitkMacroMultiplexPicType.cmake index d95bb50501..584bc8c8d1 100644 --- a/CMake/mitkMacroMultiplexPicType.cmake +++ b/CMake/mitkMacroMultiplexPicType.cmake @@ -1,20 +1,20 @@ # # MITK_MULTIPLEX_PICTYPE: generate separated source files for different # data types to reduce memory consumption of compiler during template # instantiation # # Param "file" should be named like mitkMyAlgo-TYPE.cpp -# in the file, every occurence of @TYPE@ is replaced by the +# in the file, every occurrence of @TYPE@ is replaced by the # datatype. For each datatype, a new file mitkMyAlgo-datatype.cpp # is generated and added to CPP_FILES_GENERATED. # macro(MITK_MULTIPLEX_PICTYPE file) string(REPLACE "," ";" TYPES "${MITK_ACCESSBYITK_PIXEL_TYPES}") foreach(TYPE ${TYPES}) # create filename for destination string(REPLACE " " "_" quoted_type "${TYPE}") string(REPLACE TYPE ${quoted_type} quoted_file ${file}) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/${file} ${CMAKE_CURRENT_BINARY_DIR}/${quoted_file} @ONLY) set(CPP_FILES_GENERATED ${CPP_FILES_GENERATED} ${CMAKE_CURRENT_BINARY_DIR}/${quoted_file}) endforeach(TYPE) endmacro(MITK_MULTIPLEX_PICTYPE) diff --git a/CMake/mitkSwigAddLibraryDependencies.cmake b/CMake/mitkSwigAddLibraryDependencies.cmake index 5b46f0fc8f..58d381cf35 100644 --- a/CMake/mitkSwigAddLibraryDependencies.cmake +++ b/CMake/mitkSwigAddLibraryDependencies.cmake @@ -1,30 +1,30 @@ #! This CMake macro adds the necessary library and incllude #! directories to a swig-project. #! #! params: #! swig_module : Name of the SWIG module, for example pyMITK #! library_names : Semicolon separated list of the libraries that are included, for example "MitkCore;MitkLog" #! # function inspired by # https://stackoverflow.com/questions/37205274/swig-and-cmake-make-use-of-information-provided-by-target-include-directories # This function tells cmake which additional dependencies are existing # especially with respect to the linker dependencies. function(mitkSwigAddLibraryDependencies swig_module library_names) foreach(library_name ${library_names}) # Adding each library as a linker dependency: swig_link_libraries(${swig_module} ${library_name}) # Extracting all include directories from each given project and # then including these directories to the newly created swig project. get_property(LIBRARY_INCLUDES TARGET ${library_name} PROPERTY INTERFACE_INCLUDE_DIRECTORIES) - # Checking each given librarie to include all includes from this library. + # Checking each given library to include all includes from this library. endforeach() # In addition include python dependencies: include_directories( ${PYTHON_INCLUDE_DIR}) endfunction() diff --git a/CMake/mitkTargetLinkLibrariesWithDynamicLookup.cmake b/CMake/mitkTargetLinkLibrariesWithDynamicLookup.cmake index 52eea078a2..d4424b699e 100644 --- a/CMake/mitkTargetLinkLibrariesWithDynamicLookup.cmake +++ b/CMake/mitkTargetLinkLibrariesWithDynamicLookup.cmake @@ -1,581 +1,581 @@ #.rst: # # Public Functions # ^^^^^^^^^^^^^^^^ # # The following functions are defined: # # .. cmake:command:: mitk_target_link_libraries_with_dynamic_lookup # # :: # # mitk_target_link_libraries_with_dynamic_lookup( []) # # # Useful to "weakly" link a loadable module. For example, it should be used # when compiling a loadable module when the symbols should be resolve from # the run-time environment where the module is loaded, and not a specific # system library. # # Like proper linking, except that the given ```` are not necessarily # linked. Instead, the ```` is produced in a manner that allows for # symbols unresolved within it to be resolved at runtime, presumably by the # given ````. If such a target can be produced, the provided # ```` are not actually linked. # # It links a library to a target such that the symbols are resolved at # run-time not link-time. # # The linker is checked to see if it supports undefined # symbols when linking a shared library. If it does then the library # is not linked when specified with this function. # # On platforms that do not support weak-linking, this function works just # like ``mitk_target_link_libraries``. # # .. note:: # # For OSX it uses ``undefined dynamic_lookup``. This is similar to using # ``-shared`` on Linux where undefined symbols are ignored. # # For more details, see `blog `_ # from Tim D. Smith. # # # .. cmake:command:: mitk_check_dynamic_lookup # # Check if the linker requires a command line flag to allow leaving symbols # unresolved when producing a target of type ```` that is # weakly-linked against a dependency of type ````. # # ```` # can be one of "STATIC", "SHARED", "MODULE", or "EXE". # # ```` # can be one of "STATIC", "SHARED", or "MODULE". # # Long signature: # # :: # # mitk_check_dynamic_lookup( # # # []) # # # Short signature: # # :: # # mitk_check_dynamic_lookup() # set to "MODULE" # # set to "SHARED" # # # The result is cached between invocations and recomputed only when the value # of CMake's linker flag list changes; ``CMAKE_STATIC_LINKER_FLAGS`` if # ```` is "STATIC", and ``CMAKE_SHARED_LINKER_FLAGS`` otherwise. # # # Defined variables: # # ```` # Whether the current C toolchain supports weak-linking for target binaries of # type ```` that are weakly-linked against a dependency target of # type ````. # # ```` # List of flags to add to the linker command to produce a working target # binary of type ```` that is weakly-linked against a dependency # target of type ````. # # ``HAS_DYNAMIC_LOOKUP__`` # Cached, global alias for ```` # # ``DYNAMIC_LOOKUP_FLAGS__`` # Cached, global alias for ```` # # # Private Functions # ^^^^^^^^^^^^^^^^^ # # The following private functions are defined: # # .. warning:: These functions are not part of the scikit-build API. They # exist purely as an implementation detail and may change from version # to version without notice, or even be removed. # # We mean it. # # # .. cmake:command:: _get_target_type # # :: # # _get_target_type( ) # # # Shorthand for querying an abbreviated version of the target type # of the given ````. # # ```` is set to: # # - "STATIC" for a STATIC_LIBRARY, # - "SHARED" for a SHARED_LIBRARY, # - "MODULE" for a MODULE_LIBRARY, # - and "EXE" for an EXECUTABLE. # # Defined variables: # # ```` # The abbreviated version of the ````'s type. # # # .. cmake:command:: _test_weak_link_project # # :: # # _test_weak_link_project( # # # ) # # # Attempt to compile and run a test project where a target of type # ```` is weakly-linked against a dependency of type ````: # # - ```` can be one of "STATIC", "SHARED", "MODULE", or "EXE". # - ```` can be one of "STATIC", "SHARED", or "MODULE". # # Defined variables: # # ```` # Whether the current C toolchain can produce a working target binary of type # ```` that is weakly-linked against a dependency target of type # ````. # # ```` # List of flags to add to the linker command to produce a working target # binary of type ```` that is weakly-linked against a dependency # target of type ````. # function(_get_target_type result_var target) set(target_type "SHARED_LIBRARY") if(TARGET ${target}) get_property(target_type TARGET ${target} PROPERTY TYPE) endif() set(result "STATIC") if(target_type STREQUAL "STATIC_LIBRARY") set(result "STATIC") endif() if(target_type STREQUAL "SHARED_LIBRARY") set(result "SHARED") endif() if(target_type STREQUAL "MODULE_LIBRARY") set(result "MODULE") endif() if(target_type STREQUAL "EXECUTABLE") set(result "EXE") endif() set(${result_var} ${result} PARENT_SCOPE) endfunction() function(_test_weak_link_project target_type lib_type can_weak_link_var project_name) set(gnu_ld_ignore "-Wl,--unresolved-symbols=ignore-all") set(osx_dynamic_lookup "-undefined dynamic_lookup") set(no_flag "") foreach(link_flag_spec gnu_ld_ignore osx_dynamic_lookup no_flag) set(link_flag "${${link_flag_spec}}") set(test_project_dir "${PROJECT_BINARY_DIR}/CMakeTmp") set(test_project_dir "${test_project_dir}/${project_name}") set(test_project_dir "${test_project_dir}/${link_flag_spec}") set(test_project_dir "${test_project_dir}/${target_type}") set(test_project_dir "${test_project_dir}/${lib_type}") set(test_project_src_dir "${test_project_dir}/src") set(test_project_bin_dir "${test_project_dir}/build") file(MAKE_DIRECTORY ${test_project_src_dir}) file(MAKE_DIRECTORY ${test_project_bin_dir}) set(mod_type "STATIC") set(link_mod_lib TRUE) set(link_exe_lib TRUE) set(link_exe_mod FALSE) if("${target_type}" STREQUAL "EXE") set(link_exe_lib FALSE) set(link_exe_mod TRUE) else() set(mod_type "${target_type}") endif() if("${mod_type}" STREQUAL "MODULE") set(link_mod_lib FALSE) endif() file(WRITE "${test_project_src_dir}/CMakeLists.txt" " cmake_minimum_required(VERSION ${CMAKE_VERSION}) project(${project_name} C) include_directories(${test_project_src_dir}) add_library(number ${lib_type} number.c) add_library(counter ${mod_type} counter.c) ") if("${mod_type}" STREQUAL "MODULE") file(APPEND "${test_project_src_dir}/CMakeLists.txt" " set_target_properties(counter PROPERTIES PREFIX \"\") ") endif() if(link_mod_lib) file(APPEND "${test_project_src_dir}/CMakeLists.txt" " target_link_libraries(counter number) ") elseif(NOT link_flag STREQUAL "") file(APPEND "${test_project_src_dir}/CMakeLists.txt" " set_target_properties(counter PROPERTIES LINK_FLAGS \"${link_flag}\") ") endif() file(APPEND "${test_project_src_dir}/CMakeLists.txt" " add_executable(main main.c) ") if(link_exe_lib) file(APPEND "${test_project_src_dir}/CMakeLists.txt" " target_link_libraries(main number) ") elseif(NOT link_flag STREQUAL "") file(APPEND "${test_project_src_dir}/CMakeLists.txt" " target_link_libraries(main \"${link_flag}\") ") endif() if(link_exe_mod) file(APPEND "${test_project_src_dir}/CMakeLists.txt" " target_link_libraries(main counter) ") else() file(APPEND "${test_project_src_dir}/CMakeLists.txt" " target_link_libraries(main \"${CMAKE_DL_LIBS}\") ") endif() file(WRITE "${test_project_src_dir}/number.c" " #include static int _number; void set_number(int number) { _number = number; } int get_number() { return _number; } ") file(WRITE "${test_project_src_dir}/number.h" " #ifndef _NUMBER_H #define _NUMBER_H extern void set_number(int); extern int get_number(void); #endif ") file(WRITE "${test_project_src_dir}/counter.c" " #include int count() { int result = get_number(); set_number(result + 1); return result; } ") file(WRITE "${test_project_src_dir}/counter.h" " #ifndef _COUNTER_H #define _COUNTER_H extern int count(void); #endif ") file(WRITE "${test_project_src_dir}/main.c" " #include #include #include ") if(NOT link_exe_mod) file(APPEND "${test_project_src_dir}/main.c" " #include ") endif() file(APPEND "${test_project_src_dir}/main.c" " int my_count() { int result = get_number(); set_number(result + 1); return result; } int main(int argc, char **argv) { int result; ") if(NOT link_exe_mod) file(APPEND "${test_project_src_dir}/main.c" " void *counter_module; int (*count)(void); counter_module = dlopen(\"./counter.so\", RTLD_LAZY | RTLD_GLOBAL); if(!counter_module) goto error; count = dlsym(counter_module, \"count\"); if(!count) goto error; ") endif() file(APPEND "${test_project_src_dir}/main.c" " result = count() != 0 ? EXIT_FAILURE : my_count() != 1 ? EXIT_FAILURE : my_count() != 2 ? EXIT_FAILURE : count() != 3 ? EXIT_FAILURE : count() != 4 ? EXIT_FAILURE : count() != 5 ? EXIT_FAILURE : my_count() != 6 ? EXIT_FAILURE : EXIT_SUCCESS; ") if(NOT link_exe_mod) file(APPEND "${test_project_src_dir}/main.c" " goto done; error: - fprintf(stderr, \"Error occured:\\n %s\\n\", dlerror()); + fprintf(stderr, \"Error occurred:\\n %s\\n\", dlerror()); result = 1; done: if(counter_module) dlclose(counter_module); ") endif() file(APPEND "${test_project_src_dir}/main.c" " return result; } ") set(_rpath_arg) if(APPLE AND ${CMAKE_VERSION} VERSION_GREATER 2.8.11) set(_rpath_arg "-DCMAKE_MACOSX_RPATH='${CMAKE_MACOSX_RPATH}'") endif() try_compile(project_compiles "${test_project_bin_dir}" "${test_project_src_dir}" "${project_name}" CMAKE_FLAGS "-DCMAKE_SHARED_LINKER_FLAGS='${CMAKE_SHARED_LINKER_FLAGS}'" "-DCMAKE_ENABLE_EXPORTS=ON" ${_rpath_arg} OUTPUT_VARIABLE compile_output) set(project_works 1) set(run_output) if(project_compiles) execute_process(COMMAND ${CMAKE_CROSSCOMPILING_EMULATOR} "${test_project_bin_dir}/main" WORKING_DIRECTORY "${test_project_bin_dir}" RESULT_VARIABLE project_works OUTPUT_VARIABLE run_output ERROR_VARIABLE run_output) endif() set(test_description "Weak Link ${target_type} -> ${lib_type} (${link_flag_spec})") if(project_works EQUAL 0) set(project_works TRUE) message(STATUS "Performing Test ${test_description} - Success") else() set(project_works FALSE) message(STATUS "Performing Test ${test_description} - Failed") file(APPEND ${CMAKE_BINARY_DIR}/${CMAKE_FILES_DIRECTORY}/CMakeError.log "Performing Test ${test_description} failed with the " "following output:\n" "BUILD\n-----\n${compile_output}\nRUN\n---\n${run_output}\n") endif() set(${can_weak_link_var} ${project_works} PARENT_SCOPE) if(project_works) set(${project_name} ${link_flag} PARENT_SCOPE) break() endif() endforeach() endfunction() function(mitk_check_dynamic_lookup) # Two signatures are supported: if(ARGC EQUAL "1") # # mitk_check_dynamic_lookup() # set(target_type "MODULE") set(lib_type "SHARED") set(has_dynamic_lookup_var "${ARGV0}") set(link_flags_var "unused") elseif(ARGC GREATER "2") # # mitk_check_dynamic_lookup( # # # []) # set(target_type "${ARGV0}") set(lib_type "${ARGV1}") set(has_dynamic_lookup_var "${ARGV2}") if(ARGC EQUAL "3") set(link_flags_var "unused") else() set(link_flags_var "${ARGV3}") endif() else() message(FATAL_ERROR "missing arguments") endif() _check_dynamic_lookup( ${target_type} ${lib_type} ${has_dynamic_lookup_var} ${link_flags_var} ) set(${has_dynamic_lookup_var} ${${has_dynamic_lookup_var}} PARENT_SCOPE) if(NOT "x${link_flags_var}x" MATCHES "^xunusedx$") set(${link_flags_var} ${${link_flags_var}} PARENT_SCOPE) endif() endfunction() function(_check_dynamic_lookup target_type lib_type has_dynamic_lookup_var link_flags_var ) # hash the CMAKE_FLAGS passed and check cache to know if we need to rerun if("${target_type}" STREQUAL "STATIC") string(MD5 cmake_flags_hash "${CMAKE_STATIC_LINKER_FLAGS}") else() string(MD5 cmake_flags_hash "${CMAKE_SHARED_LINKER_FLAGS}") endif() set(cache_var "HAS_DYNAMIC_LOOKUP_${target_type}_${lib_type}") set(cache_hash_var "HAS_DYNAMIC_LOOKUP_${target_type}_${lib_type}_hash") set(result_var "DYNAMIC_LOOKUP_FLAGS_${target_type}_${lib_type}") if( NOT DEFINED ${cache_hash_var} OR NOT "${${cache_hash_var}}" STREQUAL "${cmake_flags_hash}") unset(${cache_var} CACHE) endif() if(NOT DEFINED ${cache_var}) set(skip_test FALSE) if(CMAKE_CROSSCOMPILING AND NOT CMAKE_CROSSCOMPILING_EMULATOR) set(skip_test TRUE) endif() if(skip_test) set(has_dynamic_lookup FALSE) set(link_flags) else() _test_weak_link_project(${target_type} ${lib_type} has_dynamic_lookup link_flags) endif() set(caveat " (when linking ${target_type} against ${lib_type})") set(${cache_var} "${has_dynamic_lookup}" CACHE BOOL "linker supports dynamic lookup for undefined symbols${caveat}") mark_as_advanced(${cache_var}) set(${result_var} "${link_flags}" CACHE STRING "linker flags for dynamic lookup${caveat}") mark_as_advanced(${result_var}) set(${cache_hash_var} "${cmake_flags_hash}" CACHE INTERNAL "hashed flags for ${cache_var} check") endif() set(${has_dynamic_lookup_var} "${${cache_var}}" PARENT_SCOPE) set(${link_flags_var} "${${result_var}}" PARENT_SCOPE) endfunction() function(mitk_target_link_libraries_with_dynamic_lookup target) _get_target_type(target_type ${target}) set(link_props) set(link_items) set(link_libs) foreach(lib ${ARGN}) _get_target_type(lib_type ${lib}) mitk_check_dynamic_lookup(${target_type} ${lib_type} has_dynamic_lookup dynamic_lookup_flags) if(has_dynamic_lookup) if(dynamic_lookup_flags) if("${target_type}" STREQUAL "EXE") list(APPEND link_items "${dynamic_lookup_flags}") else() list(APPEND link_props "${dynamic_lookup_flags}") endif() endif() else() list(APPEND link_libs "${lib}") endif() endforeach() if(link_props) list(REMOVE_DUPLICATES link_props) endif() if(link_items) list(REMOVE_DUPLICATES link_items) endif() if(link_libs) list(REMOVE_DUPLICATES link_libs) endif() if(link_props) set_target_properties(${target} PROPERTIES LINK_FLAGS "${link_props}") endif() set(links "${link_items}" "${link_libs}") if(links) target_link_libraries(${target} "${links}") endif() endfunction() diff --git a/CMake/mitkTestDriverFiles.cmake.in b/CMake/mitkTestDriverFiles.cmake.in index c7538a7495..94174a6124 100644 --- a/CMake/mitkTestDriverFiles.cmake.in +++ b/CMake/mitkTestDriverFiles.cmake.in @@ -1,47 +1,47 @@ if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/files.cmake) include(${CMAKE_CURRENT_SOURCE_DIR}/files.cmake) endif() # Write a header file containing include directives and custom code # for the test driver. set(TESTDRIVER_EXTRA_INCLUDES ) list(APPEND MODULE_TEST_EXTRA_DRIVER_INCLUDE "mitkLogBackend.h") list(REMOVE_DUPLICATES MODULE_TEST_EXTRA_DRIVER_INCLUDE) foreach(_include ${MODULE_TEST_EXTRA_DRIVER_INCLUDE}) set(TESTDRIVER_EXTRA_INCLUDES "${TESTDRIVER_EXTRA_INCLUDES} #include <${_include}>") endforeach() set(TESTDRIVER_EXTRA_INCLUDES "${TESTDRIVER_EXTRA_INCLUDES} #include std::vector globalCmdLineArgs;") set(_extra_include_file ${CMAKE_CURRENT_BINARY_DIR}/${TESTDRIVER}_extras.h) configure_file(${MITK_CMAKE_DIR}/mitkTestDriverExtraIncludes.h.in ${_extra_include_file}) set(CMAKE_TESTDRIVER_BEFORE_TESTMAIN " for (int avIndex = 1; avIndex < ac; ++avIndex) globalCmdLineArgs.push_back(av[avIndex]); mitk::LogBackend::Register(); ${MODULE_TEST_EXTRA_DRIVER_INIT};" ) set(CMAKE_TESTDRIVER_AFTER_TESTMAIN "mitk::LogBackend::Unregister();") create_test_sourcelist(_test_cpp_files ${MODULE_NAME}_main.cpp ${MODULE_TESTS} ${MODULE_RENDERING_TESTS} ${MODULE_IMAGE_TESTS} ${MODULE_SURFACE_TESTS} ${MODULE_CUSTOM_TESTS} EXTRA_INCLUDE ${_extra_include_file} ) list(APPEND CPP_FILES ${_test_cpp_files}) # Some old CMake scripts use TEST_CPP_FILES in their files.cmake -# file of the test driver to add source fiels to the executable +# file of the test driver to add source files to the executable # (they should just use CPP_FILES like in any other files.cmake # file instead). if(TEST_CPP_FILES) list(APPEND CPP_FILES ${TEST_CPP_FILES}) endif() if(MODULE_RESOURCE_FILES) list(APPEND RESOURCE_FILES ${MODULE_RESOURCE_FILES}) endif() diff --git a/CMakeLists.txt b/CMakeLists.txt index 7070de702e..db6bc24f3d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,1413 +1,1413 @@ #[[ When increasing the minimum required version, check if Boost_ADDITIONAL_VERSIONS in CMake/PackageDepends/MITK_Boost_Config.cmake can be removed. See the first long comment in CMakeExternals/Boost.cmake for details. ]] set(MITK_CMAKE_MINIMUM_REQUIRED_VERSION 3.18) cmake_minimum_required(VERSION ${MITK_CMAKE_MINIMUM_REQUIRED_VERSION}) if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.19 AND CMAKE_VERSION VERSION_LESS 3.19.2) message(FATAL_ERROR "\ CMake v${CMAKE_VERSION} is defective [1]. \ Please either downgrade to v3.18 or upgrade to at least v3.19.2.\n\ [1] https://gitlab.kitware.com/cmake/cmake/-/issues/21529") endif() #----------------------------------------------------------------------------- # Policies #----------------------------------------------------------------------------- #[[ T28060 https://cmake.org/cmake/help/v3.18/policy/CMP0091.html https://cmake.org/cmake/help/v3.18/variable/CMAKE_MSVC_RUNTIME_LIBRARY.html We pass CMP0091 to all external projects as command-line argument: -DCMAKE_POLICY_DEFAULT_CMP0091:STRING=OLD ]] cmake_policy(SET CMP0091 OLD) if(POLICY CMP0135) cmake_policy(SET CMP0135 NEW) # https://cmake.org/cmake/help/v3.24/policy/CMP0135.html endif() #----------------------------------------------------------------------------- # Superbuild Option - Enabled by default #----------------------------------------------------------------------------- option(MITK_USE_SUPERBUILD "Build MITK and the projects it depends on via SuperBuild.cmake." ON) if(MITK_USE_SUPERBUILD) project(MITK-superbuild) set(MITK_SOURCE_DIR ${PROJECT_SOURCE_DIR}) set(MITK_BINARY_DIR ${PROJECT_BINARY_DIR}) else() project(MITK VERSION 2023.12.99) include_directories(SYSTEM ${MITK_SUPERBUILD_BINARY_DIR}) endif() #----------------------------------------------------------------------------- # MITK Extension Feature #----------------------------------------------------------------------------- set(MITK_EXTENSION_DIRS "" CACHE STRING "") unset(MITK_ABSOLUTE_EXTENSION_DIRS) foreach(MITK_EXTENSION_DIR ${MITK_EXTENSION_DIRS}) get_filename_component(MITK_ABSOLUTE_EXTENSION_DIR "${MITK_EXTENSION_DIR}" ABSOLUTE) list(APPEND MITK_ABSOLUTE_EXTENSION_DIRS "${MITK_ABSOLUTE_EXTENSION_DIR}") endforeach() set(MITK_DIR_PLUS_EXTENSION_DIRS "${MITK_SOURCE_DIR}" ${MITK_ABSOLUTE_EXTENSION_DIRS}) #----------------------------------------------------------------------------- # Update CMake module path #----------------------------------------------------------------------------- set(MITK_CMAKE_DIR ${MITK_SOURCE_DIR}/CMake) set(CMAKE_MODULE_PATH ${MITK_CMAKE_DIR}) foreach(MITK_EXTENSION_DIR ${MITK_ABSOLUTE_EXTENSION_DIRS}) set(MITK_CMAKE_EXTENSION_DIR "${MITK_EXTENSION_DIR}/CMake") if(EXISTS "${MITK_CMAKE_EXTENSION_DIR}") list(APPEND CMAKE_MODULE_PATH "${MITK_CMAKE_EXTENSION_DIR}") endif() endforeach() #----------------------------------------------------------------------------- # CMake function(s) and macro(s) #----------------------------------------------------------------------------- # Standard CMake macros include(FeatureSummary) include(CTest) include(CMakeParseArguments) include(FindPackageHandleStandardArgs) # MITK macros include(mitkFunctionGetGccVersion) include(mitkFunctionCheckCompilerFlags) include(mitkFunctionSuppressWarnings) # includes several functions include(mitkMacroEmptyExternalProject) include(mitkFunctionEnableBuildConfiguration) include(mitkFunctionWhitelists) include(mitkFunctionAddExternalProject) include(mitkFunctionAddLibrarySearchPaths) SUPPRESS_VC_DEPRECATED_WARNINGS() #----------------------------------------------------------------------------- # Set a default build type if none was specified #----------------------------------------------------------------------------- if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) message(STATUS "Setting build type to 'Debug' as none was specified.") set(CMAKE_BUILD_TYPE Debug CACHE STRING "Choose the type of build." FORCE) # Set the possible values of build type for cmake-gui set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo") endif() if(CMAKE_COMPILER_IS_GNUCXX) mitkFunctionGetGccVersion(${CMAKE_CXX_COMPILER} GCC_VERSION) else() set(GCC_VERSION 0) endif() set(MITK_CXX_STANDARD 17) set(CMAKE_CXX_EXTENSIONS 0) set(CMAKE_CXX_STANDARD ${MITK_CXX_STANDARD}) set(CMAKE_CXX_STANDARD_REQUIRED 1) # This is necessary to avoid problems with compile feature checks. # CMAKE_CXX_STANDARD seems to only set the -std=c++ flag for targets. # However, compile flag checks also need to be done with -std=c++. # The MITK_CXX_FLAG variable is also used for external projects # build during the MITK super-build. mitkFunctionCheckCompilerFlags("-std=c++${MITK_CXX_STANDARD}" MITK_CXX${MITK_CXX_STANDARD}_FLAG) #----------------------------------------------------------------------------- # Warn if source or build path is too long #----------------------------------------------------------------------------- if(WIN32) set(_src_dir_length_max 50) set(_bin_dir_length_max 50) if(MITK_USE_SUPERBUILD) set(_src_dir_length_max 34) # _src_dir_length_max - strlen(ep/src/ITK-build) set(_bin_dir_length_max 40) # _bin_dir_length_max - strlen(MITK-build) endif() string(LENGTH "${MITK_SOURCE_DIR}" _src_n) string(LENGTH "${MITK_BINARY_DIR}" _bin_n) # The warnings should be converted to errors if(_src_n GREATER _src_dir_length_max) message(WARNING "MITK source code directory path length is too long (${_src_n} > ${_src_dir_length_max})." "Please move the MITK source code directory to a directory with a shorter path." ) endif() if(_bin_n GREATER _bin_dir_length_max) message(WARNING "MITK build directory path length is too long (${_bin_n} > ${_bin_dir_length_max})." "Please move the MITK build directory to a directory with a shorter path." ) endif() endif() #----------------------------------------------------------------------------- # Additional MITK Options (also shown during superbuild) #----------------------------------------------------------------------------- # ----------------------------------------- # General build options option(BUILD_SHARED_LIBS "Build MITK with shared libraries" ON) option(WITH_COVERAGE "Enable/Disable coverage" OFF) option(BUILD_TESTING "Test the project" ON) option(MITK_FAST_TESTING "Disable long-running tests like packaging" OFF) option(MITK_XVFB_TESTING "Execute test drivers through xvfb-run" OFF) option(MITK_BUILD_ALL_APPS "Build all MITK applications" OFF) option(MITK_BUILD_EXAMPLES "Build the MITK Examples" OFF) mark_as_advanced( MITK_XVFB_TESTING MITK_FAST_TESTING MITK_BUILD_ALL_APPS ) #----------------------------------------------------------------------------- # Set UI testing flags #----------------------------------------------------------------------------- if(MITK_XVFB_TESTING) set(MITK_XVFB_TESTING_COMMAND "xvfb-run" "--auto-servernum" CACHE STRING "Command and options to test through Xvfb") mark_as_advanced(MITK_XVFB_TESTING_COMMAND) endif(MITK_XVFB_TESTING) # ----------------------------------------- # Other options set(MITK_CUSTOM_REVISION_DESC "" CACHE STRING "Override MITK revision description") mark_as_advanced(MITK_CUSTOM_REVISION_DESC) set_property(GLOBAL PROPERTY MITK_EXTERNAL_PROJECTS "") include(CMakeExternals/ExternalProjectList.cmake) foreach(MITK_EXTENSION_DIR ${MITK_ABSOLUTE_EXTENSION_DIRS}) set(MITK_CMAKE_EXTERNALS_EXTENSION_DIR "${MITK_EXTENSION_DIR}/CMakeExternals") if(EXISTS "${MITK_CMAKE_EXTERNALS_EXTENSION_DIR}/ExternalProjectList.cmake") include("${MITK_CMAKE_EXTERNALS_EXTENSION_DIR}/ExternalProjectList.cmake") endif() endforeach() # ----------------------------------------- # Other MITK_USE_* options not related to # external projects build via the # MITK superbuild option(MITK_USE_BLUEBERRY "Build the BlueBerry platform" ON) option(MITK_USE_OpenMP "Use OpenMP" OFF) option(MITK_USE_Python3 "Use Python 3" OFF) #----------------------------------------------------------------------------- # Build configurations #----------------------------------------------------------------------------- set(_buildConfigs "Custom") file(GLOB _buildConfigFiles CMake/BuildConfigurations/*.cmake) foreach(_buildConfigFile ${_buildConfigFiles}) get_filename_component(_buildConfigFile ${_buildConfigFile} NAME_WE) list(APPEND _buildConfigs ${_buildConfigFile}) endforeach() foreach(MITK_EXTENSION_DIR ${MITK_ABSOLUTE_EXTENSION_DIRS}) file(GLOB _extBuildConfigFiles "${MITK_EXTENSION_DIR}/CMake/BuildConfigurations/*.cmake") foreach(_extBuildConfigFile ${_extBuildConfigFiles}) get_filename_component(_extBuildConfigFile "${_extBuildConfigFile}" NAME_WE) list(APPEND _buildConfigs "${_extBuildConfigFile}") endforeach() list(REMOVE_DUPLICATES _buildConfigs) endforeach() set(MITK_BUILD_CONFIGURATION "WorkbenchRelease" CACHE STRING "Use pre-defined MITK configurations") set_property(CACHE MITK_BUILD_CONFIGURATION PROPERTY STRINGS ${_buildConfigs}) mitkFunctionEnableBuildConfiguration() mitkFunctionCreateWhitelistPaths(MITK) mitkFunctionFindWhitelists(MITK) # ----------------------------------------- # Qt version related variables option(MITK_USE_Qt6 "Use Qt 6 library" ON) if(MITK_USE_Qt6) set(MITK_QT6_MINIMUM_VERSION 6.6) set(MITK_QT6_COMPONENTS Concurrent Core Core5Compat CoreTools Designer DesignerComponentsPrivate Gui Help LinguistTools Network OpenGL OpenGLWidgets Qml Sql StateMachine Svg ToolsTools UiTools WebEngineCore WebEngineWidgets Widgets Xml ) if(APPLE) list(APPEND MITK_QT6_COMPONENTS DBus) endif() # Hint at default install locations of Qt if(NOT Qt6_DIR) if(MSVC) set(_dir_candidates "C:/Qt") if(CMAKE_GENERATOR MATCHES "^Visual Studio [0-9]+ ([0-9]+)") set(_compilers "msvc${CMAKE_MATCH_1}") elseif(CMAKE_GENERATOR MATCHES "Ninja") include(mitkFunctionGetMSVCVersion) mitkFunctionGetMSVCVersion() if(VISUAL_STUDIO_PRODUCT_NAME MATCHES "^Visual Studio ([0-9]+)") set(_compilers "msvc${CMAKE_MATCH_1}") endif() endif() if(_compilers MATCHES "[0-9]+") if (CMAKE_MATCH_0 EQUAL 2022) list(APPEND _compilers "msvc2019") # Binary compatible endif() endif() else() set(_dir_candidates ~/Qt) if(APPLE) set(_compilers clang) else() list(APPEND _dir_candidates /opt/Qt) set(_compilers gcc) endif() endif() if(CMAKE_SIZEOF_VOID_P EQUAL 8) foreach(_compiler ${_compilers}) list(APPEND _compilers64 "${_compiler}_64") endforeach() set(_compilers ${_compilers64}) endif() if(APPLE) list(APPEND _compilers macos) endif() foreach(_dir_candidate ${_dir_candidates}) get_filename_component(_dir_candidate ${_dir_candidate} REALPATH) foreach(_compiler ${_compilers}) set(_glob_expression "${_dir_candidate}/6.*/${_compiler}") file(GLOB _hints ${_glob_expression}) list(SORT _hints) list(APPEND MITK_QT6_HINTS ${_hints}) endforeach() endforeach() endif() find_package(Qt6 ${MITK_QT6_MINIMUM_VERSION} COMPONENTS ${MITK_QT6_COMPONENTS} REQUIRED HINTS ${MITK_QT6_HINTS}) get_target_property(QT_QMAKE_EXECUTABLE Qt6::qmake LOCATION) get_target_property(QT_HELPGENERATOR_EXECUTABLE Qt6::qhelpgenerator LOCATION) endif() if(Qt6_DIR) list(APPEND CMAKE_PREFIX_PATH "${Qt6_DIR}/../../..") list(REMOVE_DUPLICATES CMAKE_PREFIX_PATH) endif() # ----------------------------------------- # Custom dependency logic if(WIN32 AND Qt6_DIR) set(_dir_candidate "${Qt6_DIR}/../../../../../Tools/OpenSSLv3/Win_x64") get_filename_component(_dir_candidate ${_dir_candidate} ABSOLUTE) if(EXISTS "${_dir_candidate}") set(OPENSSL_ROOT_DIR "${_dir_candidate}") endif() endif() find_package(OpenSSL 3) if(NOT OpenSSL_FOUND) find_package(OpenSSL 1.1.1) endif() option(MITK_USE_SYSTEM_Boost "Use the system Boost" OFF) set(MITK_USE_Boost_LIBRARIES "" CACHE STRING "A semi-colon separated list of required Boost libraries") if(MITK_USE_cpprestsdk) if(NOT OpenSSL_FOUND) set(openssl_message "Could not find OpenSSL (dependency of C++ REST SDK).\n") if(UNIX) if(APPLE) set(openssl_message "${openssl_message}Please install it using your favorite package management " "system (i.e. Homebrew or MacPorts).\n") else() set(openssl_message "${openssl_message}Please install the dev package of OpenSSL (i.e. libssl-dev).\n") endif() else() set(openssl_message "${openssl_message}Please either install Win32 OpenSSL:\n" " https://slproweb.com/products/Win32OpenSSL.html\n" "Or use the Qt Maintenance tool to install:\n" " Developer and Designer Tools > OpenSSL Toolkit > OpenSSL 64-bit binaries\n") endif() set(openssl_message "${openssl_message}If it still cannot be found, you can hint CMake to find OpenSSL by " "adding/setting the OPENSSL_ROOT_DIR variable to the root directory of an " "OpenSSL installation. Make sure to clear variables of partly found " "versions of OpenSSL before, or they will be mixed up.") message(FATAL_ERROR ${openssl_message}) endif() list(APPEND MITK_USE_Boost_LIBRARIES date_time regex system) if(UNIX) list(APPEND MITK_USE_Boost_LIBRARIES atomic chrono filesystem random thread) endif() list(REMOVE_DUPLICATES MITK_USE_Boost_LIBRARIES) set(MITK_USE_Boost_LIBRARIES ${MITK_USE_Boost_LIBRARIES} CACHE STRING "A semi-colon separated list of required Boost libraries" FORCE) endif() if(MITK_USE_Python3) set(MITK_USE_ZLIB ON CACHE BOOL "" FORCE) if(APPLE) set(python3_mininum_version 3.11) else() set(python3_mininum_version 3.8) endif() find_package(Python3 ${python3_mininum_version} REQUIRED COMPONENTS Interpreter Development NumPy) if(WIN32) string(REPLACE "\\" "/" Python3_STDARCH "${Python3_STDARCH}") string(REPLACE "\\" "/" Python3_STDLIB "${Python3_STDLIB}") string(REPLACE "\\" "/" Python3_SITELIB "${Python3_SITELIB}") endif() endif() if(BUILD_TESTING AND NOT MITK_USE_CppUnit) message("> Forcing MITK_USE_CppUnit to ON because BUILD_TESTING=ON") set(MITK_USE_CppUnit ON CACHE BOOL "Use CppUnit for unit tests" FORCE) endif() if(MITK_USE_BLUEBERRY) option(MITK_BUILD_ALL_PLUGINS "Build all MITK plugins" OFF) mark_as_advanced(MITK_BUILD_ALL_PLUGINS) if(NOT MITK_USE_CTK) message("> Forcing MITK_USE_CTK to ON because of MITK_USE_BLUEBERRY") set(MITK_USE_CTK ON CACHE BOOL "Use CTK in MITK" FORCE) endif() endif() #----------------------------------------------------------------------------- # Pixel type multiplexing #----------------------------------------------------------------------------- # Customize the default pixel types for multiplex macros set(MITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES "int, unsigned int, short, unsigned short, char, unsigned char" CACHE STRING "List of integral pixel types used in AccessByItk and InstantiateAccessFunction macros") set(MITK_ACCESSBYITK_FLOATING_PIXEL_TYPES "double, float" CACHE STRING "List of floating pixel types used in AccessByItk and InstantiateAccessFunction macros") set(MITK_ACCESSBYITK_COMPOSITE_PIXEL_TYPES "itk::RGBPixel, itk::RGBAPixel" CACHE STRING "List of composite pixel types used in AccessByItk and InstantiateAccessFunction macros") set(MITK_ACCESSBYITK_DIMENSIONS "2,3" CACHE STRING "List of dimensions used in AccessByItk and InstantiateAccessFunction macros") mark_as_advanced(MITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES MITK_ACCESSBYITK_FLOATING_PIXEL_TYPES MITK_ACCESSBYITK_COMPOSITE_PIXEL_TYPES MITK_ACCESSBYITK_DIMENSIONS ) # consistency checks if(NOT MITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES) set(MITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES "int, unsigned int, short, unsigned short, char, unsigned char" CACHE STRING "List of integral pixel types used in AccessByItk and InstantiateAccessFunction macros" FORCE) endif() if(NOT MITK_ACCESSBYITK_FLOATING_PIXEL_TYPES) set(MITK_ACCESSBYITK_FLOATING_PIXEL_TYPES "double, float" CACHE STRING "List of floating pixel types used in AccessByItk and InstantiateAccessFunction macros" FORCE) endif() if(NOT MITK_ACCESSBYITK_COMPOSITE_PIXEL_TYPES) set(MITK_ACCESSBYITK_COMPOSITE_PIXEL_TYPES "itk::RGBPixel, itk::RGBAPixel" CACHE STRING "List of composite pixel types used in AccessByItk and InstantiateAccessFunction macros" FORCE) endif() if(NOT MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES) string(REPLACE "," ";" _integral_types ${MITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES}) string(REPLACE "," ";" _floating_types ${MITK_ACCESSBYITK_FLOATING_PIXEL_TYPES}) foreach(_scalar_type ${_integral_types} ${_floating_types}) set(MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES "${MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES}itk::VariableLengthVector<${_scalar_type}>,") endforeach() string(LENGTH "${MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES}" _length) math(EXPR _length "${_length} - 1") string(SUBSTRING "${MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES}" 0 ${_length} MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES) set(MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES ${MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES} CACHE STRING "List of vector pixel types used in AccessByItk and InstantiateAccessFunction macros for itk::VectorImage types" FORCE) endif() if(NOT MITK_ACCESSBYITK_DIMENSIONS) set(MITK_ACCESSBYITK_DIMENSIONS "2,3" CACHE STRING "List of dimensions used in AccessByItk and InstantiateAccessFunction macros") endif() find_package(Git REQUIRED) #----------------------------------------------------------------------------- # Superbuild script #----------------------------------------------------------------------------- if(MITK_USE_SUPERBUILD) include("${CMAKE_CURRENT_SOURCE_DIR}/SuperBuild.cmake") # Print configuration summary message("\n\n") feature_summary( DESCRIPTION "------- FEATURE SUMMARY FOR ${PROJECT_NAME} -------" WHAT ALL) return() endif() #***************************************************************************** #**************************** END OF SUPERBUILD **************************** #***************************************************************************** #----------------------------------------------------------------------------- # Organize MITK targets in folders #----------------------------------------------------------------------------- set_property(GLOBAL PROPERTY USE_FOLDERS ON) set(MITK_ROOT_FOLDER "MITK" CACHE STRING "") mark_as_advanced(MITK_ROOT_FOLDER) #----------------------------------------------------------------------------- # CMake function(s) and macro(s) #----------------------------------------------------------------------------- include(WriteBasicConfigVersionFile) include(CheckCXXSourceCompiles) include(GenerateExportHeader) include(mitkFunctionAddManifest) include(mitkFunctionAddCustomModuleTest) include(mitkFunctionCheckModuleDependencies) include(mitkFunctionCompileSnippets) include(mitkFunctionConfigureVisualStudioUserProjectFile) include(mitkFunctionCreateBlueBerryApplication) include(mitkFunctionCreateCommandLineApp) include(mitkFunctionCreateModule) include(mitkFunctionCreatePlugin) include(mitkFunctionCreateProvisioningFile) include(mitkFunctionGetLibrarySearchPaths) include(mitkFunctionGetVersion) include(mitkFunctionGetVersionDescription) include(mitkFunctionInstallAutoLoadModules) include(mitkFunctionInstallCTKPlugin) include(mitkFunctionInstallProvisioningFiles) include(mitkFunctionInstallThirdPartyCTKPlugins) include(mitkFunctionOrganizeSources) include(mitkFunctionUseModules) if( ${MITK_USE_MatchPoint} ) include(mitkFunctionCreateMatchPointDeployedAlgorithm) endif() include(mitkMacroConfigureItkPixelTypes) include(mitkMacroCreateExecutable) include(mitkMacroCreateModuleTests) include(mitkMacroGenerateToolsLibrary) include(mitkMacroGetLinuxDistribution) include(mitkMacroGetPMDPlatformString) include(mitkMacroInstall) include(mitkMacroInstallHelperApp) include(mitkMacroInstallTargets) include(mitkMacroMultiplexPicType) #----------------------------------------------------------------------------- # Global CMake variables #----------------------------------------------------------------------------- if(NOT DEFINED CMAKE_DEBUG_POSTFIX) # We can't do this yet because the CTK Plugin Framework # cannot cope with a postfix yet. #set(CMAKE_DEBUG_POSTFIX d) endif() #----------------------------------------------------------------------------- # Output directories. #----------------------------------------------------------------------------- set(_default_LIBRARY_output_dir lib) set(_default_RUNTIME_output_dir bin) set(_default_ARCHIVE_output_dir lib) foreach(type LIBRARY RUNTIME ARCHIVE) # Make sure the directory exists if(MITK_CMAKE_${type}_OUTPUT_DIRECTORY AND NOT EXISTS ${MITK_CMAKE_${type}_OUTPUT_DIRECTORY}) message("Creating directory MITK_CMAKE_${type}_OUTPUT_DIRECTORY: ${MITK_CMAKE_${type}_OUTPUT_DIRECTORY}") file(MAKE_DIRECTORY "${MITK_CMAKE_${type}_OUTPUT_DIRECTORY}") endif() if(MITK_CMAKE_${type}_OUTPUT_DIRECTORY) set(CMAKE_${type}_OUTPUT_DIRECTORY ${MITK_CMAKE_${type}_OUTPUT_DIRECTORY}) else() set(CMAKE_${type}_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/${_default_${type}_output_dir}) set(MITK_CMAKE_${type}_OUTPUT_DIRECTORY ${CMAKE_${type}_OUTPUT_DIRECTORY}) endif() set(CMAKE_${type}_OUTPUT_DIRECTORY ${CMAKE_${type}_OUTPUT_DIRECTORY} CACHE INTERNAL "Output directory for ${type} files.") mark_as_advanced(CMAKE_${type}_OUTPUT_DIRECTORY) endforeach() #----------------------------------------------------------------------------- # Set MITK specific options and variables (NOT available during superbuild) #----------------------------------------------------------------------------- if(OpenSSL_FOUND AND WIN32) #[[ On Windows, CMake is able to locate the link libraries for OpenSSL but it does not look for the corresponding DLLs that we need to copy to our binary directories and include in packaging. Setting these paths manually is cumbersome so we try to use a simple heuristic to automatically set them: - Based on the link libraries (usually located in a lib folder), try to find the "../bin" binary directory. - Use the base file names of the link libraries to find corresponding DLLs like "*.dll", that usually are named like "-1_1-x64.dll" or similar. ]] set(openssl_ssl_dll "") set(openssl_crypto_dll "") if(OPENSSL_SSL_LIBRARY AND EXISTS "${OPENSSL_SSL_LIBRARY}") get_filename_component(openssl_bin_dir "${OPENSSL_SSL_LIBRARY}" DIRECTORY) get_filename_component(openssl_bin_dir "${openssl_bin_dir}" DIRECTORY) set(openssl_bin_dir "${openssl_bin_dir}/bin") if(EXISTS "${openssl_bin_dir}") get_filename_component(openssl_ssl_basename "${OPENSSL_SSL_LIBRARY}" NAME_WE) file(GLOB openssl_ssl_dll "${openssl_bin_dir}/${openssl_ssl_basename}*.dll") list(LENGTH openssl_ssl_dll num_findings) if(num_findings GREATER 1) set(openssl_ssl_dll "") endif() get_filename_component(openssl_crypto_basename "${OPENSSL_CRYPTO_LIBRARY}" NAME_WE) file(GLOB openssl_crypto_dll "${openssl_bin_dir}/${openssl_crypto_basename}*.dll") list(LENGTH openssl_crypto_dll num_findings) if(num_findings GREATER 1) set(openssl_crypto_dll "") endif() endif() endif() set(MITK_OPENSSL_SSL_DLL "${openssl_ssl_dll}" CACHE FILEPATH "") if(DEFINED CACHE{MITK_OPENSSL_SSL_DLL} AND NOT MITK_OPENSSL_SSL_DLL AND openssl_ssl_dll) set(MITK_OPENSSL_SSL_DLL "${openssl_ssl_dll}" CACHE FILEPATH "" FORCE) endif() set(MITK_OPENSSL_CRYPTO_DLL "${openssl_crypto_dll}" CACHE FILEPATH "") if(DEFINED CACHE{MITK_OPENSSL_CRYPTO_DLL} AND NOT MITK_OPENSSL_CRYPTO_DLL AND openssl_crypto_dll) set(MITK_OPENSSL_CRYPTO_DLL "${openssl_crypto_dll}" CACHE FILEPATH "" FORCE) endif() if(MITK_OPENSSL_SSL_DLL AND EXISTS "${MITK_OPENSSL_SSL_DLL}" AND MITK_OPENSSL_CRYPTO_DLL AND EXISTS "${MITK_OPENSSL_CRYPTO_DLL}") foreach(config_type ${CMAKE_CONFIGURATION_TYPES}) execute_process(COMMAND "${CMAKE_COMMAND}" -E make_directory "${MITK_BINARY_DIR}/bin/${config_type}") configure_file("${MITK_OPENSSL_SSL_DLL}" "${MITK_BINARY_DIR}/bin/${config_type}/" COPYONLY) configure_file("${MITK_OPENSSL_CRYPTO_DLL}" "${MITK_BINARY_DIR}/bin/${config_type}/" COPYONLY) endforeach() MITK_INSTALL(FILES "${MITK_OPENSSL_SSL_DLL}" "${MITK_OPENSSL_CRYPTO_DLL}" ) endif() endif() # Look for optional Doxygen package find_package(Doxygen) option(BLUEBERRY_DEBUG_SMARTPOINTER "Enable code for debugging smart pointers" OFF) mark_as_advanced(BLUEBERRY_DEBUG_SMARTPOINTER) # Ask the user to show the console window for applications option(MITK_SHOW_CONSOLE_WINDOW "Use this to enable or disable the console window when starting MITK GUI Applications" ON) mark_as_advanced(MITK_SHOW_CONSOLE_WINDOW) if(NOT MITK_FAST_TESTING) if(MITK_CTEST_SCRIPT_MODE STREQUAL "Continuous" OR MITK_CTEST_SCRIPT_MODE STREQUAL "Experimental") set(MITK_FAST_TESTING ON) endif() endif() if(NOT UNIX) set(MITK_WIN32_FORCE_STATIC "STATIC" CACHE INTERNAL "Use this variable to always build static libraries on non-unix platforms") endif() if(MITK_BUILD_ALL_PLUGINS) set(MITK_BUILD_ALL_PLUGINS_OPTION "FORCE_BUILD_ALL") endif() # Configure pixel types used for ITK image access multiplexing mitkMacroConfigureItkPixelTypes() # Configure module naming conventions set(MITK_MODULE_NAME_REGEX_MATCH "^[A-Z].*$") set(MITK_MODULE_NAME_REGEX_NOT_MATCH "^[Mm][Ii][Tt][Kk].*$") set(MITK_DEFAULT_MODULE_NAME_PREFIX "Mitk") set(MITK_MODULE_NAME_PREFIX ${MITK_DEFAULT_MODULE_NAME_PREFIX}) set(MITK_MODULE_NAME_DEFAULTS_TO_DIRECTORY_NAME 1) #----------------------------------------------------------------------------- # Get MITK version info #----------------------------------------------------------------------------- mitkFunctionGetVersion(${MITK_SOURCE_DIR} MITK) mitkFunctionGetVersionDescription(${MITK_SOURCE_DIR} MITK) # MITK_VERSION set(MITK_VERSION_STRING "${MITK_VERSION_MAJOR}.${MITK_VERSION_MINOR}.${MITK_VERSION_PATCH}") if(MITK_VERSION_PATCH STREQUAL "99") set(MITK_VERSION_STRING "${MITK_VERSION_STRING}-${MITK_REVISION_SHORTID}") endif() #----------------------------------------------------------------------------- # Installation preparation # # These should be set before any MITK install macros are used #----------------------------------------------------------------------------- # on macOS all BlueBerry plugins get copied into every # application bundle (.app directory) specified here if(MITK_USE_BLUEBERRY AND APPLE) foreach(MITK_EXTENSION_DIR ${MITK_DIR_PLUS_EXTENSION_DIRS}) set(MITK_APPLICATIONS_EXTENSION_DIR "${MITK_EXTENSION_DIR}/Applications") if(EXISTS "${MITK_APPLICATIONS_EXTENSION_DIR}/AppList.cmake") set(MITK_APPS "") include("${MITK_APPLICATIONS_EXTENSION_DIR}/AppList.cmake") foreach(mitk_app ${MITK_APPS}) # extract option_name string(REPLACE "^^" "\\;" target_info ${mitk_app}) set(target_info_list ${target_info}) list(GET target_info_list 1 option_name) list(GET target_info_list 0 app_name) # check if the application is enabled if(${option_name} OR MITK_BUILD_ALL_APPS) set(MACOSX_BUNDLE_NAMES ${MACOSX_BUNDLE_NAMES} Mitk${app_name}) endif() endforeach() endif() endforeach() endif() #----------------------------------------------------------------------------- # Set coverage Flags #----------------------------------------------------------------------------- if(WITH_COVERAGE) if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") set(coverage_flags "-g -fprofile-arcs -ftest-coverage -O0 -DNDEBUG") set(COVERAGE_CXX_FLAGS ${coverage_flags}) set(COVERAGE_C_FLAGS ${coverage_flags}) endif() endif() #----------------------------------------------------------------------------- # MITK C/CXX Flags #----------------------------------------------------------------------------- set(MITK_C_FLAGS "${COVERAGE_C_FLAGS}") set(MITK_C_FLAGS_DEBUG ) set(MITK_C_FLAGS_RELEASE ) set(MITK_CXX_FLAGS "${COVERAGE_CXX_FLAGS} ${MITK_CXX${MITK_CXX_STANDARD}_FLAG}") set(MITK_CXX_FLAGS_DEBUG ) set(MITK_CXX_FLAGS_RELEASE ) set(MITK_EXE_LINKER_FLAGS ) set(MITK_SHARED_LINKER_FLAGS ) if(WIN32) set(MITK_CXX_FLAGS "${MITK_CXX_FLAGS} -DWIN32_LEAN_AND_MEAN -DNOMINMAX") mitkFunctionCheckCompilerFlags("/wd4005" MITK_CXX_FLAGS) # warning C4005: macro redefinition mitkFunctionCheckCompilerFlags("/wd4231" MITK_CXX_FLAGS) # warning C4231: nonstandard extension used : 'extern' before template explicit instantiation # the following line should be removed after fixing bug 17637 mitkFunctionCheckCompilerFlags("/wd4316" MITK_CXX_FLAGS) # warning C4316: object alignment on heap mitkFunctionCheckCompilerFlags("/wd4180" MITK_CXX_FLAGS) # warning C4180: qualifier applied to function type has no meaning mitkFunctionCheckCompilerFlags("/wd4251" MITK_CXX_FLAGS) # warning C4251: 'identifier' : class 'type' needs to have dll-interface to be used by clients of class 'type2' endif() if(APPLE) set(MITK_CXX_FLAGS "${MITK_CXX_FLAGS} -DGL_SILENCE_DEPRECATION") # Apple deprecated OpenGL in macOS 10.14 endif() if(NOT MSVC_VERSION) foreach(_flag -Wall -Wextra -Wpointer-arith -Winvalid-pch -Wcast-align -Wwrite-strings -Wno-error=gnu -Wno-error=unknown-pragmas # The strict-overflow warning is generated by ITK template code -Wno-error=strict-overflow -Woverloaded-virtual -Wstrict-null-sentinel #-Wold-style-cast #-Wsign-promo -Wno-deprecated-copy -Wno-array-bounds -Wno-cast-function-type -Wno-maybe-uninitialized -Wno-error=stringop-overread -fdiagnostics-show-option ) mitkFunctionCheckCAndCXXCompilerFlags(${_flag} MITK_C_FLAGS MITK_CXX_FLAGS) endforeach() endif() if(CMAKE_COMPILER_IS_GNUCXX AND NOT APPLE) mitkFunctionCheckCompilerFlags("-Wl,--no-undefined" MITK_SHARED_LINKER_FLAGS) mitkFunctionCheckCompilerFlags("-Wl,--as-needed" MITK_SHARED_LINKER_FLAGS) endif() if(CMAKE_COMPILER_IS_GNUCXX) mitkFunctionCheckCAndCXXCompilerFlags("-fstack-protector-all" MITK_C_FLAGS MITK_CXX_FLAGS) set(MITK_CXX_FLAGS_RELEASE "-U_FORTIFY_SOURCES -D_FORTIFY_SOURCE=2 ${MITK_CXX_FLAGS_RELEASE}") endif() set(MITK_MODULE_LINKER_FLAGS ${MITK_SHARED_LINKER_FLAGS}) set(MITK_EXE_LINKER_FLAGS ${MITK_SHARED_LINKER_FLAGS}) #----------------------------------------------------------------------------- # MITK Packages #----------------------------------------------------------------------------- set(MITK_MODULES_PACKAGE_DEPENDS_DIR ${MITK_SOURCE_DIR}/CMake/PackageDepends) set(MODULES_PACKAGE_DEPENDS_DIRS ${MITK_MODULES_PACKAGE_DEPENDS_DIR}) foreach(MITK_EXTENSION_DIR ${MITK_ABSOLUTE_EXTENSION_DIRS}) set(MITK_PACKAGE_DEPENDS_EXTENSION_DIR "${MITK_EXTENSION_DIR}/CMake/PackageDepends") if(EXISTS "${MITK_PACKAGE_DEPENDS_EXTENSION_DIR}") list(APPEND MODULES_PACKAGE_DEPENDS_DIRS "${MITK_PACKAGE_DEPENDS_EXTENSION_DIR}") endif() endforeach() if(NOT MITK_USE_SYSTEM_Boost) set(Boost_NO_SYSTEM_PATHS 1) endif() set(Boost_USE_MULTITHREADED 1) set(Boost_USE_STATIC_LIBS 0) set(Boost_USE_STATIC_RUNTIME 0) set(Boost_ADDITIONAL_VERSIONS 1.74 1.74.0) # We need this later for a DCMTK workaround set(_dcmtk_dir_orig ${DCMTK_DIR}) # This property is populated at the top half of this file get_property(MITK_EXTERNAL_PROJECTS GLOBAL PROPERTY MITK_EXTERNAL_PROJECTS) foreach(ep ${MITK_EXTERNAL_PROJECTS}) get_property(_package GLOBAL PROPERTY MITK_${ep}_PACKAGE) get_property(_components GLOBAL PROPERTY MITK_${ep}_COMPONENTS) if(MITK_USE_${ep} AND _package) if(_components) find_package(${_package} COMPONENTS ${_components} REQUIRED CONFIG) else() # Prefer config mode first because it finds external # Config.cmake files pointed at by _DIR variables. # Otherwise, existing Find.cmake files could fail. if(DEFINED ${_package}_DIR) #we store the information because it will be overwritten by find_package #and would get lost for all EPs that use on Find.cmake instead of config #files. set(_temp_EP_${_package}_dir ${${_package}_DIR}) endif(DEFINED ${_package}_DIR) find_package(${_package} QUIET CONFIG) string(TOUPPER "${_package}" _package_uc) if(NOT (${_package}_FOUND OR ${_package_uc}_FOUND)) if(DEFINED _temp_EP_${_package}_dir) - set(${_package}_DIR ${_temp_EP_${_package}_dir} CACHE PATH "externaly set dir of the package ${_package}" FORCE) + set(${_package}_DIR ${_temp_EP_${_package}_dir} CACHE PATH "externally set dir of the package ${_package}" FORCE) endif(DEFINED _temp_EP_${_package}_dir) find_package(${_package} REQUIRED) endif() endif() endif() endforeach() # Ensure that the MITK CMake module path comes first set(CMAKE_MODULE_PATH ${MITK_CMAKE_DIR} ${CMAKE_MODULE_PATH} ) if(MITK_USE_DCMTK) if(${_dcmtk_dir_orig} MATCHES "${MITK_EXTERNAL_PROJECT_PREFIX}.*") # Help our FindDCMTK.cmake script find our super-build DCMTK set(DCMTK_DIR ${MITK_EXTERNAL_PROJECT_PREFIX}) else() # Use the original value set(DCMTK_DIR ${_dcmtk_dir_orig}) endif() endif() if(MITK_USE_DCMQI) # Due to the preferred CONFIG mode in find_package calls above, # the DCMQIConfig.cmake file is read, which does not provide useful - # package information. We explictly need MODULE mode to find DCMQI. + # package information. We explicitly need MODULE mode to find DCMQI. # Help our FindDCMQI.cmake script find our super-build DCMQI set(DCMQI_DIR ${MITK_EXTERNAL_PROJECT_PREFIX}) find_package(DCMQI REQUIRED) endif() if(MITK_USE_OpenMP) find_package(OpenMP REQUIRED COMPONENTS CXX) else() find_package(OpenMP QUIET COMPONENTS CXX) if(OpenMP_FOUND) set(MITK_USE_OpenMP ON CACHE BOOL "" FORCE) elseif(APPLE AND OpenMP_libomp_LIBRARY AND NOT OpenMP_CXX_LIB_NAMES) set(OpenMP_CXX_LIB_NAMES libomp CACHE STRING "" FORCE) get_filename_component(openmp_lib_dir "${OpenMP_libomp_LIBRARY}" DIRECTORY) set(openmp_include_dir "${openmp_lib_dir}/../include") if(EXISTS "${openmp_include_dir}") get_filename_component(openmp_include_dir "${openmp_include_dir}" REALPATH) set(OpenMP_CXX_FLAGS "-Xpreprocessor -fopenmp -I${openmp_include_dir}" CACHE STRING "" FORCE) find_package(OpenMP QUIET COMPONENTS CXX) if(OpenMP_FOUND) set(MITK_USE_OpenMP ON CACHE BOOL "" FORCE) endif() endif() endif() endif() # Qt support if(MITK_USE_Qt6) if(MITK_USE_BLUEBERRY) option(BLUEBERRY_USE_QT_HELP "Enable support for integrating plugin documentation into Qt Help" ${DOXYGEN_FOUND}) mark_as_advanced(BLUEBERRY_USE_QT_HELP) # Sanity checks for in-application BlueBerry plug-in help generation if(BLUEBERRY_USE_QT_HELP) set(_force_blueberry_use_qt_help_to_off 0) if(NOT DOXYGEN_FOUND) message("> Forcing BLUEBERRY_USE_QT_HELP to OFF because Doxygen was not found.") set(_force_blueberry_use_qt_help_to_off 1) endif() if(DOXYGEN_FOUND AND DOXYGEN_VERSION VERSION_LESS 1.8.7) message("> Forcing BLUEBERRY_USE_QT_HELP to OFF because Doxygen version 1.8.7 or newer not found.") set(_force_blueberry_use_qt_help_to_off 1) endif() if(NOT QT_HELPGENERATOR_EXECUTABLE) message("> Forcing BLUEBERRY_USE_QT_HELP to OFF because QT_HELPGENERATOR_EXECUTABLE is empty.") set(_force_blueberry_use_qt_help_to_off 1) endif() if(NOT MITK_USE_Qt6) message("> Forcing BLUEBERRY_USE_QT_HELP to OFF because MITK_USE_Qt6 is OFF.") set(_force_blueberry_use_qt_help_to_off 1) endif() if(_force_blueberry_use_qt_help_to_off) set(BLUEBERRY_USE_QT_HELP OFF CACHE BOOL "Enable support for integrating plugin documentation into Qt Help" FORCE) endif() endif() if(BLUEBERRY_QT_HELP_REQUIRED AND NOT BLUEBERRY_USE_QT_HELP) message(FATAL_ERROR "BLUEBERRY_USE_QT_HELP is required to be set to ON") endif() endif() endif() #----------------------------------------------------------------------------- # Testing #----------------------------------------------------------------------------- if(BUILD_TESTING) # Configuration for the CMake-generated test driver set(CMAKE_TESTDRIVER_EXTRA_INCLUDES "#include ") set(CMAKE_TESTDRIVER_BEFORE_TESTMAIN " try {") set(CMAKE_TESTDRIVER_AFTER_TESTMAIN " } catch (const std::exception& e) { fprintf(stderr, \"%s\\n\", e.what()); return EXIT_FAILURE; } catch (...) { printf(\"Exception caught in the test driver\\n\"); return EXIT_FAILURE; }") set(MITK_TEST_OUTPUT_DIR "${MITK_BINARY_DIR}/test_output") if(NOT EXISTS ${MITK_TEST_OUTPUT_DIR}) file(MAKE_DIRECTORY ${MITK_TEST_OUTPUT_DIR}) endif() # Test the package target include(mitkPackageTest) endif() configure_file(mitkTestingConfig.h.in ${MITK_BINARY_DIR}/mitkTestingConfig.h) #----------------------------------------------------------------------------- # MITK_SUPERBUILD_BINARY_DIR #----------------------------------------------------------------------------- # If MITK_SUPERBUILD_BINARY_DIR isn't defined, it means MITK is *NOT* build using Superbuild. # In that specific case, MITK_SUPERBUILD_BINARY_DIR should default to MITK_BINARY_DIR if(NOT DEFINED MITK_SUPERBUILD_BINARY_DIR) set(MITK_SUPERBUILD_BINARY_DIR ${MITK_BINARY_DIR}) endif() #----------------------------------------------------------------------------- # Set C/CXX and linker flags for MITK code #----------------------------------------------------------------------------- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${MITK_CXX_FLAGS}") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${MITK_CXX_FLAGS_DEBUG}") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${MITK_CXX_FLAGS_RELEASE}") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${MITK_C_FLAGS}") set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${MITK_C_FLAGS_DEBUG}") set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${MITK_C_FLAGS_RELEASE}") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${MITK_EXE_LINKER_FLAGS}") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${MITK_SHARED_LINKER_FLAGS}") set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${MITK_MODULE_LINKER_FLAGS}") #----------------------------------------------------------------------------- # Add subdirectories #----------------------------------------------------------------------------- add_subdirectory(Utilities) add_subdirectory(Modules) include("${CMAKE_CURRENT_SOURCE_DIR}/Modules/ModuleList.cmake") mitkFunctionWhitelistModules(MITK MITK_MODULES) set(MITK_ROOT_FOLDER_BACKUP "${MITK_ROOT_FOLDER}") foreach(MITK_EXTENSION_DIR ${MITK_ABSOLUTE_EXTENSION_DIRS}) get_filename_component(MITK_ROOT_FOLDER "${MITK_EXTENSION_DIR}" NAME) set(MITK_MODULES_EXTENSION_DIR "${MITK_EXTENSION_DIR}/Modules") if(EXISTS "${MITK_MODULES_EXTENSION_DIR}/ModuleList.cmake") set(MITK_MODULES "") include("${MITK_MODULES_EXTENSION_DIR}/ModuleList.cmake") foreach(mitk_module ${MITK_MODULES}) add_subdirectory("${MITK_MODULES_EXTENSION_DIR}/${mitk_module}" "Modules/${mitk_module}") endforeach() endif() set(MITK_MODULE_NAME_PREFIX ${MITK_DEFAULT_MODULE_NAME_PREFIX}) endforeach() set(MITK_ROOT_FOLDER "${MITK_ROOT_FOLDER_BACKUP}") add_subdirectory(Wrapping) set(MITK_DOXYGEN_OUTPUT_DIR "${PROJECT_BINARY_DIR}/Documentation/Doxygen" CACHE PATH "Output directory for doxygen generated documentation.") if(MITK_USE_BLUEBERRY) include("${CMAKE_CURRENT_SOURCE_DIR}/Plugins/PluginList.cmake") mitkFunctionWhitelistPlugins(MITK MITK_PLUGINS) set(mitk_plugins_fullpath "") foreach(mitk_plugin ${MITK_PLUGINS}) list(APPEND mitk_plugins_fullpath Plugins/${mitk_plugin}) endforeach() set(MITK_PLUGIN_REGEX_LIST "") foreach(MITK_EXTENSION_DIR ${MITK_ABSOLUTE_EXTENSION_DIRS}) set(MITK_PLUGINS_EXTENSION_DIR "${MITK_EXTENSION_DIR}/Plugins") if(EXISTS "${MITK_PLUGINS_EXTENSION_DIR}/PluginList.cmake") set(MITK_PLUGINS "") include("${MITK_PLUGINS_EXTENSION_DIR}/PluginList.cmake") foreach(mitk_plugin ${MITK_PLUGINS}) list(APPEND mitk_plugins_fullpath "${MITK_PLUGINS_EXTENSION_DIR}/${mitk_plugin}") endforeach() endif() endforeach() if(EXISTS ${MITK_PRIVATE_MODULES}/PluginList.cmake) include(${MITK_PRIVATE_MODULES}/PluginList.cmake) foreach(mitk_plugin ${MITK_PRIVATE_PLUGINS}) list(APPEND mitk_plugins_fullpath ${MITK_PRIVATE_MODULES}/${mitk_plugin}) endforeach() endif() if(MITK_BUILD_EXAMPLES) include("${CMAKE_CURRENT_SOURCE_DIR}/Examples/Plugins/PluginList.cmake") set(mitk_example_plugins_fullpath ) foreach(mitk_example_plugin ${MITK_EXAMPLE_PLUGINS}) list(APPEND mitk_example_plugins_fullpath Examples/Plugins/${mitk_example_plugin}) list(APPEND mitk_plugins_fullpath Examples/Plugins/${mitk_example_plugin}) endforeach() endif() # Specify which plug-ins belong to this project macro(GetMyTargetLibraries all_target_libraries varname) set(re_ctkplugin_mitk "^org_mitk_[a-zA-Z0-9_]+$") set(re_ctkplugin_bb "^org_blueberry_[a-zA-Z0-9_]+$") set(_tmp_list) list(APPEND _tmp_list ${all_target_libraries}) ctkMacroListFilter(_tmp_list re_ctkplugin_mitk re_ctkplugin_bb MITK_PLUGIN_REGEX_LIST OUTPUT_VARIABLE ${varname}) endmacro() # Get infos about application directories and build options set(mitk_apps_fullpath "") foreach(MITK_EXTENSION_DIR ${MITK_DIR_PLUS_EXTENSION_DIRS}) set(MITK_APPLICATIONS_EXTENSION_DIR "${MITK_EXTENSION_DIR}/Applications") if(EXISTS "${MITK_APPLICATIONS_EXTENSION_DIR}/AppList.cmake") set(MITK_APPS "") include("${MITK_APPLICATIONS_EXTENSION_DIR}/AppList.cmake") foreach(mitk_app ${MITK_APPS}) # extract option_name string(REPLACE "^^" "\\;" target_info ${mitk_app}) set(target_info_list ${target_info}) list(GET target_info_list 0 directory_name) list(GET target_info_list 1 option_name) if(${option_name}) list(APPEND mitk_apps_fullpath "${MITK_APPLICATIONS_EXTENSION_DIR}/${directory_name}^^${option_name}") endif() endforeach() endif() endforeach() if (mitk_plugins_fullpath) ctkMacroSetupPlugins(${mitk_plugins_fullpath} BUILD_OPTION_PREFIX MITK_BUILD_ APPS ${mitk_apps_fullpath} BUILD_ALL ${MITK_BUILD_ALL_PLUGINS} COMPACT_OPTIONS) endif() set(MITK_PLUGIN_USE_FILE "${MITK_BINARY_DIR}/MitkPluginUseFile.cmake") if(${PROJECT_NAME}_PLUGIN_LIBRARIES) ctkFunctionGeneratePluginUseFile(${MITK_PLUGIN_USE_FILE}) else() file(REMOVE ${MITK_PLUGIN_USE_FILE}) set(MITK_PLUGIN_USE_FILE ) endif() endif() #----------------------------------------------------------------------------- # Documentation #----------------------------------------------------------------------------- set(MITK_DOXYGEN_ADDITIONAL_INPUT_DIRS) set(MITK_DOXYGEN_ADDITIONAL_IMAGE_PATHS) set(MITK_DOXYGEN_ADDITIONAL_EXAMPLE_PATHS) foreach(MITK_EXTENSION_DIR ${MITK_DIR_PLUS_EXTENSION_DIRS}) set(MITK_DOXYGEN_ADDITIONAL_INPUT_DIRS "${MITK_DOXYGEN_ADDITIONAL_INPUT_DIRS} \"${MITK_EXTENSION_DIR}\"") set(MITK_DOXYGEN_ADDITIONAL_IMAGE_PATHS "${MITK_DOXYGEN_ADDITIONAL_IMAGE_PATHS} \"${MITK_EXTENSION_DIR}\"") # MITK_DOXYGEN_ADDITIONAL_EXAMPLE_PATHS should be modified by MITK extensions as needed endforeach() if(DOXYGEN_FOUND) foreach(MITK_EXTENSION_DIR ${MITK_DIR_PLUS_EXTENSION_DIRS}) set(MITK_DOCUMENTATION_EXTENSION_DIR "${MITK_EXTENSION_DIR}/Documentation") file(GLOB MITK_DOCUMENTATION_EXTENSION_FILES CONFIGURE_DEPENDS "${MITK_DOCUMENTATION_EXTENSION_DIR}/*.cmake") foreach(doc_file ${MITK_DOCUMENTATION_EXTENSION_FILES}) include("${doc_file}") endforeach() endforeach() # Transform list of example paths into space-separated string of quoted paths unset(example_paths) foreach(example_path ${MITK_DOXYGEN_ADDITIONAL_EXAMPLE_PATHS}) set(example_paths "${example_paths} \"${example_path}\"") endforeach() set(MITK_DOXYGEN_ADDITIONAL_EXAMPLE_PATHS "${example_paths}") add_subdirectory(Documentation) endif() #----------------------------------------------------------------------------- # Installation #----------------------------------------------------------------------------- # set MITK cpack variables # These are the default variables, which can be overwritten ( see below ) include(mitkSetupCPack) set(use_default_config ON) set(ALL_MITK_APPS "") set(activated_apps_no 0) foreach(MITK_EXTENSION_DIR ${MITK_DIR_PLUS_EXTENSION_DIRS}) set(MITK_APPLICATIONS_EXTENSION_DIR "${MITK_EXTENSION_DIR}/Applications") if(EXISTS "${MITK_APPLICATIONS_EXTENSION_DIR}/AppList.cmake") set(MITK_APPS "") include("${MITK_APPLICATIONS_EXTENSION_DIR}/AppList.cmake") foreach(mitk_app ${MITK_APPS}) string(REPLACE "^^" "\\;" target_info ${mitk_app}) set(target_info_list ${target_info}) list(GET target_info_list 0 directory_name) list(GET target_info_list 1 option_name) list(GET target_info_list 2 executable_name) list(APPEND ALL_MITK_APPS "${MITK_EXTENSION_DIR}/Applications/${directory_name}^^${option_name}^^${executable_name}") if(${option_name} OR MITK_BUILD_ALL_APPS) MATH(EXPR activated_apps_no "${activated_apps_no} + 1") endif() endforeach() endif() endforeach() list(LENGTH ALL_MITK_APPS app_count) if(app_count EQUAL 1 AND (activated_apps_no EQUAL 1 OR MITK_BUILD_ALL_APPS)) # Corner case if there is only one app in total set(use_project_cpack ON) elseif(activated_apps_no EQUAL 1 AND NOT MITK_BUILD_ALL_APPS) # Only one app is enabled (no "build all" flag set) set(use_project_cpack ON) else() # Less or more then one app is enabled set(use_project_cpack OFF) endif() foreach(mitk_app ${ALL_MITK_APPS}) # extract target_dir and option_name string(REPLACE "^^" "\\;" target_info ${mitk_app}) set(target_info_list ${target_info}) list(GET target_info_list 0 target_dir) list(GET target_info_list 1 option_name) list(GET target_info_list 2 executable_name) # check if the application is enabled if(${option_name} OR MITK_BUILD_ALL_APPS) # check whether application specific configuration files will be used if(use_project_cpack) # use files if they exist if(EXISTS "${target_dir}/CPackOptions.cmake") include("${target_dir}/CPackOptions.cmake") endif() if(EXISTS "${target_dir}/CPackConfig.cmake.in") set(CPACK_PROJECT_CONFIG_FILE "${target_dir}/CPackConfig.cmake") configure_file(${target_dir}/CPackConfig.cmake.in ${CPACK_PROJECT_CONFIG_FILE} @ONLY) set(use_default_config OFF) endif() endif() # add link to the list list(APPEND CPACK_CREATE_DESKTOP_LINKS "${executable_name}") endif() endforeach() # if no application specific configuration file was used, use default if(use_default_config) configure_file(${MITK_SOURCE_DIR}/MITKCPackOptions.cmake.in ${MITK_BINARY_DIR}/MITKCPackOptions.cmake @ONLY) set(CPACK_PROJECT_CONFIG_FILE "${MITK_BINARY_DIR}/MITKCPackOptions.cmake") endif() # include CPack model once all variables are set include(CPack) # Additional installation rules include(mitkInstallRules) #----------------------------------------------------------------------------- # Last configuration steps #----------------------------------------------------------------------------- # ---------------- Export targets ----------------- set(MITK_EXPORTS_FILE "${MITK_BINARY_DIR}/MitkExports.cmake") file(REMOVE ${MITK_EXPORTS_FILE}) set(targets_to_export) get_property(module_targets GLOBAL PROPERTY MITK_MODULE_TARGETS) if(module_targets) list(APPEND targets_to_export ${module_targets}) endif() if(MITK_USE_BLUEBERRY) if(MITK_PLUGIN_LIBRARIES) list(APPEND targets_to_export ${MITK_PLUGIN_LIBRARIES}) endif() endif() export(TARGETS ${targets_to_export} APPEND FILE ${MITK_EXPORTS_FILE}) set(MITK_EXPORTED_TARGET_PROPERTIES ) foreach(target_to_export ${targets_to_export}) get_target_property(autoload_targets ${target_to_export} MITK_AUTOLOAD_TARGETS) if(autoload_targets) set(MITK_EXPORTED_TARGET_PROPERTIES "${MITK_EXPORTED_TARGET_PROPERTIES} set_target_properties(${target_to_export} PROPERTIES MITK_AUTOLOAD_TARGETS \"${autoload_targets}\")") endif() get_target_property(autoload_dir ${target_to_export} MITK_AUTOLOAD_DIRECTORY) if(autoload_dir) set(MITK_EXPORTED_TARGET_PROPERTIES "${MITK_EXPORTED_TARGET_PROPERTIES} set_target_properties(${target_to_export} PROPERTIES MITK_AUTOLOAD_DIRECTORY \"${autoload_dir}\")") endif() get_target_property(deprecated_module ${target_to_export} MITK_MODULE_DEPRECATED_SINCE) if(deprecated_module) set(MITK_EXPORTED_TARGET_PROPERTIES "${MITK_EXPORTED_TARGET_PROPERTIES} set_target_properties(${target_to_export} PROPERTIES MITK_MODULE_DEPRECATED_SINCE \"${deprecated_module}\")") endif() endforeach() # ---------------- External projects ----------------- get_property(MITK_ADDITIONAL_LIBRARY_SEARCH_PATHS_CONFIG GLOBAL PROPERTY MITK_ADDITIONAL_LIBRARY_SEARCH_PATHS) set(MITK_CONFIG_EXTERNAL_PROJECTS ) #string(REPLACE "^^" ";" _mitk_external_projects ${MITK_EXTERNAL_PROJECTS}) foreach(ep ${MITK_EXTERNAL_PROJECTS}) get_property(_components GLOBAL PROPERTY MITK_${ep}_COMPONENTS) set(MITK_CONFIG_EXTERNAL_PROJECTS "${MITK_CONFIG_EXTERNAL_PROJECTS} set(MITK_USE_${ep} ${MITK_USE_${ep}}) set(MITK_${ep}_DIR \"${${ep}_DIR}\") set(MITK_${ep}_COMPONENTS ${_components}) ") endforeach() foreach(ep ${MITK_EXTERNAL_PROJECTS}) get_property(_package GLOBAL PROPERTY MITK_${ep}_PACKAGE) get_property(_components GLOBAL PROPERTY MITK_${ep}_COMPONENTS) if(_components) set(_components_arg COMPONENTS \${_components}) else() set(_components_arg) endif() if(_package) set(MITK_CONFIG_EXTERNAL_PROJECTS "${MITK_CONFIG_EXTERNAL_PROJECTS} if(MITK_USE_${ep}) set(${ep}_DIR \${MITK_${ep}_DIR}) if(MITK_${ep}_COMPONENTS) mitkMacroFindDependency(${_package} COMPONENTS \${MITK_${ep}_COMPONENTS}) else() mitkMacroFindDependency(${_package}) endif() endif()") endif() endforeach() # ---------------- Tools ----------------- configure_file(${MITK_SOURCE_DIR}/CMake/ToolExtensionITKFactory.cpp.in ${MITK_BINARY_DIR}/ToolExtensionITKFactory.cpp.in COPYONLY) configure_file(${MITK_SOURCE_DIR}/CMake/ToolExtensionITKFactoryLoader.cpp.in ${MITK_BINARY_DIR}/ToolExtensionITKFactoryLoader.cpp.in COPYONLY) configure_file(${MITK_SOURCE_DIR}/CMake/ToolGUIExtensionITKFactory.cpp.in ${MITK_BINARY_DIR}/ToolGUIExtensionITKFactory.cpp.in COPYONLY) # ---------------- Configure files ----------------- configure_file(mitkVersion.h.in ${MITK_BINARY_DIR}/mitkVersion.h) configure_file(mitkConfig.h.in ${MITK_BINARY_DIR}/mitkConfig.h) set(IPFUNC_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Utilities/ipFunc) set(UTILITIES_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Utilities) configure_file(mitkConfig.h.in ${MITK_BINARY_DIR}/mitkConfig.h) configure_file(MITKConfig.cmake.in ${MITK_BINARY_DIR}/MITKConfig.cmake @ONLY) write_basic_config_version_file(${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake VERSION ${MITK_VERSION_STRING} COMPATIBILITY AnyNewerVersion) #----------------------------------------------------------------------------- # MITK Applications #----------------------------------------------------------------------------- # This must come after MITKConfig.h was generated, since applications # might do a find_package(MITK REQUIRED). add_subdirectory(Applications) if(MSVC AND TARGET MitkWorkbench) set_directory_properties(PROPERTIES VS_STARTUP_PROJECT MitkWorkbench) endif() foreach(MITK_EXTENSION_DIR ${MITK_ABSOLUTE_EXTENSION_DIRS}) set(MITK_APPLICATIONS_EXTENSION_DIR "${MITK_EXTENSION_DIR}/Applications") if(EXISTS "${MITK_APPLICATIONS_EXTENSION_DIR}/CMakeLists.txt") add_subdirectory("${MITK_APPLICATIONS_EXTENSION_DIR}" "Applications") endif() endforeach() #----------------------------------------------------------------------------- # MITK Examples #----------------------------------------------------------------------------- if(MITK_BUILD_EXAMPLES) # This must come after MITKConfig.h was generated, since applications # might do a find_package(MITK REQUIRED). add_subdirectory(Examples) endif() #----------------------------------------------------------------------------- # Print configuration summary #----------------------------------------------------------------------------- message("\n\n") feature_summary( DESCRIPTION "------- FEATURE SUMMARY FOR ${PROJECT_NAME} -------" WHAT ALL ) diff --git a/Documentation/Doxygen/2-UserManual/MITKPluginManualsList.dox b/Documentation/Doxygen/2-UserManual/MITKPluginManualsList.dox index 9a67a1ea6b..6180e08610 100644 --- a/Documentation/Doxygen/2-UserManual/MITKPluginManualsList.dox +++ b/Documentation/Doxygen/2-UserManual/MITKPluginManualsList.dox @@ -1,64 +1,64 @@ /** \page PluginListPage MITK Plugin Manuals -The plugins and bundles provide much of the extended functionality of MITK. Each encapsulates a solution to a problem and associated features. This way one can easily assemble the necessary capabilites for a workflow without adding a lot of bloat, by combining plugins as needed. +The plugins and bundles provide much of the extended functionality of MITK. Each encapsulates a solution to a problem and associated features. This way one can easily assemble the necessary capabilities for a workflow without adding a lot of bloat, by combining plugins as needed.
  • \subpage org_mitk_views_basicimageprocessing
  • \subpage org_mitk_views_datamanager
  • \subpage org_mitk_editors_dicombrowser
  • \subpage org_mitk_views_dicominspector
  • \subpage org_mitk_views_imagecropper
  • \subpage org_mitk_views_imagenavigator
  • \subpage org_mitk_views_pixelvalue
  • \subpage org_blueberry_views_logview
  • \subpage org_mitk_views_matchpoint_algorithm_browser
  • \subpage org_mitk_views_matchpoint_algorithm_control
  • \subpage org_mitk_views_matchpoint_evaluator
  • \subpage org_mitk_views_matchpoint_framereg
  • \subpage org_mitk_views_matchpoint_manipulator
  • \subpage org_mitk_views_matchpoint_mapper
  • \subpage org_mitk_views_matchpoint_visualizer
  • \subpage org_mitk_gui_qt_measurementtoolbox
    • \subpage org_mitk_views_measurement
    • \subpage org_mitk_views_imagestatistics
  • \subpage org_mitk_views_moviemaker
  • \subpage org_mitk_views_pointsetinteraction
  • \subpage org_mitk_views_python
  • \subpage org_mitk_views_remeshing
  • \subpage org_mitk_views_screenshotmaker
  • Segmentation
    • \subpage org_mitk_views_segmentation
    • \subpage org_mitk_views_segmentationutilities
    • \subpage org_mitk_views_segmentationtasklist
  • \subpage org_mitk_editors_stdmultiwidget
  • \subpage org_mitk_editors_mxnmultiwidget
  • \subpage org_mitk_views_viewnavigator
  • \subpage org_mitk_views_volumevisualization
  • \subpage org_mitk_views_properties
  • \subpage org_mitk_gui_qt_flowapplication
  • \subpage org_mitk_gui_qt_flow_segmentation
  • \subpage org_mitk_gui_qt_aicpregistration
  • \subpage org_mitk_gui_qt_cest
  • \subpage org_mitk_views_pharmacokinetics_concentration_mri
  • \subpage org_mitk_views_pharmacokinetics_mri
  • \subpage org_mitk_views_pharmacokinetics_pet
  • \subpage org_mitk_gui_qt_examples
  • \subpage org_mitk_views_fit_demo
  • \subpage org_mitk_views_fit_genericfitting
  • \subpage org_mitk_views_fit_inspector
  • \subpage org_mitk_gui_qt_overlaymanager
  • \subpage org_mitk_gui_qt_preprocessing_resampling
  • \subpage org_mitk_views_pharmacokinetics_curvedescriptor
  • \subpage org_mitk_views_pharmacokinetics_simulation
  • \subpage org_blueberry_ui_qt_objectinspector
  • \subpage org_mitk_gui_qt_xnat
*/ diff --git a/Documentation/Doxygen/3-DeveloperManual/Application/BlueBerry/BlueBerryExtensionPointsIntro.dox b/Documentation/Doxygen/3-DeveloperManual/Application/BlueBerry/BlueBerryExtensionPointsIntro.dox index 23128dd14c..dfa5db4be5 100644 --- a/Documentation/Doxygen/3-DeveloperManual/Application/BlueBerry/BlueBerryExtensionPointsIntro.dox +++ b/Documentation/Doxygen/3-DeveloperManual/Application/BlueBerry/BlueBerryExtensionPointsIntro.dox @@ -1,34 +1,34 @@ /** \page BlueBerryExampleExtensionPoint Extension Points -\brief A minimal applictaion that definines an extension point and collects extensions. +\brief A minimal application that defines an extension point and collects extensions. -# \subpage IntroductionExtensionPoints "Introduction" -# \subpage ExtensionPointDefinition "Extension Point Definition" -# \subpage ExtensionContribution "Extension Contribution" \page IntroductionExtensionPoints Introduction: Extension Point/Extension Concept The BlueBerry application framework provides the concept of extension points and extensions. The main goal is to allow the extension of functionality of a plugin (based on the contract defined by the extension point) by several other plugins. Both the extension point and the extension are defined in the according plugin.xml. \imageMacro{ExtensionPoints.png,"Extension Point concept",5.40} \section SimpleExample Why Extension Points? In the following simple example we have a plugin 'a' and 'b' with two classes 'A' and 'B' in these plugins. \imageMacro{ExtensionPointEx.png,"Simple Example",5.10} Plugin 'a' uses the extension point mechanism and creates an extension point that can be extended by other plugins. Now if class 'A' reaches a part that can be extended it asks 'a' if another plugin is registered. If that's the case the functionality of the plugin 'b' that is defined in class 'B' is executed. A plugin can therefore be arbitrary extended. \section BlueBerryExampleExtensionPoint_Examples Examples The two following example plugins describe the usage of the BlueBerry Extension Points. One example defines an extension point and the other example extends the created extension point. \li \ref org_mitk_example_gui_extensionpointdefinition \li \ref org_mitk_example_gui_extensionpointcontribution [\ref BlueBerryExampleExtensionPoint] [Next: \ref ExtensionPointDefinition] [\ref BlueBerryExamples] */ diff --git a/Documentation/Doxygen/3-DeveloperManual/Application/BlueBerry/BlueBerryIntro.dox b/Documentation/Doxygen/3-DeveloperManual/Application/BlueBerry/BlueBerryIntro.dox index 5543d831ae..a63c6f6c59 100644 --- a/Documentation/Doxygen/3-DeveloperManual/Application/BlueBerry/BlueBerryIntro.dox +++ b/Documentation/Doxygen/3-DeveloperManual/Application/BlueBerry/BlueBerryIntro.dox @@ -1,79 +1,79 @@ /** \page BlueBerryIntro BlueBerry Application Framework BlueBerry is an application framework used in MITK for creating modular and extensible end-user applications. More high-level documentation can be found below: - \subpage BlueBerryWorkbench - \ref BlueBerryExamples Please see the \ref BlueBerryExamples for code examples demonstrating different features of the application framework. The BlueBerry developer reference is available here: - \ref BlueBerryPlugins - \subpage BlueBerryExtPointsIndex \page BlueBerryWorkbench The Workbench: What are Views, Editors, Perspectives? -BlueBerry makes use of the Eclipse UI guidlines which state some concepts on how to build up a GUI. The different objects of the platform UI shall be described here: +BlueBerry makes use of the Eclipse UI guidelines which state some concepts on how to build up a GUI. The different objects of the platform UI shall be described here: \section Workbench Workbench \li root object of the platform UI \li collection of \ref WorkbenchWindow "windows" \imageMacro{workbench.jpg,"The Workbech",11.64} \section WorkbenchWindow WorkbenchWindow \li has one page \imageMacro{workbench-window.jpg,"Workbench Windows",8.47} \section WorkbenchPage Workbench Page \li denotes to the inner part of the \ref WorkbenchWindow "window", that is: everything except the title bar \li may have one menu bar, one toolbar, one shortcut bar, and one statusbar \li has one or more \ref Perspective "perspectives" \imageMacro{workbench-page.jpg,"Workbench Page",8.47} \section Perspective Perspective
  • A visual container for a set of \ref Views "views" and content \ref Editors "editors"
  • Shows \ref Views "views" and \ref Editors "editors" in a certain layout
  • Like a page within a book:
    • Only one perspective is visible at any time
    • There are several perspectives inside a page
\imageMacro{workbench-window-perspective.png,"A Perspective",11.79} \section Part Part \li every \ref Views "View" or \ref Editors "Editor" is called \b Part \subsection Editors Editors \li the StdMultiWidget is an example for an editor in our MainApp \li Contains the primary content, such as a document or image data, which users interact with \li content is the primary focus of attention and a reflection of the primary task \li primary position in the UI \li contributes commands to the workbench's main menu bar and toolbar \li shared in other perspectives \imageMacro{workbench-window-editor-area.png,"Editor Area",11.79} \subsection Views Views
  • support the primary task
    • navigate a hierarchy of information
    • open an \ref Editors "editor"
    • view/edit properties
  • The views exist wholly within the perspective (not shared, one instance at a time)
  • Every functionality is a view- it supports medical image processing
\imageMacro{workbench-window-views.png,"Views",11.79} \section ClassDiagram Summary as class diagram \imageMacro{workbench-class-diagram.jpg,"class diagram",13.74} */ diff --git a/Documentation/Doxygen/3-DeveloperManual/Application/BlueBerryExtensionPointReference.dox b/Documentation/Doxygen/3-DeveloperManual/Application/BlueBerryExtensionPointReference.dox index 427ca8e76e..97c148422a 100644 --- a/Documentation/Doxygen/3-DeveloperManual/Application/BlueBerryExtensionPointReference.dox +++ b/Documentation/Doxygen/3-DeveloperManual/Application/BlueBerryExtensionPointReference.dox @@ -1,1356 +1,1356 @@ /** \page BlueBerryExtPointsIndex BlueBerry Extension-Point Reference - \subpage BlueBerryExtPointsIndex_CoreExpr - \subpage BlueBerryExtPointsIndex_PlatformRuntime - \subpage BlueBerryExtPointsIndex_Workbench \page BlueBerryExtPointsIndex_CoreExpr Platform Core Expressions \tableofcontents \section BlueBerryExtPointsIndex_CoreExpr_ExprDef Expression Definitions \subsection BlueBerryExtPointsIndex_CoreExpr_ExprDef_Id Identifier \c org.blueberry.core.expressions.definitions \subsection BlueBerryExtPointsIndex_CoreExpr_ExprDef_Desc Description This extension point allows you to create reusable extensions. They can then be used in other core expression constructs. The reference element in a core expression will evaluated the expression definition with the evaluation context that is active for the reference element. \subsection BlueBerryExtPointsIndex_CoreExpr_ExprDef_ConfMarkup Configuration Markup \code{.unparsed} \endcode - point: a fully qualified identifier of the target extension point - id: an optional identifier of the extension instance - name: an optional name of the extension instance \code{.unparsed} \endcode Provides a global definition of an expression to be used with the \c \ expression element. This helps to reuse common expressions. - id: a globally unique identifier for the expression definition \code{.unparsed} \endcode A generic root element. The element can be used inside an extension point to define its enablement expression. The children of an enablement expression are combined using the and operator. \code{.unparsed} \endcode This element represent a NOT operation on the result of evaluating it's sub-element expression. \code{.unparsed} \endcode This element represent an AND operation on the result of evaluating all it's sub-elements expressions. \code{.unparsed} \endcode This element represent an OR operation on the result of evaluating all it's sub-element expressions. \code{.unparsed} \endcode This element is used to perform an instanceof check of the object in focus. The expression returns \c EvaluationResult.TRUE if the object's type is a sub type of the type specified by the attribute value. Otherwise \c EvaluationResult.FALSE is returned. - value: a fully qualified name of a class or interface \code{.unparsed} \endcode This element is used to evaluate the property state of the object in focus. -The set of testable properties can be extended using the propery tester extension point. +The set of testable properties can be extended using the property tester extension point. The test expression returns \c EvaluationResult.NOT_LOADED if the property tester doing the actual testing isn't loaded yet and the attribute \c forcePluginActivation is set to \c false. If \c forcePluginActivation is set to \c true and the evaluation context used to evaluate this expression support plug-in activation then evaluating the property will result in activating the plug-in defining the tester. - property: The name of an object's property to test. -- args: Additional arguments passed to the property tester. Multiple arguments are seperated by commas. Each individual argument is converted into a Java base type using the same rules as defined for the value attribute of the test expression. +- args: Additional arguments passed to the property tester. Multiple arguments are separated by commas. Each individual argument is converted into a Java base type using the same rules as defined for the value attribute of the test expression. - value: The expected value of the property. Can be omitted if the property is a boolean property. The test expression is supposed to return \c EvaluationResult.TRUE if the property matches the value and \c EvaluationResult.FALSE otherwise. The value attribute is converted into a Java base type using the following rules: - The string \c "true" is converted into \c Boolean.TRUE . - The string \c "false" is converted into \c Boolean.FALSE . - If the string contains a dot then the interpreter tries to convert the value into a \c Float object. If this fails the string is treated as a \c java.lang.String. - If the string only consists of numbers then the interpreter converts the value in an \c Integer object. - In all other cases the string is treated as a \c java.lang.String . - The conversion of the string into a \c Boolean , \c Float , or \c Integer can be suppressed by surrounding the string with single quotes. For example, the attribute \c value="'true'" is converted into the string "true". - forcePluginActivation: A flag indicating whether the plug-in contributing the property tester should be loaded if necessary. As such, this flag should be used judiciously, in order to avoid unnecessary plug-in activations. Most clients should avoid setting this flag to \c true. This flag is only honored if the evaluation context used to evaluate this expression allows plug-in activation. Otherwise the flag is ignored and no plug-in loading takes place. \code{.unparsed} \endcode Tests a system property by calling the \c System.getProperty method and compares the result with the value specified through the value attribute. - property: The name of an system property to test. - value: The expected value of the property. The value is interpreted as a string value. \code{.unparsed} \endcode This element is used to perform an equals check of the object in focus. The expression returns \c EvaluationResult.TRUE if the object is equal to the value provided by the attribute value. Otherwise \c EvaluationResult.FALSE is returned. - value: The expected value. The value provided as a string is converted into a Java base type using the same rules as for the value attribute of the test expression. \code{.unparsed} \endcode This element is used to test the number of elements in a collection. - value: An expression to specify the number of elements in a list. Following wildcard characters can be used: - *: any number of elements - ?: no elements or one element - +: one or more elements - !: no elements - integer value: the list must contain the exact number of elements \code{.unparsed} \endcode This element changes the object to be inspected for all its child element to the object referenced by the given variable. If the variable can not be resolved then the expression will throw an \c ExpressionException when evaluating it. The children of a with expression are combined using the and operator. - variable: The name of the variable to be used for further inspection. It is up to the evaluator of an extension point to provide the variable in the variable pool. \code{.unparsed} \endcode This element changes the object to be inspected for all its child element to the object referenced by the given variable. If the variable can not be resolved then the expression will throw an \c ExpressionException when evaluating it. The children of a with expression are combined using the and operator. - variable: The name of the variable to be resolved. This variable is then used as the object in focus for child element evaluation. It is up to the evaluator of an extension point to provide a corresponding variable resolver (see berry::IVariableResolver) through the evaluation context passed to the root expression element when evaluating the expression. -- args: Additional arguments passed to the variable resolver. Multiple arguments are seperated by commas. Each individual argument is converted into a Java base type using the same rules as defined for the value attribute of the test expression. +- args: Additional arguments passed to the variable resolver. Multiple arguments are separated by commas. Each individual argument is converted into a Java base type using the same rules as defined for the value attribute of the test expression. \code{.unparsed} \endcode This element is used to adapt the object in focus to the type specified by the attribute type. The expression returns not loaded if either the adapter or the type referenced isn't loaded yet. It throws an \c ExpressionException during evaluation if the type name doesn't exist at all. The children of an adapt expression are combined using the and operator. - type: the type to which the object in focus is to be adapted \code{.unparsed} \endcode This element is used to iterate over a variable that is of type \c java.util.Collection. If the object in focus is not of type \c java.util.Collection then an \c ExpressionException will be thrown while evaluating the expression. - operator: Either "and" or "or". The operator defines how the child elements will be combined. If not specified, "and" will be used. - ifEmpty: The value return from the iterate expression if the collection is empty. If not specified then \c true is returned when the operator equals "and" and \c false is returned if the operator equals "or". \code{.unparsed} \endcode This element is used to reference an expression from the \c org.blueberry.core.expressions.definitions extension point. The expression definition will be evaluated within the current expression element using the current evaluation context. - definitionId: the unique id of an expression from \c org.blueberry.core.expressions.definitions \subsection BlueBerryExtPointsIndex_CoreExpr_ExprDef_Examples Examples \code{.unparsed} \endcode Then this expression definition can be used when composing other expressions. \code{.unparsed} \endcode \section BlueBerryExtPointsIndex_CoreExpr_CommExpr Common Expressions \subsection BlueBerryExtPointsIndex_CoreExpr_CommExpr_Id Identifier \c org.blueberry.core.expressions.commonExpression \subsection BlueBerryExtPointsIndex_CoreExpr_CommExpr_ConfMarkup Configuration Markup \code{.unparsed} \endcode A generic root element. The element can be used inside an extension point to define its enablement expression. The children of an enablement expression are combined using the and operator. \code{.unparsed} \endcode This element represent a NOT operation on the result of evaluating it's sub-element expression. \code{.unparsed} \endcode This element represent an AND operation on the result of evaluating all it's sub-elements expressions. \code{.unparsed} \endcode This element represent an OR operation on the result of evaluating all it's sub-element expressions. \code{.unparsed} \endcode This element is used to perform an instanceof check of the object in focus. The expression returns \c EvaluationResult.TRUE if the object's type is a sub type of the type specified by the attribute value. Otherwise \c EvaluationResult.FALSE is returned. - value: a fully qualified name of a class or interface \code{.unparsed} \endcode This element is used to evaluate the property state of the object in focus. -The set of testable properties can be extended using the propery tester extension point. +The set of testable properties can be extended using the property tester extension point. The test expression returns \c EvaluationResult.NOT_LOADED if the property tester doing the actual testing isn't loaded yet and the attribute \c forcePluginActivation is set to false. If \c forcePluginActivation is set to \c true and the evaluation context used to evaluate this expression support plug-in activation then evaluating the property will result in activating the plug-in defining the tester. - property: The name of an object's property to test. -- args: Additional arguments passed to the property tester. Multiple arguments are seperated by commas. Each individual argument is converted into a Java base type using the same rules as defined for the value attribute of the test expression. +- args: Additional arguments passed to the property tester. Multiple arguments are separated by commas. Each individual argument is converted into a Java base type using the same rules as defined for the value attribute of the test expression. - value: The expected value of the property. Can be omitted if the property is a boolean property. The test expression is supposed to return EvaluationResult.TRUE if the property matches the value and EvaluationResult.FALSE otherwise. The value attribute is converted into a Java base type using the following rules: - The string "true" is converted into \c Boolean.TRUE - The string "false" is converted into \c Boolean.FALSE - If the string contains a dot then the interpreter tries to convert the value into a \c Float object. If this fails the string is treated as a \c java.lang.String - If the string only consists of numbers then the interpreter converts the value in an \c Integer object. - In all other cases the string is treated as a \c java.lang.String - The conversion of the string into a \c Boolean , \c Float , or \c Integer can be suppressed by surrounding the string with single quotes. For example, the attribute \c value="'true'" is converted into the string "true" - forcePluginActivation: A flag indicating whether the plug-in contributing the property tester should be loaded if necessary. As such, this flag should be used judiciously, in order to avoid unnecessary plug-in activations. Most clients should avoid setting this flag to true. This flag is only honored if the evaluation context used to evaluate this expression allows plug-in activation. Otherwise the flag is ignored and no plug-in loading takes place. \code{.unparsed} \endcode Tests a system property by calling the \c System.getProperty method and compares the result with the value specified through the value attribute. - property: The name of an system property to test. - value: The expected value of the property. The value is interpreted as a string value. \code{.unparsed} \endcode This element is used to perform an equals check of the object in focus. The expression returns \c EvaluationResult.TRUE if the object is equal to the value provided by the attribute value. Otherwise \c EvaluationResult.FALSE is returned. - value: The expected value. The value provided as a string is converted into a Java base type using the same rules as for the value attribute of the test expression. \code{.unparsed} \endcode This element is used to test the number of elements in a collection. - value: An expression to specify the number of elements in a list. Following wildcard characters can be used: - *: any number of elements - ?: no elements or one element - +: one or more elements - !: no elements - integer value: the list must contain the exact number of elements \code{.unparsed} \endcode This element changes the object to be inspected for all its child element to the object referenced by the given variable. If the variable can not be resolved then the expression will throw an \c ExpressionException when evaluating it. The children of a with expression are combined using the and operator. - variable: The name of the variable to be used for further inspection. It is up to the evaluator of an extension point to provide the variable in the variable pool. \code{.unparsed} \endcode This element changes the object to be inspected for all its child element to the object referenced by the given variable. If the variable can not be resolved then the expression will throw an \c ExpressionException when evaluating it. The children of a with expression are combined using the and operator. - variable: The name of the variable to be resolved. This variable is then used as the object in focus for child element evaluation. It is up to the evaluator of an extension point to provide a corresponding variable resolver (see berry::IVariableResolver) through the evaluation context passed to the root expression element when evaluating the expression. -- args: Additional arguments passed to the variable resolver. Multiple arguments are seperated by commas. Each individual argument is converted into a Java base type using the same rules as defined for the value attribute of the test expression. +- args: Additional arguments passed to the variable resolver. Multiple arguments are separated by commas. Each individual argument is converted into a Java base type using the same rules as defined for the value attribute of the test expression. \code{.unparsed} \endcode This element is used to adapt the object in focus to the type specified by the attribute type. The expression returns not loaded if either the adapter or the type referenced isn't loaded yet. It throws an \c ExpressionException during evaluation if the type name doesn't exist at all. The children of an adapt expression are combined using the and operator. - type: the type to which the object in focus is to be adapted \code{.unparsed} \endcode This element is used to iterate over a variable that is of type \c java.util.Collection. If the object in focus is not of type \c java.util.Collection then an \c ExpressionException will be thrown while evaluating the expression. - operator: Either "and" or "or". The operator defines how the child elements will be combined. If not specified, "and" will be used. - ifEmpty: The value return from the iterate expression if the collection is empty. If not specified then \c true is returned when the operator equals "and" and \c false is returned if the operator equals "or". \code{.unparsed} \endcode This element is used to reference an expression from the \c org.blueberry.core.expressions.definitions extension point. The expression definition will be evaluated within the current expression element using the current evaluation context. - definitionId: the unique id of an expression from \c org.blueberry.core.expressions.definitions \section BlueBerryExtPointsIndex_CoreExpr_PropTest Property Testers \subsection BlueBerryExtPointsIndex_CoreExpr_PropTest_Id Identifier \c org.blueberry.core.expressions.propertyTesters \subsection BlueBerryExtPointsIndex_CoreExpr_PropTest_Desc Description This extension point allows to add properties to an already existing type. Those properties can then be used inside the expression language's test expression element. \subsection BlueBerryExtPointsIndex_CoreExpr_PropTest_ConfMarkup Configuration Markup \code{.unparsed} \endcode - point: a fully qualified identifier of the target extension point - id: an optional identifier of the extension instance - name: an optional name of the extension instance \code{.unparsed} \endcode - id: Unique identifier for the property tester. - type: The type to be extended by this property tester. - namespace: A unique id determining the name space the properties are added to. - properties: A comma separated list of properties provided by this property tester. - class: The name of the class that implements the testing methods. The class must be public and extend \c org.blueberry.core.expressions.PropertyTester with a public 0-argument constructor. \subsection BlueBerryExtPointsIndex_CoreExpr_PropTest_Examples Examples \code{.unparsed} \endcode \page BlueBerryExtPointsIndex_PlatformRuntime Platform Runtime \tableofcontents \section BlueBerryExtPointsIndex_PlatformRuntime_App Applications \subsection BlueBerryExtPointsIndex_PlatformRuntime_App_Id Identifier \c org.blueberry.osgi.applications \subsection BlueBerryExtPointsIndex_PlatformRuntime_App_Desc Description The applications extension point allows plugins to contribute applications to the BlueBerry Platform. \subsection BlueBerryExtPointsIndex_PlatformRuntime_App_ConfMarkup Configuration Markup \code{.unparsed} \endcode \code{.unparsed} \endcode \code{.unparsed} \endcode \subsection BlueBerryExtPointsIndex_PlatformRuntime_App_Examples Examples \code{.unparsed} \endcode \section BlueBerryExtPointsIndex_PlatformRuntime_Prod Products \subsection BlueBerryExtPointsIndex_PlatformRuntime_Prod_Id Identifier \c org.blueberry.core.runtime.products \subsection BlueBerryExtPointsIndex_PlatformRuntime_Prod_Desc Description Products are the BlueBerry unit of branding. Product extensions are supplied by plug-ins wishing to define one or more products. There must be one product per extension as the extension id is used in processing and identifying the product. There are two possible forms of product extension, static and dynamic. Static product extensions directly contain all relevant information about the product. Dynamic product extensions identify a class (an \c IProductProvider) which is capable of defining one or more products when queried. \subsection BlueBerryExtPointsIndex_PlatformRuntime_Prod_ConfMarkup Configuration Markup \code{.unparsed} \endcode \code{.unparsed} \endcode - application: the default application to run when running this product - name: the human-readable name of this product - description: the human-readable description of this product \code{.unparsed} \endcode - name: the key under which this property is stored - value: the value of this property \code{.unparsed} \endcode \code{.unparsed} \endcode - class: the fully-qualified name of a class which implements \c IProductProvider \subsection BlueBerryExtPointsIndex_PlatformRuntime_Prod_Examples Examples \code{.unparsed} \endcode The following is an example of a dynamic product (product provider) declaration: Following is an example of an application declaration: \code{.unparsed} \endcode \subsection BlueBerryExtPointsIndex_PlatformRuntime_Prod_SupplImpl Supplied Implementation No implementations of \c IProductProvider are supplied. \page BlueBerryExtPointsIndex_Workbench Workbench \tableofcontents \section BlueBerryExtPointsIndex_Workbench_Edit Internal and External Editors \subsection BlueBerryExtPointsIndex_Workbench_Edit_Id Identifier \c org.blueberry.ui.editors \subsection BlueBerryExtPointsIndex_Workbench_Edit_Desc Description This extension point is used to add new editors to the workbench. A editor is a visual component within a workbench page. It is typically used to edit or browse a document or input object. To open an editor, the user will typically invoke "Open" on an \c IFile. When this action is performed the workbench registry is consulted to determine an appropriate editor for the file type and then a new instance of the editor type is created. The actual result depends on the type of the editor. The workbench provides support for the creation of internal editors, which are tightly integrated into the workbench, and external editors, which are launched in a separate frame window. There are also various levels of integration between these extremes. In the case of an internal editor tight integration can be achieved between the workbench window and the editor part. The workbench menu and toolbar are pre-loaded with a number of common actions, such as cut, copy, and paste. The active part, view or editor, is expected to provide the implementation for these actions. An internal editor may also define new actions which appear in the workbench window. These actions only appear when the editor is active. The integration between the workbench and external editors is more tenuous. In this case the workbench may launch an editor but after has no way of determining the state of the external editor or collaborating with it by any means except through the file system. \subsection BlueBerryExtPointsIndex_Workbench_Edit_ConfMarkup Configuration Markup \code{.unparsed} \endcode - point: a fully qualified identifier of the target extension point - id: an optional identifier of the extension instance - name: an optional name of the extension instance \code{.unparsed} \endcode - id: a unique name that will be used to identify this editor - name: a translatable name that will be used in the UI for this editor - icon: A relative name of the icon that will be used for all resources that match the specified extensions. Editors should provide an icon to make it easy for users to distinguish between different editor types. If you specify a command rather than a class, an icon is not needed. In that case, the workbench will use the icon provided by the operating system. - extensions: an optional field containing the list of file types understood by the editor. This is a string containing comma separate file extensions. For instance, an editor which understands hypertext documents may register for "htm, html". - class: the name of a class that implements berry::IEditorPart. The attributes \c class , \c command , and \c launcher are mutually exclusive. If this attribute is defined then \c contributorClass should also be defined. - command: a command to run in order to launch an external editor. The executable command must be located on the system path or in the plug-in's directory. The attributes class, command, and launcher are mutually exclusive. - launcher: the name of a class which that implements berry::IEditorLauncher. A launcher will open an external editor. The attributes \c class , \c command , and \c launcher are mutually exclusive. - contributorClass: the name of a class that implements berry::IEditorActionBarContributor. This attribute should only be defined if the \c class attribute is defined. This class is used to add new actions to the workbench menu and tool bar which reflect the features of the editor type. - default: if true, this editor will be used as the default editor for the type. This is only relevant in a case where more than one editor is registered for the same type. If an editor is not the default for the type, it can still be launched using "Open with..." submenu for the selected resource. Please note that this attribute is only honored for filename and extension associations at this time. It will not be honored for content type bindings. Content type-based resolution will occur on a first come, first serve basis and is not explicitly specified. - filenames: an optional field containing the list of file names understood by the editor. This is a string containing comma separate file names. For instance, an editor which understands specific hypertext documents may register for "ejb.htm, ejb.html". - matchingStrategy: the name of a class that implements berry::IEditorMatchingStrategy. This attribute should only be defined if the \c class attribute is defined. This allows the editor extension to provide its own algorithm for matching the input of one of its editors to a given editor input. \code{.unparsed} \endcode Advertises that the containing editor understands the given content type and is suitable for editing files of that type. - contentTypeId: The content type identifier. This is an ID defined by the \c org.blueberry.core.runtime.contentTypes extension point. \subsection BlueBerryExtPointsIndex_Workbench_Edit_Examples Examples The following is an example of an internal editor extension definition: \code{.unparsed} \endcode \subsection BlueBerryExtPointsIndex_Workbench_Edit_SupplImpl Supplied Implementation The workbench provides a "Default Text Editor". The end user product may contain other editors as part of the shipping bundle. In that case, editors will be registered as extensions using the syntax described above. \section BlueBerryExtPointsIndex_Workbench_Keywords Keywords \subsection BlueBerryExtPointsIndex_Workbench_Keywords_Id Identifier \c org.blueberry.ui.keywords \subsection BlueBerryExtPointsIndex_Workbench_Keywords_Desc Description The keywords extension point defines keywords and a unique id for reference by other schemas. See \c propertyPages and \c preferencePages . \subsection BlueBerryExtPointsIndex_Workbench_Keywords_ConfMarkup Configuration Markup \code{.unparsed} \endcode - point: a fully qualified identifier of the target extension point - id: an optional identifier of the extension instance - name: an optional name of the extension instance \code{.unparsed} \endcode - id: The id is the unique id used to reference the keyword - label: The human readable label of the keyword \subsection BlueBerryExtPointsIndex_Workbench_Keywords_Examples Examples The following is an example of a keyword extension: \code{.unparsed} \endcode \subsection BlueBerryExtPointsIndex_Workbench_Keywords_SupplImpl Supplied Implementation Keywords are used only with preference and property pages. See the \c keywordReference element of the \c org.blueberry.ui.propertyPages and \c org.blueberry.ui.preferencePages extension points. \section BlueBerryExtPointsIndex_Workbench_PerspExt Perspective Extensions \subsection BlueBerryExtPointsIndex_Workbench_PerspExt_Id Identifier \c org.blueberry.ui.perspectiveExtensions \subsection BlueBerryExtPointsIndex_Workbench_PerspExt_Desc Description This extension point is used to extend perspectives registered by other plug-ins. A perspective defines the initial contents of the window action bars (menu and toolbar) and the initial set of views and their layout within a workbench page. Other plug-ins may contribute actions or views to the perspective which appear when the perspective is selected. Optional additions by other plug-ins are appended to the initial definition. \subsection BlueBerryExtPointsIndex_Workbench_PerspExt_ConfMarkup Configuration Markup \code{.unparsed} \endcode - point: a fully qualified identifier of the target extension point - id: an optional identifier of the extension instance - name: an optional name of the extension instance \code{.unparsed} \endcode - targetID: the unique identifier of the perspective (as specified in the registry) into which the contribution is made. If the value is set to "*" the extension is applied to all perspectives. \code{.unparsed} \endcode - id: the unique identifier of the action set which will be added to the perspective. \code{.unparsed} \endcode - id: the unique identifier of the view which will be added to the perspective's "Show View" submenu of the "Window" menu. \code{.unparsed} \endcode - id: the unique identifier of the perspective which will be added to the perspective's "Open Perspective" submenu of the "Window" menu. \code{.unparsed} \endcode - id: the unique identifier of the new wizard which will be added to the perspective's "New" submenu of the "File" menu. \code{.unparsed} \endcode - id: the unique identifier of the view which will be added to the perspective's "Show In..." prompter in the Navigate menu. \code{.unparsed} \endcode - id: the unique identifier of the view which will be added to the perspective layout. - relative: the unique identifier of a view which already exists in the perspective. This will be used as a reference point for placement of the view. The relationship between these two views is defined by \c relationship . Ignored if relationship is "fast". - relationship: specifies the relationship between \c id and \c relative . The following values are supported: - fast: the view extension will be created as a fast view. - stack: the view extension will be stacked with the relative view in a folder. - left, right, top, bottom: the view extension will be placed beside the relative view. In this case a \c ratio must also be defined. - ratio: the percentage of area within the relative view which will be donated to the view extension. If the view extension is a fast view, the ratio is the percentage of the workbench the fast view will cover when active. This must be defined as a floating point value and lie between 0.05 and 0.95. - visible: whether the view is initially visible when the perspective is opened. This attribute should have a value of "true" or "false" if used. If this attribute is not used, the view will be initially visible by default. - closeable: whether the view is closeable in the target perspective. This attribute should have a value of "true" or "false" if used. If this attribute is not used, the view will be closeable, unless the perspective itself is marked as fixed. - moveable: whether the view is moveable. A non-moveable view cannot be moved either within the same folder, or moved between folders in the perspective. This attribute should have a value of "true" or "false" if used. If this attribute is not used, the view will be moveable, unless the perspective itself is marked as fixed. - standalone: whether the view is a standalone view. A standalone view cannot be docked together with others in the same folder. This attribute should have a value of "true" or "false" if used. This attribute is ignored if the relationship attribute is "fast" or "stacked". If this attribute is not used, the view will be a regular view, not a standalone view (default is "false"). - showTitle: whether the view's title is shown. This attribute should have a value of "true" or "false" if used. This attribute only applies to standalone views. If this attribute is not used, the view's title will be shown (default is "true"). - minimized: If the perspective extension will result in a new view stack being created (i.e. the 'relationship' attribute is one of left, right, top or bottom) this field determines the new stack's initial display state. \code{.unparsed} \endcode - id: The unique identifier of the Command which is to be removed from the menu. WARNING: This is considered to be a 'Product level' extension and should not be used in consumable plugins without great care. \code{.unparsed} \endcode - id: The unique identifier of the Command which is to be removed from thetoolbar. WARNING: This is considered to be a 'Product level' extension and should not be used in consumable plugins without great care. \subsection BlueBerryExtPointsIndex_Workbench_PerspExt_Examples Examples The following is an example of a perspective extension (note the subelements and the way attributes are used): \code{.unparsed} \endcode In the example above, an action set, view shortcut, new wizard shortcut, and perspective shortcut are contributed to the initial contents of the Resource Perspective. In addition, the Package Explorer view is stacked on the Resource Navigator and the Type Hierarchy View is added beside the Resource Navigator. \section BlueBerryExtPointsIndex_Workbench_Persp Perspectives \subsection BlueBerryExtPointsIndex_Workbench_Persp_Id Identifier \c org.blueberry.ui.perspective \subsection BlueBerryExtPointsIndex_Workbench_Persp_Desc Description This extension point is used to add perspective factories to the workbench. A perspective factory is used to define the initial layout and visible action sets for a perspective. The user can select a perspective by invoking the "Open Perspective" submenu of the "Window" menu. \subsection BlueBerryExtPointsIndex_Workbench_Persp_ConfMarkup Configuration Markup \code{.unparsed} \endcode - point: a fully qualified identifier of the target extension point - id: an optional identifier of the extension instance - name: an optional name of the extension instance \code{.unparsed} \endcode - id: a unique name that will be used to identify this perspective. - name: a translatable name that will be used in the workbench window menu bar to represent this perspective. - class: a fully qualified name of the class that implements berry::IPerspectiveFactory interface. - icon: a relative name of the icon that will be associated with this perspective. - fixed: indicates whether the layout of the perspective is fixed. If true, then views created by the perspective factory are not closeable, and cannot be moved. The default is false. \code{.unparsed} \endcode An optional subelement whose body should contain text providing a short description of the perspective. \subsection BlueBerryExtPointsIndex_Workbench_Persp_Examples Examples The following is an example of a perspective extension: \code{.unparsed} \endcode \subsection BlueBerryExtPointsIndex_Workbench_Persp_SupplImpl Supplied Implementation The workbench provides a "Resource Perspective". Additional perspectives may be added by plug-ins. They are selected using the "Open Perspective" submenu of the "Window" menu. \section BlueBerryExtPointsIndex_Workbench_PrefPages Preference Pages \subsection BlueBerryExtPointsIndex_Workbench_PrefPages_Id Identifier \c org.blueberry.ui.preferencePages \subsection BlueBerryExtPointsIndex_Workbench_PrefPages_Desc Description The workbench provides one common dialog box for preferences. The purpose of this extension point is to allow plug-ins to add pages to the preference dialog box. When preference dialog box is opened (initiated from the menu bar), pages contributed in this way will be added to the dialog box. \subsection BlueBerryExtPointsIndex_Workbench_PrefPages_ConfMarkup Configuration Markup \code{.unparsed} \endcode - point: a fully qualified identifier of the target extension point - id: an optional identifier of the extension instance - name: an optional name of the extension instance \code{.unparsed} \endcode - id: a unique name that will be used to identify this page. - name: a translatable name that will be used in the UI for this page. - class: a name of the fully qualified class that implements berry::IWorkbenchPreferencePage. - category: a path indicating the location of the page in the preference tree. The path may either be a parent node ID or a sequence of IDs separated by '/', representing the full path from the root node. \code{.unparsed} \endcode A reference by a preference page to a keyword. See the keywords extension point. - id: The id of the keyword being referred to. \subsection BlueBerryExtPointsIndex_Workbench_PrefPages_Examples Examples The following is an example for the preference extension point: \code{.unparsed} \endcode \subsection BlueBerryExtPointsIndex_Workbench_PrefPages_SupplImpl Supplied Implementation The workbench adds several pages for setting the preferences of the platform. Pages registered through this extension will be added after them according to their category information. \section BlueBerryExtPointsIndex_Workbench_PresFact Presentation Factories \subsection BlueBerryExtPointsIndex_Workbench_PresFact_Id Identifier \c org.blueberry.ui.presentationFactories \subsection BlueBerryExtPointsIndex_Workbench_PresFact_Desc Description This extension point is used to add presentation factories to the workbench. A presentation factory defines the overall look and feel of the workbench, including how views and editors are presented. \subsection BlueBerryExtPointsIndex_Workbench_PresFact_ConfMarkup Configuration Markup \code{.unparsed} \endcode \code{.unparsed} \endcode - class: Specify the fully qualified class to be used for the presentation factory. The specified value must implement the interface berry::IPresentationFactory. - id: a unique name that will be used to identify this presentation factory - name: a translatable name that can be used to show this presentation factory in the UI \subsection BlueBerryExtPointsIndex_Workbench_PresFact_Examples Examples The following is an example of a presentationFactory extension: \code{.unparsed} \endcode \subsection BlueBerryExtPointsIndex_Workbench_PresFact_SupplImpl Supplied Implementation If a presentation factory is not specified or is missing then the implementation in berry::QtWorkbenchPresentationFactory will be used. \section BlueBerryExtPointsIndex_Workbench_Services Services \subsection BlueBerryExtPointsIndex_Workbench_Services_Id Identifier \c org.blueberry.ui.services \subsection BlueBerryExtPointsIndex_Workbench_Services_Desc Description Define service factories so that services can be contributed declaratively and will be available through berry::IServiceLocator::GetService(Class). The implementation of \c AbstractServiceFactory must be able to return a global service and multiple child services (if applicable). \subsection BlueBerryExtPointsIndex_Workbench_Services_ConfMarkup Configuration Markup \code{.unparsed} \endcode Contribute services to the workbench. \code{.unparsed} \endcode -Match a service interface to a factory that can supply a hierachical implementation of that service. +Match a service interface to a factory that can supply a hierarchical implementation of that service. - factoryClass: The factory that extends \c AbstractServiceFactory and can create the implementation for the \c serviceClass. \code{.unparsed} \endcode A service this factory can provide. - serviceClass: The interface that represents a service contract. \code{.unparsed} \endcode A Source Provider supplies source variables to the IEvaluationService. It can also notify the IEvaluationService when one or more of the variables change. - provider: This class must provide variables and call the appropriate \c fireSourceChanged(*) method when any of the variables change. \code{.unparsed} \endcode A source variable from this provider. A source provider must declare all variables that it provides. - name: The name of a contributed source variable. It is a good practice to prepend the plugin id to the variable name to avoid collisions with other source providers. - priorityLevel: For conflict resolution used by services like the \c IHandlerService , contributed source variables must assign a priority. workbench is the global default priority. See \c ISources for relative priority information. \subsection BlueBerryExtPointsIndex_Workbench_Services_Examples Examples Here is a basic definition: \code{.unparsed} \endcode The LevelServiceFactory can return an ILevelService when it is requested from the IServiceLocator: \code{.unparsed} berry::ILevelService::Pointer s = GetSite()->GetService(my::ILevelService::GetStaticClassName()); std::cout << s->GetLevel(); \endcode In this test example, the factory would instantiate three \c ILevelService implementations during the first call to \c GetSite()->GetService(*) . The global one in the workbench, one for the workbench window, and one for the site. \section BlueBerryExtPointsIndex_Workbench_Tweak Tweaklets \subsection BlueBerryExtPointsIndex_Workbench_Tweak_Id Identifier \c org.blueberry.ui.tweaklets \subsection BlueBerryExtPointsIndex_Workbench_Tweak_Desc Description Typically, although not required, the value of the \c definition attribute is the fully qualified name without colons of an abstract class defined by the workbench, and the value of the \c implementation attribute is the fully qualified name of a non-abstract class provided by the extending plug-in. \subsection BlueBerryExtPointsIndex_Workbench_Tweak_ConfMarkup Configuration Markup \code{.unparsed} \endcode \code{.unparsed} \endcode - id: a unique name that will be used to identify this tweaklet - name: a translatable name that will be used in the UI for this tweaklet - description: a translatable short description of this tweaklet, to be used in the UI - definition: an identifier of the tweaklet definition in the workbench, typically a fully qualified type name without colons - implementation: an identifier of the tweaklet implementation provided by the extender, typically a fully qualified class name \subsection BlueBerryExtPointsIndex_Workbench_Tweak_SupplImpl Supplied Implementation Tweaklets are usually used to specialize the workbench for a specific GUI toolkit. \section BlueBerryExtPointsIndex_Workbench_Views Views \subsection BlueBerryExtPointsIndex_Workbench_Views_Id Identifier \c org.blueberry.ui.views \subsection BlueBerryExtPointsIndex_Workbench_Views_Desc Description This extension point is used to define additional views for the workbench. A view is a visual component within a workbench page. It is typically used to navigate a hierarchy of information (like the workspace), open an editor, or display properties for the active editor. The user can make a view visible from the Window > Show View menu or close it from the view local title bar. In order to reduce the visual clutter in the Show View Dialog, views should be grouped using categories. \subsection BlueBerryExtPointsIndex_Workbench_Views_ConfMarkup Configuration Markup \code{.unparsed} \endcode - point: a fully qualified identifier of the target extension point - id: an optional identifier of the extension instance - name: an optional name of the extension instance \code{.unparsed} \endcode - id: a unique name that will be used to identify this category - name: a translatable name that will be used in the UI for this category - parentCategory: an optional path composed of category IDs separated by '/'. This allows the creation of a hierarchy of categories. \code{.unparsed} \endcode - id: a unique name that will be used to identify this view - name: a translatable name that will be used in the UI for this view - category: an optional attribute that is composed of the category IDs separated by '/'. Each referenced category must be declared in a corresponding category element. - class: a fully qualified name of the class that implements berry::IViewPart. A common practice is to subclass berry::ViewPart or berry::QtViewPart in order to inherit the default functionality. - internal: a hint if the view should be considered internal, e.g., in enumerations of views in the UI. - icon: a relative name of the icon that will be associated with the view. - fastViewWidthRatio: the percentage of the width of the workbench that the view will take up as an active fast view. This must be defined as a floating point value and lie between 0.05 and 0.95. If no value is supplied, a default ratio will be used. - allowMultiple: flag indicating whether this view allows multiple instances to be created using IWorkbenchPage::ShowView(QString id, QString secondaryId). The default is false. - restorable: flag indicating whether this view allows to be restored upon workbench restart. If set to false, the view will not be open after a workbench restart. The default is true. \code{.unparsed} \endcode An optional subelement whose body should contain text providing a short description of the view. \code{.unparsed} \endcoe A sticky view is a view that will appear by default across all perspectives in a window once it is opened. Its initial placement is governemed by the location attribute, but nothing prevents it from being moved or closed by the user. Use of this element will only cause a placeholder for the view to be created, it will not show the view. -Please note that usage of this element should be done with great care and should only be applied to views that truely have a need to live across perspectives. +Please note that usage of this element should be done with great care and should only be applied to views that truly have a need to live across perspectives. - id: the id of the view to be made sticky. - location: optional attribute that specifies the location of the sticky view relative to the editor area. If absent, the view will be docked to the right of the editor area. -- closeable: optional attribute that specifies wether the view should be closeable. If absent it will be closeable. -- moveable: optional attribute that specifies wether the view should be moveable. If absent it will be moveable. +- closeable: optional attribute that specifies whether the view should be closeable. If absent it will be closeable. +- moveable: optional attribute that specifies whether the view should be moveable. If absent it will be moveable. \subsection BlueBerryExtPointsIndex_Workbench_Views_Examples Examples The following is an example of the extension point: \code{.unparsed} \endcode The following is an example of a sticky view declaration: \code{.unparsed} \endcode \subsection BlueBerryExtPointsIndex_Workbench_Views_SupplImpl Supplied Implementation The BlueBerry Platform provides a number of standard views. From the user point of view, these views are no different from any other view provided by the plug-ins. All the views can be shown from the "Show View" submenu of the "Window" menu. The position of a view is persistent: it is saved when the view is closed and restored when the view is reopened in a single session. The position is also persisted between workbench sessions. */ diff --git a/Documentation/Doxygen/3-DeveloperManual/Concepts/Annotation.dox b/Documentation/Doxygen/3-DeveloperManual/Concepts/Annotation.dox index b75fbd7a25..2486231383 100644 --- a/Documentation/Doxygen/3-DeveloperManual/Concepts/Annotation.dox +++ b/Documentation/Doxygen/3-DeveloperManual/Concepts/Annotation.dox @@ -1,51 +1,51 @@ /** \page AnnotationPage Annotation Concept \tableofcontents \section AnnotationPage_Introduction Annotations The annotations in MITK are a simple way to display additional information on the render windows. A class, deriving from mitk::Annotation represents an arbitrary 2D or 3D object that can be rendered as an Annotation. This can for example be used for the annotation of 3D points or to Annotation despriptions in the window corners. Instances of the MicroService mitk::AbstractAnnotationRenderer are used to add the annotations to the renderwindows, updating them and depending on their implementation, organize them in a layout. This module contains implementations for mitk::AbstractAnnotationRenderer as well as mitk::Annotation. Currently, the following features are realized within the Annotation module.
  1. 2D and 3D textelements are already defined in the Annotation module and are using VTK to create custom annotations.
  2. 2D and 3D annotations can be placed freely by providing a display position
  3. 2D annotations can be placed in a layout, which organizes the annotations in the display corners.
\section AnnotationPage_ArchitectureSection General Architecture The mitk::Annotation can be implemented using a custom rendering framework like VTK. In this diagram, the vtkAnnotation is shown as the superclass for all Annotations which use the vtk framework for rendering. The AnnotationManager can be registered to several BaseRenderer instances in order to call the update method of each Annotation during the rendering phase of the renderer. It also manages the respective Layouters which are used to manage the placement of a group of Annotations. \subsection AnnotationPage_AnnotationSubsection Annotation The mitk::Annotation is an abstract class that can manage property lists like the mitk::DataNode and provides the interfaces to the methods AddToBaseRenderer, AddToRenderer, RemoveFromBaseRenderer RemoveFromRenderer and Update. The subclasses of the mitk::Annotation have to implement these methods -in order to provide the functionallity of an Annotation. There are already a few implementations of mitk::Annotation which are using VTK as a rendering +in order to provide the functionality of an Annotation. There are already a few implementations of mitk::Annotation which are using VTK as a rendering framework to display the Annotations. In order to show an Annotation, it has to be registered as a MicroService. \subsection AnnotationPage_AnnotationRendererSubsection AbstractAnnotationRenderer The AbstractAnnotationRenderer is the base class for all types of AnnotationRenderers, which are used for the management of multiple annotations in a specific BaseRenderer. -For each BaseRenderer, an AnnotationRenderer is registerered as a MicroService, using the type and the renderer name as a unique identifier. This way it is possible to keep the Annotations for a specific BaseRenderer -even if this renderer is temporarilly not existent (e.g. when a RenderWindow was closed). +For each BaseRenderer, an AnnotationRenderer is registered as a MicroService, using the type and the renderer name as a unique identifier. This way it is possible to keep the Annotations for a specific BaseRenderer +even if this renderer is temporarily not existent (e.g. when a RenderWindow was closed). Implementations of the AbstractAnnotationRenderer are using the us::ServiceTracker in order to manage all registered Annotations and to listen to the events which are thrown if a new Annotation is registered, if an Annotation was unregistered or if it was modified. \subsubsection AnnotationPage_ManualPlacementAnnotationRendererSubsection ManualPlacementAnnotationRenderer Using the ManualPlacementAnnotationRenderer, allows for a simple placement of Annotations. The Annotation manages its placement internally or using the Position2D / Position3D properties. \subsubsection AnnotationPage_LayoutAnnotationRendererSubsection LayoutAnnotationRenderer The LayoutAnnotationRenderer allows automatic placement in the RenderWindow corners and sorts the Annotations by a specified priority. \subsection AnnotationPage_AnnotationUtilsSubsection AnnotationUtils mitk::AnnotationUtils is a collection of static convenience functions, for the registration of AnnotationRenderers and to request certain registered Annotations. */ diff --git a/Documentation/Doxygen/3-DeveloperManual/Concepts/BasicDataTypes.dox b/Documentation/Doxygen/3-DeveloperManual/Concepts/BasicDataTypes.dox index fec4b1577a..b469fdc6b2 100644 --- a/Documentation/Doxygen/3-DeveloperManual/Concepts/BasicDataTypes.dox +++ b/Documentation/Doxygen/3-DeveloperManual/Concepts/BasicDataTypes.dox @@ -1,60 +1,60 @@ /** \page BasicDataTypesPage Numeric MITK data types and their usage. This page describes how to use very foundational data-tyes in MITK like mitk::Vector, mitk::Point and mitk::Matrix and how they can interact. \tableofcontents \section Structure Structure The previously known, monolythic structure of putting every basic type into mitkVector.h has been broken and mitkVector.h has been split up into several, more atomic files, namely: -# mitkNumericConstants.h : contains basic constants like mitk::ScalarType or mitk::eps -# mitkArray.h : copy itk::FixedArrays (like itk::Point and itk::Vector) from and to types which implement the [] operator (array-types), like e.g. Plain Old Datatypes (POD) -# mitkPoint.h : the mitk::Point class. This is basically the itk::Point with the added ToArray and Fill members to conveniently copy from and to array-types. In MITK, a point is considered a fixed geometric location and thus cannot be summed or multiplied. --# mitkVector.h : the mitk::Vector class. This is an itk::Vector, but with the possiblity to implicitly convert to vnl_vector and vnl_vector_fixed. In MITK, vectors denote directions and can be summed or multiplied with scalars. +-# mitkVector.h : the mitk::Vector class. This is an itk::Vector, but with the possibility to implicitly convert to vnl_vector and vnl_vector_fixed. In MITK, vectors denote directions and can be summed or multiplied with scalars. -# mitkMatrix.h : the mitk::Matrix class. This is an itk::Matrix with the added ToArray and Fill members to conveniently copy from and to array-types. -# mitkQuaternion.h : a typedef to vnl_quaternion. -# mitkAffineTransform3D.h : a typedef to itk::ScalableAffineTransform -# mitkNumericTypes.h : this file includes all of the above as a convenience header The Equal methods to compare Points, Vectors, Matrices, ... have been moved into the respective files. E.g., if you want to compare two vectors simply use the Equal method provided by mitkVector.h. \section Conversion Conversion between the data-types If you want to convert a mitk::Vector from or to a vnl_vector or a vnl_vector_fixed, simply write \code mitkVector3D = vnlVector3D; vnlVector3D_2 = mitkVector3D; \endcode Unfortunately this mechanism couldn't be implemented to every type of conversion. But in any case, the ToArray and FillVector/FillPoint/FillMatrix member functions can be used to convert to array-types. E.g., \code cv::Vec3d cvVec3D; mitkVector3D.ToArray(cvVec3D); mitkVector3D_2.FillVector(cvVec3D); \endcode No implicit conversion from mitk::Point to mitk::Vector was implemented as this would break with itk's concept of separating points and vectors. If you want to convert, use: \code mitkVector3D = mitkPoint3D.GetVectorFromOrigin(); mitkPoint3D_2 = mitkVector3D; \endcode more examples of how to convert between data types can be found in the tests: -# mitkArrayTypeConversionTest.cpp -# mitkPointTypeConversionTest.cpp -# mitkVectorTypeConversionTest.cpp -# mitkMatrixTypeConversionTest.cpp */ diff --git a/Documentation/Doxygen/3-DeveloperManual/Concepts/DICOMTesting.dox b/Documentation/Doxygen/3-DeveloperManual/Concepts/DICOMTesting.dox index c9cacceb51..d75141f172 100644 --- a/Documentation/Doxygen/3-DeveloperManual/Concepts/DICOMTesting.dox +++ b/Documentation/Doxygen/3-DeveloperManual/Concepts/DICOMTesting.dox @@ -1,154 +1,154 @@ /** \page DICOMTesting MITK DICOM testing \section DICOMTesting_introduction Introduction Reading DICOM data into mitk::Images is a complicated business since DICOM and MITK have very different ideas of images. DICOMITKSeriesGDCMReader (formerly: DicomSeriesReader) brings DICOM and MITK as close together as possible by offering methods to load DICOM CT and MR images into mitk::Images, optionally grouping slices to 3D+t images. Since there are so many possible sources for mistakes with any change to this loading process, testing the many -assumptions implemented in DICOMITKSeriesGDCMReader is worthwhile. This document describes what and how theses kind of tests are implemented. +assumptions implemented in DICOMITKSeriesGDCMReader is worthwhile. This document describes what and how these kind of tests are implemented. \section DICOMTesting_problem Problem description The task of loading DICOM files into mitk::Images is a challenge because of major differences in the way that DICOM and MITK represent images: - DICOM images + are mostly stored as one slice per file - + do not describe how they can be arraged into image stacks with orthogonal axis + + do not describe how they can be arranged into image stacks with orthogonal axis + sometimes they cannot be arranged in image stacks as described above (e.g. tilted gantry) - mitk::Image (at least its mature areas) + represents image stacks with orthogonal axis (nothing like a tilted gantry) + have a concept of a fourth dimension (time steps) Because image processing applications based on MITK still need a way to load DICOM images into mitk::Image objects, MITK needs a way to build image stacks and this needs to be well tested. For more background information, see David Clunie's most valuable posts on comp.protocols.dicom, e.g.: - https://groups.google.com/g/comp.protocols.dicom/c/bbkZcuFh8NQ - https://groups.google.com/g/comp.protocols.dicom/c/6b0Ul76j5ms - https://groups.google.com/g/comp.protocols.dicom/c/XYC7C3-vy4E - https://groups.google.com/g/comp.protocols.dicom/c/RWhjXgg6P7o \section DICOMTesting_testidea Test principle The general idea for DICOM loaing tests is to run a set of known DICOM files through DICOMITKSeriesGDCMReader's methods GetSeries() and LoadDicomSeries() to generate mitk::Images. These images are then compared to expected image properties, such as the number of individual mitk::Images, positions, orientations, spacings, etc. Stored expectations look like this (should be self-explanatory): \verbatim -- Image 1 Pixeltype: s BitsPerPixel: 16 Dimension: 4 Dimensions: 64 64 6 1 Geometry: Matrix: 5.25 0 0 0 5.2468 0.139598 0 -0.183222 3.99757 Offset: -159.672 -309.974 -69.0122 Center: 0 0 0 Translation: -159.672 -309.974 -69.0122 Scale: 1 1 1 Origin: -159.672 -309.974 -69.0122 Spacing: 5.25 5.25 4 TimeBounds: 0 1 -- Image 2 Pixeltype: s BitsPerPixel: 16 Dimension: 4 Dimensions: 64 64 41 1 Geometry: Matrix: 5.25 0 0 0 5.25 0 0 0 4 Offset: -160.672 -311.672 -285 Center: 0 0 0 Translation: -160.672 -311.672 -285 Scale: 1 1 1 Origin: -160.672 -311.672 -285 Spacing: 5.25 5.25 4 TimeBounds: 0 1 \endverbatim \section DICOMTesting_implementation Implementation \section DICOMTesting_implementation_utils Test helpers (applications and classes) Application DumpDICOMMitkImage Takes a list of DICOM images, loads them using TestDICOMLoading, then dumps information about the resulting mitk::Images to standard output. This application is helpful when defining reference data for tests. Application VerifyDICOMMitkImageDump Takes a list of DICOM images and loads them using TestDICOMLoading. Takes a dump file as generated by DumpDICOMMitkImage, parses it and compares it to actually generated mitk::Images. This application is used to implement the majority of test cases. They all load images, then verify the expected result structure. Class TestDICOMLoading \section PageDICOMLoadingTests_testcaseimplementation Test case implementation Individual test cases are stored in the MITK-Data repository and constructed by Core/Code/Testing/DICOMTesting/Testing/CMakeLists.txt The CMake file parses given directories for subdirectories containing specific test cases. Each directory contains two files: - File "input": lists DICOM files that should be loaded for this test case - File "expected.dump": contains the image properties in the above mentioned dump format Each test case is translated into a CTest test which evaluates the return value of a call to VerifyDICOMMitkImageDump. \section PageDICOMLoadingTests_testcases Implemented test cases From test set \b TinyCTAbdomen (see description.txt in this directory for details on test images): - singleslice : just a single slice (should work and contain meaningful spacing) - two_slices : two slices, spacing should be calculated correctly - all : load a "normal" series as a single 3D block - 3D_and_T : load a small set of slices with multiple time-steps - diff_orientation : load a set of files containing two differently oriented image blocks; at least two images (110,111) have minor errors in small decimal digits - diff_orientation_gaps : load a set of files containing two differently oriented image blocks, each missing slices, so blocks must be split - - diff_spacing : load a set of files containint two set of slices with different spacings + - diff_spacing : load a set of files containing two set of slices with different spacings - gap : load slices that cannot form a single 3D block, because single slices are missing - - gaps : slices missing in differnt locations, so multiple splits needed + - gaps : slices missing in different locations, so multiple splits needed - unsorted_gaps : as before, just file names are unsorted - single_negative_spacing : from reported bug related to single MR images with misleading spacing information - tilted_gantry : slice origins do not align along first slice normal (happens with tilted gantries) - interleaved : two volumes of slices with origins along the same line. The volumes' slices interleave in their border region. This test is meant to correctly sort apart the two blocks instead of generating many two-slices groups in the interleaved region. - CR-MONO1-10-chest-spacing-none : CR image without spacing information - (1.0, 1.0) should be assumed - CR-MONO1-10-chest-spacing-pixelspacing : CR image with only "Pixel Spacing" tag - this should be used as spacing - CR-MONO1-10-chest-spacing-imagerpixelspacing : CR image with only "Imager Pixel Spacing" tag - this should be used as spacing - CR-MONO1-10-chest-spacing-calibrated : CR image with BOTH "Imager Pixel Spacing" and "Pixel Spacing" defined - "Pixel Spacing" should be used - OT-MONO2-8-colon : Secondary Capture image with gray value pixels - should be loaded - OT-PAL-8-face : Secondary Capture image with PALETTE pixel values - can be loaded, but display is not correct yet From test set \b TiltHead (see description.txt in this directory for details on test images): - head_ct_tilt : load a series with gantry tilt and check its orientation \section DICOMTesting_othertests Specific other tests This list is meant to provide an up-to-date list of all implemented DICOM loading tests. If you ever find this outdated, please update it or make the persons who invalidated the list update it. mitkDICOMTestingSanityTest_* These tests implement basic testing of the implemented helper classes. The tests use DICOMITKSeriesGDCMReader to load a number of DICOM image. They verify: - DICOMITKSeriesGDCMReader recognizes all input files as DICOM images - DICOMITKSeriesGDCMReader generates a number of mitk::Images from the DICOM images - the number of mitk::Images matches a number given on the command line or CTest's add_test() - helper methods in class TestDICOMLoading make minimal sense (comparison of an image dump to itself must be valid) */ diff --git a/Documentation/Doxygen/3-DeveloperManual/Concepts/DataInteraction.dox b/Documentation/Doxygen/3-DeveloperManual/Concepts/DataInteraction.dox index 374ae959a6..f69eec4d22 100644 --- a/Documentation/Doxygen/3-DeveloperManual/Concepts/DataInteraction.dox +++ b/Documentation/Doxygen/3-DeveloperManual/Concepts/DataInteraction.dox @@ -1,230 +1,230 @@ /** \page DataInteractionPage Interaction Concepts \tableofcontents \section DataInteractionPage_Introduction Introduction to Interaction in MITK Interaction is a very important task in medical image processing software. Therefore MITK provides a special interaction concept that provides the developer with an easy way to develop and maintain user interaction separately from the algorithms processing the input. This allows e.g. for common interaction schemes to be re-used in different contexts. The core of the interaction concept is based on entities called \b DataInteractors that listen for certain pre-defined events and execute actions when such an event is triggered.\n In the following the different components of the interaction concept are explained. First a a high-level overview about how the different components interact is given, then some parts are explained in more detail. \subsection FurtherReadingInteraction Topics related to interaction - further information: See the \ref DataInteractionTechnicalPage page for a more technical explanation. \n Consult \ref HowToUseDataInteractor for usage information.\n See \ref SectionImplementationDataInteractor for an example on how to implement a new mitk::DataInteractor \n for information about how to create new events refer to ImplementNewEventsPage.\n -The documentation of the depricated former concept can be found at \ref InteractionPage. +The documentation of the deprecated former concept can be found at \ref InteractionPage. \n For a list of changes with respect to the previous interaction concept please refer to the \ref InteractionMigration \section DataInteractionPage_HandlingSection Event Handling & GUI Toolkit Abstraction The following sequence diagram gives an exemplary overview of the process from creating an event until executing an action in the mitk::DataInteractor. This diagram assumes the usage of the Qt framework, but also shows that the interaction concept itself is implemented independent of any specific graphical user interface toolkit. \imageMacro{event_handling.png,"",16}
  1. a user event is triggered and send to MITK
  2. this layer serves as an adapter from the GUI toolkit (here Qt) events to MITK internal events (later referred to as \link mitk::InteractionEvent InteractionEvents\endlink).
  3. once the event is adapted it is send to a mitk::Dispatcher, which is linked to a render window, to be handled.
  4. on the mitk::Dispatcher level all objects are known that can react to incoming events (mitk::DataInteractor and mitk::InteractionEventObserver instances)
  5. a mitk::DataInteractor is offered an event and checks its mitk::EventConfig object, which returns if a variant of this event has been defined for this DataInteractor.
  6. if the DataInteractor has a variant for the event, it consults its state machine to check if the input can be handled in the current state
  7. the actions associated with a state change (transition) are executed and the event is successfully handled.
\section DataInteractionPage_EventPage Events Events can describe any sort of user input, such as key strokes, mouse clicks or touch gestures. These events are mapped from an UI framework like Qt to an MITK internal representation and send to the mitk::Dispatcher which in turn deals with further processing of the event. These events are not limited to classical input devices but can be extended at will, by introducing new classes which e.g. describe events from tracking devices, etc. Refer to \subpage ImplementNewEventsPage to see how new events and thereby input devices can be integrated. For an overview of available Events see mitk::InteractionEvent, for on overview of parameters see the \subpage DataInteractionTechnicalPage. \section DataInteractionPage_InteractionEventHandlerSection InteractionEventHandler Is the term describing objects in general that can handle events. These objects can be divided into two groups, namely \link mitk::DataInteractor DataInteractors\endlink and mitk::InteractionEventObserver. Their difference is that mitk::DataInteractor instances are linked with a mitk::DataNode which they manipulate, whereas mitk::InteractionEventObserver instances do not have a mitk::DataNode and therefore are not supposed to manipulate any data. \dot digraph linker_deps { node [shape=record, fontname=Helvetica, fontsize=10]; a [ label="InteractionEventHandler" ]; d [ label="{EventStateMachine|HandleEvent()}" ]; b [ label="{DataInteractor|PerformAction()}" ]; a -> d; d -> b; } \enddot \subsection DataInteractionPage_DataInteractorsSection DataInteractors -DataInteractors are specialized mitk::InteractionEventHandler which handle events for one spefific DataNode. They are implemented following a concept called state machines +DataInteractors are specialized mitk::InteractionEventHandler which handle events for one specific DataNode. They are implemented following a concept called state machines (see e.g. Wikipedia ). \subsubsection DataInteractionPage_StateMachinesSection StateMachines A specific events action is usually desired to depend on the content of the data object and the state of the interaction. For example when adding a line by clicking with the mouse, the first two clicks are supposed to add a point. But the second click should additionally finish the interaction and a subsequent third click should be ignored. State machines provide a great way to model such interaction in which the same user interaction can trigger different actions depending on the current state. Therefore DataInteractors work with so called state machine patterns. The basic idea here is that each interaction can be described by states and transitions which in turn trigger actions. These patterns define a workflow and different patterns can be applied to the same mitk::DataInteractor and cause this mitk::DataInteractor to perform different user interactions. This principle is best described by an example. Imagine a mitk::DataInteractor with the functionality (1) to add Points at a given mouse position and connect them by a line and (2) check if two points are on the same position. Using this mitk::DataInteractor, different mitk::StateMachine patterns/descriptions can be given which each cause the mitk::DataInteractor to perform different interaction schemes. State machine pattern 1: We want the user to draw a line. A simple state machine could express this by three states like this: \dot digraph linker_deps { node [shape=circle, fontname=Helvetica, fontsize=10]; a [ label="NoPoints" ]; b [ label="OnePoint" ]; c [ label="TwoPoints" ]; a -> b [label="MousePress/AddPoint",fontname=Helvetica, fontsize=10]; b -> c [label="MousePress/AddPoint",fontname=Helvetica, fontsize=10]; { rank=same; a b c } } \enddot With each MousePress event the AddPoint function is called and adds a point at the mouse position, unless two points already exist. State machine pattern 2: The same mitk::DataInteractor can also operate after the following state machine, which models the interaction to input a closed contour. The mitk::DataInteractor can detect an AddPoint event on an already existing point and will trigger a PointsMatch event. \dot digraph { node [shape=circle, fontname=Helvetica, fontsize=10]; a [ label="StartState" ]; b [ label="ClosedContour"]; a -> a [label="MousePress/AddPoint",fontname=Helvetica, fontsize=10]; a -> b [label="PointsMatch/AddPoint",fontname=Helvetica, fontsize=10]; } \enddot In this way state machines provide both, a nice and structured way to represent interaction tasks and description of the interaction which is separated from the code. One DataInteractor can be re-used for different tasks by simply exchanging the state machine pattern. These patterns are described in XML files. \subsubsection DataInteractionPage_DefinitionStateMachine Definition of a State Machine The definition is made up out of four components.
  • States - represent the current status of the interaction
  • Transitions - describe the events needed to change from one state to another
  • Conditions - are executed, before a transition is taken
  • Actions - are executed, when a transition is taken and conditions for that transition have passed
Each state machine needs exactly one designated start state into which the state machine is set in the beginning. An example of a state machine describing the interaction of example 2 looks like this: \code \endcode Example 1: State machine pattern, that describes adding points to a contour until the PointsMatch event is triggered. For a more detailed description of state machine patterns see here. \subsection DataInteractionPage_InteractionEventObserverSection InteractionEventObserver mitk::InteractionEventObserver instances are objects which will receive all user input and are intended for observation only, they should never modify any DataNodes. For mitk::InteractionEventObserver it is optional to use the state machine functionality, the default is without. How to use the state machine functionality is described in the documentation of mitk::InteractionEventObserver::Notify. \dot digraph event_observer { node [shape=record, fontname=Helvetica, fontsize=10]; c [ label="{InteractionEventObserver|Notify()}" ]; a [ label="InteractionEventHandler" ]; b [ label="{EventStateMachine|HandleEvent()}" ]; d [ label="{MyCustomObserver|PerformAction()}" ]; c -> d; a -> b; b -> d [style="dashed",label="optional"]; } \enddot \subsection DataInteractionPage_ConfigurationSection Configuration In a lot of cases it is preferable to implement interactions independent of a specific event (e.g. left click with mouse), such that it is possible to easily change this. This is achieved through configuration of \link mitk::InteractionEventHandler InteractionEventHandlers\endlink. This allows to change the behavior at runtime. The mitk::InteractionEventHandler class provides an interface to easily modify the user input that triggers an action by loading a different configuration. This allows to implement user-specific behavior of the software on an abstract level and to switch it at runtime. This is achieved through XML files describing a configuration. These files can be loaded by the mitk::InteractionEventHandler and will lead to an internal mapping from specific user input to an abstract description of the event given in the config file. In order to do this we distinguish between a specific event and an event variant. A specific event is described by its event class, which determines the category of an event, e.g. the class mitk::MousePressEvent, and its parameter which make this event unique, e.g. LeftMouseButton pressed and no modifier keys pressed. The event variant is a name that is assigned to a specific event, and to which an mitk::InteractionEventHandler listens. To illustrate this, an example is given here for two different configuration files. We assume that a mitk::InteractionEventHandler listens to the event variant 'AddPoint', two possible config files could then look like this: \code \endcode Example 2: Event description of a left click with the mouse and \code \endcode Example 3: Event description of a left click with the mouse while pressing the shift-key If the mitk::InteractionEventHandler is loaded with the first configuration the event variant 'MousePress' is triggered when the user performs a mouse click, while when the second configuration is loaded 'MousePress' is triggered when the user performs a right click while pressing the shift button. In this way all objects derived from mitk::InteractionEventHandler can be configured. For a detailed description about how to create the XML file see the \ref DataInteractionTechnicalPage page. \section DataInteractionPage_DispatcherSection Dispatcher Instances of mitk::Dispatcher receive all events and distribute them to their related mitk::DataInteractor instances. This is done by ordering the DataInteractors according to the layer of their mitk::DataNode in descending order. Then the event is offered to the first mitk::DataInteractor, which in turn checks if it can handle the event. This is done for each mitk::DataInteractor until the first processes the event, after this the other DataInteractors are skipped and all InteractionEventObservers are notified. \section DataInteractionPage_Examples Examples Examples can be found at \subpage InteractionHowTo */ diff --git a/Documentation/Doxygen/3-DeveloperManual/Concepts/Exceptions.dox b/Documentation/Doxygen/3-DeveloperManual/Concepts/Exceptions.dox index 53e2c839eb..f796e2bafc 100644 --- a/Documentation/Doxygen/3-DeveloperManual/Concepts/Exceptions.dox +++ b/Documentation/Doxygen/3-DeveloperManual/Concepts/Exceptions.dox @@ -1,90 +1,90 @@ /** \page ExceptionPage Error Handling and Exception Concept \tableofcontents \section ExceptionHandling General Exception Handling In MITK, errors during program execution are handled by the well known exception handling concept which is part of the C++ language. In case of unexpected exceptional behaviour or errors during program execution MITK classes throw exceptions. MITK exceptions are always objects of the class mitk::Exception or of one of its subclasses. \subsection Throw Throwing of exceptions Exceptions should always be thrown by using the predefined exception macros. If you want to throw a mitk::Exception in your code, simply use the following macro. \verbatim //This command will throw a mitk::Exception and add a message. //The macro will also add filename and line number to the exception //object. mitkThrow() << "Here comes your exception message"; \endverbatim You can also stream more complex messages, e.g. adding integers or other variables to your exception messages, like shown in the following example. \verbatim mitkThrow() << "This time we show the values of some variables:" << m_MyObject->GetSize() << m_MyInteger; \endverbatim \subsection Doc Documentation of exceptions If you throw exceptions in your code, please also document this in your doxygen comments by using the "@throws " tag. This will help users of your class to catch and handle the exceptions in a proper way. An example how to document exception throwing is given below. \verbatim class myExampleClass { /** Documentation * @brief This method does [...] - * @throws mitk::Exception This exception is thrown, when the following error occures: [...] + * @throws mitk::Exception This exception is thrown, when the following error occurs: [...] */ void MyExampleMethod() { //here comes your code //here happens an exception - mitkThrow() << "An exception occured because [...], method can't continue."; + mitkThrow() << "An exception occurred because [...], method can't continue."; } } \endverbatim In general, exceptions emit no logging messages by default because they are intended to be catched by overlying classes. This classes should then decide what to do, e.g. to log an error message or handle the exception in another way. See the logging documentation for more details on error logging. \subsection Catch Catching exceptions Exceptions should be caught by overlying classes, if they can handle them in a proper way. Catching exceptions is very simple, use the standard try-catch block to do so. An example is given below. \verbatim try { //call of a method which may throw an exception myObject->MyExampleMethod(); } catch (const mitk::Exception& e) { //This code is executed if an exception of the given type was thrown above. //For example log an error message here or do some other proper handling of the exception. } \endverbatim -Please do not use "catch (...)" because normally your class can't garantee to handle all exceptions in a proper way without differentiating them. +Please do not use "catch (...)" because normally your class can't guarantee to handle all exceptions in a proper way without differentiating them. \section SpecializedExceptionHandling Defining and Using more Specialized Exceptions -The basic MITK exception concept was kept very simple and should suffice in many cases. But you can also use more specialized exceptions, if needed. Nevertheless all MITK exceptions should be subclasses of mitk::exception. You can define your own exception classes by simply implementing new classes which derive from mitk::exception. Thus, you can catch your exception seperately when needed. The mitkExceptionClassMacro helps to keep implementing new exception classes as simple as possible, like shown in the following code example. +The basic MITK exception concept was kept very simple and should suffice in many cases. But you can also use more specialized exceptions, if needed. Nevertheless all MITK exceptions should be subclasses of mitk::exception. You can define your own exception classes by simply implementing new classes which derive from mitk::exception. Thus, you can catch your exception separately when needed. The mitkExceptionClassMacro helps to keep implementing new exception classes as simple as possible, like shown in the following code example. \verbatim #include class mitk::MySpecializedException : public mitk::Exception { public: mitkExceptionClassMacro(mitk::MySpecializedException,mitk::Exception); }; \endverbatim -To throw your specialized exception you should use the corresponing macro, which is shown in the next code snippet. +To throw your specialized exception you should use the corresponding macro, which is shown in the next code snippet. \verbatim mitkThrowException(mitk::MySpecializedException) << "this is error info"; \endverbatim */ diff --git a/Documentation/Doxygen/3-DeveloperManual/Concepts/GeometryOverview.dox b/Documentation/Doxygen/3-DeveloperManual/Concepts/GeometryOverview.dox index f7bbcb984b..efe242ee3c 100644 --- a/Documentation/Doxygen/3-DeveloperManual/Concepts/GeometryOverview.dox +++ b/Documentation/Doxygen/3-DeveloperManual/Concepts/GeometryOverview.dox @@ -1,160 +1,160 @@ /** \page GeometryOverviewPage Geometry Overview \tableofcontents \section GeometryOverviewPage_Introduction Introduction to Geometries Geometries are used to describe the geometrical properties of data objects in space and time. To use the geometry classes in the right way you have to understand the three different coordinate types present in MITK: \imageMacro{CoordinateTypes.png,"",16} \section GeometryOverviewPage_CoordTypes The different coordinate types \subsection GeometryOverviewPage_WorldCoord -- World coordinates are describing the actual spacial position of all MITK objects regarding a global coordinate system, normally specified by the imaging modality +- World coordinates are describing the actual spatial position of all MITK objects regarding a global coordinate system, normally specified by the imaging modality - World coordinates are represented by mitk::Point3D objects. - The geometry defines the offset, orientation, and scale of the considered data objects in reference to the world coordinate systems. - World coordinates are always measured in mm - If you are dealing with an image geometry, the origin of an image is pointing to the CENTER of the bottom-left-back voxel. - If you are NOT dealing with an image geometry (no defined discrete Voxels), the origin is pointing to the bottom-left-back CORNER - Index coordinates can be converted to world coordinates by calling mitk::BaseGeometry::IndexToWorld() \imageMacro{worldcoordinateSystem.png,"",12} \subsection GeometryOverviewPage_CornerCoord Corner-based coordinates \imageMacro{WorldcoordinateSystemCenterBased.png,"",12} \subsection GeometryOverviewPage_CenterCoord Center-based image-coordinates \subsubsection GeometryOverviewPage_CenterCoord_ContIdx Continuous index coordinates -- Dividing world coordinates through the pixel spacing and simultanously taking the offset into account leads to continuous index coordinates inside your dataobject. So continuous coordinates can be float values! +- Dividing world coordinates through the pixel spacing and simultaneously taking the offset into account leads to continuous index coordinates inside your dataobject. So continuous coordinates can be float values! - Continuous index coordinates are represented by mitk::Point3D objects. - They can be obtained by calling mitk::BaseGeometry::WorldToIndex(), where &pt_mm is a point in worldcoordinates.\n \subsubsection GeometryOverviewPage_CenterCoord_Idx Index coordinate system - Index coordinates are discrete values that address voxels of a data object explicitly. - Index coordinates are represented by itk::Index<3> objects. - Basically they are continuous index coordinates which are rounded from half integer up. - E.g. (0,0) specifies the very first pixel of a 2D image, (0,1) the pixel of the next column in the same row - If you have world coordinates, they can be converted to discrete index coordinates by calling mitk::BaseGeometry::WorldToIndex()\n\n \section GeometryOverviewPage_PointsAndVector Difference between Points and Vectors -Like ITK, MITK differenciate between points and vectors. +Like ITK, MITK differentiate between points and vectors. A point defines a position in a coordinate system while a vector is the distance between two points. Therefore points and vectors behave different if a coordinate transformation is applied. -An offest in a coordinate transformation will affect a transformed point but not a vector. +An offset in a coordinate transformation will affect a transformed point but not a vector. An Example: -If two systems are given, which differ by a offset of (1,0,0). The point A(2,2,2) in system one will correspont to point A'(3,2,2) in the second system. +If two systems are given, which differ by a offset of (1,0,0). The point A(2,2,2) in system one will correspond to point A'(3,2,2) in the second system. But a vector a(2,2,2) will correspond to the vector a'(2,2,2). \section GeometryOverviewPage_Concept The Geometry Concept As the superclass of all MITK geometries mitk::BaseGeometry holds: -- a spacial bounding box which is axes-parallel in index coordinates (often discrete indices of pixels), to be accessed by BaseGeometry::GetBoundingBox() +- a spatial bounding box which is axes-parallel in index coordinates (often discrete indices of pixels), to be accessed by BaseGeometry::GetBoundingBox() - a time related bounding box which holds the temporal validity of the considered data object in milliseconds (start and end time), to be accessed by BaseGeometry::GetTimeBounds(). The default for 3D geometries is minus infinity to plus infinity, meaning the object is always displayed independent of displayed time in MITK. - position information in form of a Euclidean transform in respect to world coordinates (i.e. a linear transformation matrix and offset) to convert (discrete or continuous) index coordinates to world coordinates and vice versa, to be accessed by mitk::BaseGeometry::GetIndexToWorldTransform(). See also: \ref GeometryOverviewPage_Introduction "Introduction to Geometries" - Many other properties (e.g. origin, extent, ...) which can be found in the mitk::BaseGeometry "class documentation" - VERY IMPORTANT: A flag called \c isImageGeometry, which indicates whether the coordinates are center-based or not! See also: \ref GeometryOverviewPage_Introduction "Introduction to Geometries" and \ref GeometryOverviewPage_Putting_Together . IMPORTANT: Putting it together for an Image. Every data object (sub-)class of BaseData has a mitk::TimeGeometry which is accessed by mitk::BaseData::GetTimeGeometry(). This mitk::TimeGeometry holds one or more mitk::BaseGeometry objects which describes the object at specific time points, e.g. provides conversion between world and index coordinates and contains bounding boxes covering the area in which the data are placed. There is the possibility of using different implementations of the abstract mitk::TimeGeometry class which may differ in how the time steps are saved and the times are calculated. There are two ways to represent a time, either by a mitk::TimePointType or a mitk::TimeStepType. -The first is similar to the continous index coordinates and defines a Time Point in milliseconds from timepoint zero. +The first is similar to the continuous index coordinates and defines a Time Point in milliseconds from timepoint zero. The second type is similar to index coordinates. These are discrete values which specify the number of the current time step going from 0 to GetNumberOfTimeSteps(). The conversion between a time point and a time step is done by calling the method mitk::TimeGeometry::TimeStepToTimePoint() or mitk::TimeGeometry::TimePointToTimeStep(). Note that the duration of a time step may differ from object to object, so in general it is better to calculate the corresponding time steps by using time points. Also the distance of the time steps does not need to be equidistant over time, it depends on the used mitk::TimeGeometry implementation. Each mitk::TimeGeometry has a bounding box covering the whole area in which the corresponding object is situated during all time steps. This bounding box may be accessed by calling mitk::TimeGeometry::GetBoundingBoxInWorld() and is always in world coordinates. The bounding box is calculated from all time steps, to manually start this calculation process call mitk::TimeGeometry::Update(). The bounding box is not updated if the getter is called. The mitk::TimeGeometry does not provide a transformation of world coordinates into image coordinates since each time step may has a different transformation. If a conversion between image and world is needed, the mitk::BaseGeometry for a specific time step or time point must be fetched either by mitk::TimeGeometry::GetGeometryForTimeStep() or mitk::TimeGeometry::GetGeometryForTimePoint() and then the conversion is calculated by using this geometry. The mitk::TimeGeometry class is an abstract class therefore it is not possible to instantiate it. Instead a derived class must be used. Currently the only class that can be chosen is mitk::ProportionalTimeGeometry which assumes that the time steps are ordered equidistant. To initialize an object with given geometries call mitk::ProportionalTimeGeometry::Initialize() with an existing BaseGeometry and the number of time steps. The given geometries will be copied and not referenced! Also, the mitk::BaseGeometry is an abstract class and derived classes must be used. The most simple implementation, i.e. the one to one implementation of the BaseGeometry class, is the class mitk::Geometry3D. mitk::SlicedGeometry3D is a sub-class of mitk::BaseGeometry, which describes data objects consisting of slices, e.g., objects of type Image (or mitk::SlicedData, which is the super-class of mitk::Image). Therefore, mitk::Image::GetTimeGeometry() will contain a list of mitk::SlicedGeometry3D instances. There is a special method mitk::SlicedData::GetSlicedGeometry(t) which directly returns a mitk::SlicedGeometry3D to avoid the need of casting. The class mitk::SlicedGeometry3D contains a list of mitk::PlaneGeometry objects describing the slices in the image. We have here spatial steps from 0 to GetSlices(). mitk::SlicedGeometry3D::InitializeEvenlySpaced(PlaneGeometry *planeGeometry, unsigned int slices) initializes a stack of slices with the same thickness, one starting at the position where the previous one ends. mitk::PlaneGeometry provides methods for working with 2D manifolds (i.e., simply spoken, an object that can be described using a 2D coordinate-system) in 3D space. For example it allows mapping of a 3D point on the 2D manifold using mitk::PlaneGeometry::Map(). Finally there is the mitk::AbstractTransformGeometry which describes a 2D manifold in 3D space, defined by a vtkAbstractTransform. It is a abstract superclass for arbitrary user defined geometries. An example is the mitk::ThinPlateSplineCurvedGeometry. \subsection GeometryOverviewPage_Putting_Together Putting it together for an Image Please read this section carefully if you are working with Images! The definition of the position of the corners of an image is different than the one of other data objects: As mentioned in the previous section, world coordinates of data objects (e.g. surfaces) usually specify the bottom left back corner of an object. In contrast to that, a geometry of an mitk::Image is center-based, which means that the world coordinates of a voxel belonging to an image points to the center of that voxel. E.g: \imageMacro{PixelCenterBased.png,"",6} \subsection GeometryOverviewPage_CenterBasedVoxel Center-based voxel If the origin of e.g. a surface lies at (15,10,0) in world coordinates, the origin`s world coordinates for an image are internally calculated like the following: \code{.unparsed} (15-0.5*X-Spacing 10-0.5*Y-Spacing 0-0.5*Z-Spacing) \endcode If the image`s spacing is (x,y,z)=(1,1,3) then the corner coordinates are (14.5,9.5,-1.5). If your geometry describes an image, the member variable \c isImageGeometry must be changed to true. This variable indicates also if your geometry is center-based or not. The change can be done in two ways: - You are sure that your origin is already center-based. Whether because you adjusted it manually or you copied it from another image. In that case, you can call the function \c setImageGeometry(true) or \c imageGeometryOn() to set the bool variable to true. - You created a new geometry, did not manually adjust the origin to be center-based and have the bool value \c isImageGeometry set to false (default). In that case, call the function \c ChangeImageGeometryConsideringOriginOffset(true) . It will adjust your origin automatically and set the bool flag to true. If you experience displaced contours, figures or other stuff, it is an indicator that you have not considered the origin offset mentioned above. An image has a mitk::TimeGeometry, which contains one or more mitk::SlicedGeometry3D instances (one for each time step), all of which contain one or more instances of (sub-classes of) mitk::PlaneGeometry. As a reminder: Geometry instances referring to images need a slightly different definition of corners, see mitk::BaseGeometry::SetImageGeometry. -This is usualy automatically called by Image. +This is usually automatically called by Image. \section GeometryOverviewPage_Connection Connection between MITK, ITK and VTK Geometries \imageMacro{ITK_VTK_MITK_Geometries.png,"",10} - VTK transformation for rendering - ITK transformation for calculations - Both automatically updated when one is changed Attention: Not automatically updated when changed hardcoded. Example: \c geometry->GetVtkMatrix()->Rotate(...) */ diff --git a/Documentation/Doxygen/3-DeveloperManual/Concepts/Interaction.dox b/Documentation/Doxygen/3-DeveloperManual/Concepts/Interaction.dox index 4c4b6290ad..bfef846c53 100644 --- a/Documentation/Doxygen/3-DeveloperManual/Concepts/Interaction.dox +++ b/Documentation/Doxygen/3-DeveloperManual/Concepts/Interaction.dox @@ -1,128 +1,128 @@ /** \deprecated \page InteractionPage Interaction and Undo/Redo Concepts \note The following page refers to the deprecated interaction frame work. Please refer to \ref DataInteractionPage for information about the current one. \tableofcontents \section InteractionPage_Introduction Interaction in MITK \b Interaction is one of the most important tasks in clinically useful image processing software. Due to that, MITK has a special interaction concept, with which the developer can map the desired interaction. For a simple change in interaction he doesn't have to change the code. All information about the sequence of the interaction is stored in an XML-file that is loaded by the application during startup procedure at runtime. That even allows the storage of different interaction patterns, e.g. an interaction behaviour like in MS PowerPoint, in Adobe Photoshop or like the interaction behaviour on a medical image retrieval system. \section InteractionPage_Statemachines_Implementation Statemachines to implement Interaction The interaction in MITK is implemented with the concept of state machines (by Mealy). This concept allows to build the steps of interaction with different states, which each have different conditions, very alike the different interactions that may have to be build to develop medical imaging applications. Furthermore state machines can be implemented using object oriented programming (OOP). Due to that we can abstract from the section of code, that implements the interaction and focus on the sequence of interaction. What steps must the user do first before the program can compute a result? For example he has to declare three points in space first and these points are the input of a filter so only after the definition of the points, the filter can produce a result. The according interaction sequence will inform the filter after the third point is set and not before that. Now the filter after an adaption only needs two points as an input. The sequence of the interaction can be easily changed if it is build up as a sequence of objects and not hard implemented in a e.g. switch/case block. Or the user wants to add a point in the scene with the right mouse button instead of the left. Wouldn't it be nice to only change the definition of an interaction sequence rather than having to search through the code and changing every single if/else condition? \subsection InteractionPage_Statemachine State Machine So a separation of the definition of a sequence in interaction and its implementation is a useful step in the development of an interactive application. To be able to do that, we implemented the concept of state machines with several classes: States, Transitions and Actions define the interaction pattern. The state machine itself adds the handling of events, that are sent to it. \imageMacro{statemachine.jpg,"",10} \subsubsection InteractionPage_ExampleA Example A: A deterministic Mealy state machine has always one current state (here state 1). If an event 1 is sent to the state machine, it searches in its current state for a transition that waits for event 1 (here transition 1). The state machine finds transition 1, changes the current state to state2, as the transition points to it and executes actions 1 and 2. Now state 2 is the current state. The state machine receives an event 2 and searches for an according transition. Transition 2 waits for event 2, and since the transition leads to state 2 the current state is not changed. Action 3 and 4 are executed. Now Event 3 gets send to the state machine but the state machine can't find an according transition in state 2. Only transition 2 , that waits for event 2 and transition 4, that waits for event 4 are defined in that state. So the state machine ignores the event and doesn't change the state or execute an action. Now the state machine receives an event 4 and finds transition 3. So now the current state changes from state 2 to state 1 and actions 5 and 1 are executed. Several actions can be defined in one transition. The execution of an action is the active part of the state machine. Here is where the state machine can make changes in data, e.g. add a Point into a list. See mitk::StateMachine, mitk::State, mitk::Event, mitk::Action, mitk::Transition, mitk::Interactor \subsection InteractionPage_GuardState Guard States Guard States are a special kind of states. The action, that is executed after the state is set as current state, sends a new event to the state machine, which leads out of the guard state. So the state machine will only stay in a guard state for a short time. This kind of state is used to check different conditions, e.g. if an Object is picked or whether a set of points will be full after the addition of one point. \imageMacro{statemachine_guard.jpg,"",10} \subsubsection InteractionPage_ExampleB Example B: Event 1 is sent to the state machine. This leads the current state from state 1 into state check. The action 1 is executed. This action checks a condition and puts the result into a new event, that is sent and handled by the same (this) state machine. E.g. is the object picked with the received mouse-coordinate? The event, that is generated, will be Yes or No. In case of event No, the state machine sets the current state back to state 1 and executes action 2. In case of event Yes, the state machine changes the state from state check into state 2 and executes action 3, which e.g. can select said object. \subsection InteractionPage_XMLDefinitionStatemachine Definition of a State machine Due to the separation of the definition of an interaction sequence and its implementation, the definition has to be archived somewhere, where the application can reach it during startup and build up all the objects (states, transitions and actions) that represent the sequence of a special interaction. -In MITK, these informations are defined in an XML-file (i.e. DisplayInteraction.xml or BoundingShapeInteraction.xml) +In MITK, this information is defined in an XML-file (i.e. DisplayInteraction.xml or BoundingShapeInteraction.xml) \note Please note that since this is a resource which is compiled into the executable, changes you make to this file will only be reflected in application behavior after you recompile your code. The structure is the following (from \ref InteractionPage_ExampleA) : \code \endcode The identification numbers (ID) inside a state machine have to be unique. Each state machine has to have one state, that is defined as the start-state of that state machine. This means, initially, the current state of the state machine is the start-state. -The Event-Ids seen above are also defined in the statemachine.xml file. They specify a unique number for a combination of input-conditions (key, mouse and so on). See \ref InteractionPage_InteractionEvents for further informations. +The Event-Ids seen above are also defined in the statemachine.xml file. They specify a unique number for a combination of input-conditions (key, mouse and so on). See \ref InteractionPage_InteractionEvents for further information. The statemachine is compiled into an application at compile time. The definition of one single state machine is called the \a statemachine-pattern. Since this pattern is build up during startup with objects (states, transitions and actions) and these objects only hold information about what interaction may be done at the current state, we can also reuse the pattern. \note You as a developer don't necessarily have to implement your own XML-File! We already have defined some interaction-patterns (e.g. for setting Points in 2D or 3D) which you can use and adapt. \subsubsection InteractionPage_ReusePattern Reuse of Interaction Patterns If we for example have a pattern called "pointset", which defines how the user can set different points into the scene and there is an instance of a state machine called "PointSetInteractor". This state machine has a pointer pointing to the current state in its assigned state machine pattern. Several events are send to the state machine, which moves the pointer from one state to the next, according to the transitions, and executes the actions, referenced in the transitions. But now a new instance of the class "PointSetInteractor" has to be build. So we reuse the pattern and let the current state pointer of the new object point to the start state of the pattern "pointset". The implementation of the actions is \b not done inside a class of the pattern (\a state, \a transition, \a action), it is done inside a state machine class (see the reference for mitkStatemachine). \subsection InteractionPage_InteractionEvents Events During runtime, events are thrown from e.g. the mouse to the operating system, are then send to your graphical user interface and from there it has to be send to the MITK-object called \a mitkEventMapper. This class maps the events received with an internal list of all events that can be understood in MITK. The definition of all understandable events is also located in the XML-File the state machines are defined in. If the received event can be found in the list, an internal mitk-eventnumber is added to the event and send to the object \a mitkGlobalInteraction. See mitk::Event, mitk::GlobalInteraction \subsection InteractionPage_GlobalInteraction GlobalInteraction This object administers the transmission of events to registered state machines. There can be two kinds of state machines, the ones that are only listening and ones that also change data. Listening state machines are here called Listeners and state machines that also change data are called Interactors. \note The discrimination between \a Listener and \a Interactor is only made in mitkGlobalInteraction. As Listener an object derived from class StateMachine can be added and removed from GlobalInteraction and as Interactor an object derived from class Interactor can be added and removed. See the interaction class diagram for further information. To add or remove a state machine to the list of registered interactors, call \a AddInteractor or \a RemoveInteractor of \a GlobalInteraction or to add or remove a listener call \a AddListener of \a RemoveListener. Listeners are always provided with the events. Interactors shall only be provided with an event, if they can handle the event. Because of that the method CanHandleEvent is called, which is implemented in each Interactor. This method analyses the event and returns a value between 0 (can't handle event) and 1 (Best choice to handle the event). Information, that can help to calculate this jurisdiction can be the bounding box of the interacted data and the picked mouse-position stored in the event. So after the object \a GlobalInteraction has received an event, it sends this event to all registered Listeners and then asks all registered Interactors through the method \a CanHandleEvent how good each Interactor can handle this event. The Interactor which can handle the event the best receives the event. Also see the documented code in \a mitkGlobalInteraction. To not ask all registered interactors on a new event, the class \a Interactor also has a mode, which can be one of the following: deselected, subselected (deprecated since HierarchicalInteraction has been removed), selected. These modes are also used for the event mechanism. If an interactor is in a state, where the user builds up a graphical object, it is likely that the following events are also for the build of the object. Here the interactor is in mode selected as long as the interactor couldn't handle an event. Then it changes to mode deselected. The mode changes are done in the actions through operations (described further down) and so declared inside the interaction pattern. See mitk::GlobalInteraction \subsection InteractionPage_Interactors Interactors The class \a Interactor is the superclass for all state machines, that solve the interaction for a single data-object. An example is the class \a mitkPointSetInteractor which handles the interaction of the data \a mitkPointSet. Inside the class \a mitkPointSetInteractor all actions, defined in the interaction-pattern "pointsetinteractor", are implemented. Inside the implementation of these actions (\a ExecuteAction(...) ), so called \a mitkOperations are created, filled with information and send to the \a mitkUndoController and to \a mitkOperactionActor (the data, the interaction is handled for). See mitk::Interactor \subsection InteractionPage_ExecOperations Executing Operations The class mitkOperation and its subclasses basically holds all information needed to execute a certain change of data. -This change of data is only done inside the data-class itself, which is derived from the interface \a mitkOperationActor. Interactors handle the interaction through state-differentiation and combine all informations about the change in a \a mitkOperation and send this operation-object to the method ExecuteOperation (of data-class). Here the necessary data is extracted and then the change of data is performed. +This change of data is only done inside the data-class itself, which is derived from the interface \a mitkOperationActor. Interactors handle the interaction through state-differentiation and combine all information about the change in a \a mitkOperation and send this operation-object to the method ExecuteOperation (of data-class). Here the necessary data is extracted and then the change of data is performed. When the operation-object, here called do-operation, is created inside the method \a ExecuteAction (in class \a mitkInteractor), an undo-operation is also created and together with the do-operation stored in an object called \a OperationEvent. After the Interactor has sent the do-operation to the data, the operation-event-object then is sent to the instance of class \a mitkUndoController, which administrates the undo-mechanism. See mitk::Operation, mitk::OperationActor \subsection InteractionPage_UndoController UndoController The instance of class \a mitkUndoController administrates different Undo-Models. Currently implemented is a limited linear Undo. Only one Undo-Model can be activated at a time. The UndoController sends the received operation events further to the current Undo-Model, which then stores it according to the model. If the method \a Undo() of UndoController is called (e.g. Undo-Button pressed from ) the call is send to the current Undo-Model. Here the undo-operation from the last operation event in list is taken and send to the data, referenced in a pointer which is also stored in the operation-event. A call of the method \a Redo() is handled accordingly. See mitk::UndoController, mitk::LimitedLinearUndo \subsection InteractionPage_references References [Bin99] Robert V. Binder. Testing Object-Oriented Systems: Models, Patterns, and Tools. Addison-Wesley, 1999 */ diff --git a/Documentation/Doxygen/3-DeveloperManual/Concepts/NewEvents.dox b/Documentation/Doxygen/3-DeveloperManual/Concepts/NewEvents.dox index 9a3545d9cf..e29e638d01 100644 --- a/Documentation/Doxygen/3-DeveloperManual/Concepts/NewEvents.dox +++ b/Documentation/Doxygen/3-DeveloperManual/Concepts/NewEvents.dox @@ -1,67 +1,67 @@ /** \page ImplementNewEventsPage What needs to be done to implement a new event? \tableofcontents \section WhenUseEvents When to use this guide -In general there are no restriction about how events are created and processed. The intention of this page is to provide how to contruct Events +In general there are no restriction about how events are created and processed. The intention of this page is to provide how to construct Events in order to use them with the MITK Interaction Concept. \section OriginEvents Origin and processing of Events -\ref DataInteractionPage gives a brief description of how events are generated and proccessed. After events are created they need to be send +\ref DataInteractionPage gives a brief description of how events are generated and processed. After events are created they need to be send via mitk::RenderWindowBase::HandleEvent() so that they are relayed to a Dispatcher which handles the event distribution to the DataInteractors. \section EventImplementation Event Implementation The only prerequisite for a new event to be accepted is that it is derived from the mitk::InteractionEvent class. The new event must implement at least two functions, as shown in the example of the MousePressEvent. Implementation of equality for each event class by implementing IsEqual. Equality does \b not mean an exact copy or pointer equality. Equality is determined by agreement in all attributes that are necessary to describe the event for a state machine transition. Here, for example, for the a mouse event press event, it is important which modifiers are used, which mouse button was used to triggered the event, but the mouse position is irrelevant. \code bool mitk::MousePressEvent::IsEqual(const mitk::InteractionEvent& interactionEvent) { // cast to your event class here (this is guaranteed to work const mitk::MousePressEvent& mpe = static_cast(interactionEvent); // Checking relevant attributes, such as EventButton and Modifiers, but omitting the mouse coordinates // Replace this to check for your event class' attributes return (this->GetEventButton() == mpe.GetEventButton() && this->GetModifiers() == mpe.GetModifiers() && this->GetButtonStates() == mpe.GetButtonStates()) && Superclass::IsEqual(interactionEvent); } \endcode The function IsSuperClassOf() implements an up cast to check if the provided baseClass object is derived from this class. This function is used to support polymorphism on state machine pattern (XML) level. \code bool mitk::MousePressEvent::IsSuperClassOf(InteractionEvent::Pointer baseClass) { // Replace with your class, or implement some rules which classes are accepted. MousePressEvent* event = dynamic_cast(baseClass.GetPointer()); if (event != NULL) { return true; } return false; } \endcode \section EventConstruction Configuration File & Event Factory Regarding the state machine and the state machine pattern parser there are no changes necessary. To be able to parse the new event, changes have to be made in the mitk::EventConfig and mitk::EventFactory classes. The mitk::EventConfig class has to be adapted in a way such that the parameters that describe the event are parsed. The parsing can be easily extended, and should provide a mitk::PropertyList for each event parsed. Lastly the mitk::EventFactory has to be extended to use this mitk::PropertyList and construct an event based on it. */ diff --git a/Documentation/Doxygen/3-DeveloperManual/Concepts/Persistence.dox b/Documentation/Doxygen/3-DeveloperManual/Concepts/Persistence.dox index 125ad9b03c..fd29abfb07 100644 --- a/Documentation/Doxygen/3-DeveloperManual/Concepts/Persistence.dox +++ b/Documentation/Doxygen/3-DeveloperManual/Concepts/Persistence.dox @@ -1,65 +1,65 @@ /** \page PersistenceConceptPage Persistence Concept \tableofcontents -In general, persistence referes to the capability of an application to permanently store data, settings, states, etc. so that they outlive a restart. Normal data objects, such as images, surfaces, etc., which are visible in the data storage of MITK can be saved easily by the save/load functions, see \ref DataManagementPage for more details. This page focus on the persistence of settings, configurations, object states and parameters of the application. Depending on the component, e.g. plugin, module, etc., where you want to store your settings, MITK offers various ways to do so: +In general, persistence refers to the capability of an application to permanently store data, settings, states, etc. so that they outlive a restart. Normal data objects, such as images, surfaces, etc., which are visible in the data storage of MITK can be saved easily by the save/load functions, see \ref DataManagementPage for more details. This page focus on the persistence of settings, configurations, object states and parameters of the application. Depending on the component, e.g. plugin, module, etc., where you want to store your settings, MITK offers various ways to do so: -# For code inside UI independent modules with low dependencies to other libraries the mitk::PersistenceService inside the MITK persistence module can be used to store parameters by using the mitk::PropertyList class: \ref PersistenceMITKService -# UI dependent code where the Qt library is available can use QSettings: \ref PersistenceQTSetting -# If the UI setting of Plugins should be stored permanently, the persistence features of blueberry can be used: \ref PersistenceBlueberry -# Extended features, which include persistence, are provided by the configuration service. \ref PersistenceConfigurationService \section PersistenceMITKService MITK Persistence Service For persistence of member variables and parameters, the interface mitk::IPersistenceService in the MITK core provides an API to store variables permanently by means of mitk::PropertyList objects. These properties can be stored inside a MITK scene together with the data so that the user loads data and restores the application state at once. The current actual implementation of this interface can be found in the module Persistence. The class mitk::PersistenceService offers a rather simple way to store variables permanently. To enable MITK persistence inside your class, simply include the persistence interface and use PERSISTENCE_GET_SERVICE_METHOD_MACRO in the private part of your class, as shown in the next code snippet: \code #include //[...] private: PERSISTENCE_GET_SERVICE_METHOD_MACRO \endcode You can then access a persistent property list by using a unique id, e.g. the name of your plugin. Variables can be added to this property list and will be available after restarting the application. \code mitk::PropertyList::Pointer propList = this->GetPersistenceService()->GetPropertyList("org.mitk.myUniqueModule"); //set a variable: propList->Set("deviceNumber", m_Controls->GrabbingDeviceNumber->value()); //get a variable: int grabbingDeviceNumber = 0; propList->Get("deviceNumber", deviceNumber); \endcode When a MITK scene with stored property list is loaded within MITK the list will change automatically. However a class can be informed by the service object by when the list is changed by adding it as observer to the service object. \code this->GetPersistenceService()->AddPropertyListReplacedObserver(this); \endcode An example implementation for the use of the mitk::PersistenceService can be found in the corresponding unit test mitkPersistenceTest in the Persistence module. \section PersistenceQTSetting Qt Settings Within the UI dependent modules inside MITK, the Qt::settings class can also be used to store variables permanently. The following code snippet shows an example: \code //in the header: #include //[...} QSettings m_MySettings; //in the cpp file: //initialize the settings object (e.g. constructor): m_MySettings("MyClass","MyDescription") //store settings: m_MySettings.setValue("identifier",value); //load settings: int intExample = m_MySettings.value("identifier").toInt(); \endcode However, integration into a MITK scene file is not possible with Qt::settings. \section PersistenceBlueberry Persistence Features of Blueberry In blueberry, the view states can be saved and restored which is described here: MITK.org: Save and Restore your View State. Additionally, there is a possibility to make the preferences of a view persistent, which is documented in the class API documentation of the persistence service. \section PersistenceConfigurationService Configuration Service An implementation of a configuration service is planned for MITK in near future but not available yet. */ diff --git a/Documentation/Doxygen/3-DeveloperManual/Concepts/Pipelining.dox b/Documentation/Doxygen/3-DeveloperManual/Concepts/Pipelining.dox index 1ff6d4f4f8..95e8b944ca 100644 --- a/Documentation/Doxygen/3-DeveloperManual/Concepts/Pipelining.dox +++ b/Documentation/Doxygen/3-DeveloperManual/Concepts/Pipelining.dox @@ -1,85 +1,85 @@ /** \page PipelineingConceptPage Pipelining Concept \tableofcontents \section PipelineingConceptPage_Introduction Introduction to Pipelining Image processing in MITK draws heavily from the pipelining concept, and a clear understaning of it is crucial when developing with MITK. This document will first clarify the general idea behind pipelining and then discuss some MITK specifics that you should know about. In the real world, a pipeline connects a source of some kind with a consumer of another. So we identify three key concepts: 1. The source, which generates data of some kind. 2. The pipeline, which transports the data. Many different pipeline segments can be switched in line to achieve this. 3. The consumer, which uses the data to do something of interest. -The analogy to real pipelines falls a little short in one point: A physical pipeline would never process it's contents, while in software development a pipeline usually does (this is why they are often dubbed filters as well). One might ask why one shouldn't just implement the processing logic in the consumer onject itself, since it onviously knows best what to do with it's data. The two main reasons for this are reusability and flexibility. Say, one wants to display a bone segmentation from a CT-image. Let's also assume for the sake of this introduction, that this is a simple task. One could build a monolithic class that solves the problem. Or one builds a pipeline between the displaying class and the source. We know that bones are very bright in a CT Scan, so we use a treshold filter, and then a segmentation Filter to solve the problem. +The analogy to real pipelines falls a little short in one point: A physical pipeline would never process it's contents, while in software development a pipeline usually does (this is why they are often dubbed filters as well). One might ask why one shouldn't just implement the processing logic in the consumer onject itself, since it onviously knows best what to do with it's data. The two main reasons for this are reusability and flexibility. Say, one wants to display a bone segmentation from a CT-image. Let's also assume for the sake of this introduction, that this is a simple task. One could build a monolithic class that solves the problem. Or one builds a pipeline between the displaying class and the source. We know that bones are very bright in a CT Scan, so we use a threshold filter, and then a segmentation Filter to solve the problem. \imageMacro{pipelining_example_ct.png,"",10} Now let's further assume that after successfully selling this new technology to a large firm, we plan to do the same with ultrasound imaging technology. The brithness relations in Ultrasound images are basically the same, but ultrasound images are very noisy, and the contrast is significantly lower. Since we used pipelining, this is no problem: We don't need to change our old segmentation class - we just plug two new filters in front of the pipeline: \imageMacro{pipelining_example_us.png,"",16} This may seem trivial, but when working with several input streams from many different devices that themselves stem from many different vendors, pipelining can save the day when it comes to broad range support of different specifications. \section PipelineingConceptPage_InMITK Pipelining in MITK \subsection PipelineingConceptPage_Update The Update() Mechanism The flow of data inside a pipeline is triggered by only one function call to the consumer, which is Update(). Each part of the pipeline then triggers the Update() method of it's antecessor. Finally, the source creates a new batch of data using it's own GenerateData() method, and notifies its successor that new data is available. The pipeline can then start to process the data until the finished data batch is available as an output of the last Filter. \imageMacro{pipelining_update.png,"",12} \subsection PipelineingConceptPage_Hierarchy The Pipeline Hierarchy -Tha base class for all parts of the pipeline except the consumer (which can be of any class) is mitk::Baseprocess. This class introduces the ability to process data, has an output and may have an input as well. You will however rarly work with this class directly. +The base class for all parts of the pipeline except the consumer (which can be of any class) is mitk::Baseprocess. This class introduces the ability to process data, has an output and may have an input as well. You will however rarly work with this class directly. \imageMacro{pipelining_hierarchy.png,"",16} Several source classes extend BaseProcess. Depending on the type of data they deliver, these are ImageSource, PointSetSource and SurfaceSource. All of these mark the start of a pipeline. The filters themselves extend one of the source classes. This may not immediately make sense, but remember that a filter basically is a source with an additional input. \section PipelineingConceptPage_WorkWith Working with Filter \subsection PipelineingConceptPage_Setup Setting Up a Pipeline \verbatim // Create Participants mitk::USVideoDevice::Pointer videoDevice = mitk::USVideoDevice::New("-1", "Manufacturer", "Model"); TestUSFilter::Pointer filter = TestUSFilter::New(); // Make Videodevice produce it's first set of Data, so it's output isn't empty videoDevice->Update(); - // attacht filter input to device output + // attach filter input to device output filter->SetInput(videoDevice->GetOutput()); // Pipeline is now functional filter->Update(); \endverbatim \subsection PipelineingConceptPage_Implement Writing Your Own Filter When writing your first Filter, this is the recommended way to go about: - Identify which kinds of Data you require for input, and which for output - According to the information from step one, extend the most specific subclass of BaseProcess available. E.g. a filter that processes images, should extend ImageToImageFilter. - Identify how many inputs and how many outputs you require. - In the constructor, define the number of outputs, and create an output. \verbatim //set number of outputs this->SetNumberOfOutputs(1); //create a new output mitk::Image::Pointer newOutput = mitk::Image::New(); this->SetNthOutput(0, newOutput); \endverbatim -- Implement MakeOutput(). This Method creats a new, clean Output that can be written to. Refer to Filters with similiar task for this. +- Implement MakeOutput(). This Method creates a new, clean Output that can be written to. Refer to Filters with similar task for this. - Implement GenerateData(). This Method will generate the output based on the input it. At time of execution you can assume that the Data in input is a new set. */ \ No newline at end of file diff --git a/Documentation/Doxygen/3-DeveloperManual/Concepts/Properties.dox b/Documentation/Doxygen/3-DeveloperManual/Concepts/Properties.dox index e40b17a393..85cfbda276 100644 --- a/Documentation/Doxygen/3-DeveloperManual/Concepts/Properties.dox +++ b/Documentation/Doxygen/3-DeveloperManual/Concepts/Properties.dox @@ -1,236 +1,236 @@ /** \page PropertiesPage Properties \tableofcontents \section PropertyConcept The Concept Behind MITK Properties Properties belong to a datanode and contain information relevant to the handling of the node by MITK. They provide a place to store additional information which is not part of the actual data, and as such have no reason to be contained within the data/file itself, but might be needed for such things as rendering (e.g. transfer functions) or interaction (e.g. the name of the node). -Propteries can be read and set: +Properties can be read and set: \code //1: Read a property mitk::Property::Pointer readProperty = node->GetProperty("color"); //read the property "color" from the data node mitk::ColorProperty::Pointer colorProperty = dynamic_cast(readProperty); //cast this property to the subtype ColorProperty //2: Write a property mitk::BoolProperty::Pointer newProperty = mitk::BoolProperty::New( true ); //create a new property, in this case a boolean property node->SetProperty( "IsTensorVolume", newProperty ); //add this property to node with the key "IsTensorVolume" \endcode \section PropertiesPage_PropertyServices Property Services Property services enable you to extend the capabilities of the basic property system of MITK. Currently there are four property services, i.e., aliases, descriptions, extensions, and filters. All of them are briefly described in the following. More detailed information can be found in the corresponding API documentation, as all of the services are completely documented. All property services are provided through the functionalities of the \ref MicroServices_UserDocs "C++ Micro Services". Hence, you can use property services from all of your modules and plugins. \subsection PropertiesPage_PropertyAliases Property Aliases You can add an alias to a certain property name which is displayed by the Property View instead of the genuine property name. A property name can have more than one alias in which case the property is displayed multiple times in the Properties View. In addition, aliases can be easily restricted to certain data node types. Property aliases are provided through the service interface mitk::IPropertyAliases. \subsection PropertiesPage_PropertyDescriptions Property Descriptions The Property View will display a description of the currently selected property at its bottom if the property has an associated description. Descriptions are parsed and displayed as rich text. Property descriptions are provided through the service interface mitk::IPropertyDescriptions. \subsection PropertiesPage_PropertyExtensions Property Extensions Property extensions are a very generic approach to attach meta data to properties. They have the sole requirement of inheriting from mitk::PropertyExtension. The Property View searches for known property extensions like mitk::IntPropertyExtension and mitk::FloatPropertyExtension to configure the editors of the properties, e.g., you can specify the valid range for a numeric property. Property extensions are provided through the service interface mitk::IPropertyExtensions. \subsection PropertiesPage_PropertyFilters Property Filters Property filters are a rather powerful tool to filter shown properties by the Property View. There is a single global filter and the possibility to add filters on top of it for specific data node types. Filters consist of blacklist entries and whitelist entries, i.e., blacklisted entries are filtered out and whitelisted entries are the only properties which remain after filtering. If both lists contain entries only whitelisted entries are displayed that are not blacklisted. Property filters are provided through the service interface mitk::IPropertyFilters. \section ListOfIndependentProperty A List Of Module Independent Properties This section lists most of the known properties in MITK according to where they are used. Not every node needs each (or even close to) of these properties. \subsection FileManagement File Management
  • path - The physical path the file was loaded from
  • name - The node name in the datamanager
  • selected - Whether the node is selected in the datamanager
\subsection GenericRenderingProperty Generic Rendering Properties
  • color - Color the surface, grey value image, whatever should be rendered in (default is usually white). There is a special mitk::ColorProperty and you can use the Getter/Setter methods to access it. The color is defined with three values (RGB) in the range between 0.0 and 1.0. \remark If you are inside a mapper you can use the following code to access the color: \code float rgb[3]={1.0f, 1.0f, 1.0f}; GetColor( rgb, BaseRenderer ); \endcode (The BaseRenderer is usually known inside a mapper). \warning This property will not affect images if you set the property "LookupTable". In that case a user-defined lookuptable will be used.
  • in plane resample extent by geometry - Toggles: Resampling grid corresponds to the current world geometry. This means that the spacing of the output 2D image depends on the currently selected world geometry, and *not* on the image itself.
  • Resampling grid corresponds to the input geometry. This means that the spacing of the output 2D image is directly derived from the associated input image, regardless of the currently selected world geometry.
  • layer - Controls which image is considered "on top" of another. In the case that two should inhabit the same space, the higher layer occludes lower layer. So far it works for images and pointsets. The layer property applies only for similar datatypes. Pointsets are always rendered in front of images and the layer will not have any effect.
  • levelwindow - In general, grayscale images contain values between 0 and 255. Therefore, the default window boundaries are set respectively. For displaying the image within a certain range, ie. 50 - 200, this property can be used to adjust the minimum and maximum boundary.
  • LookupTable - This property contains a user defined lookuptable, which can be used to map scalar values to color values. Example: If an image contains a value of 128, in the resulting image the passed lookuptable could map this value to red (255, 0, 0). \warning If you set the "LookupTable" property the "color" property will not longer affect your image. Example for setting up a lookuptable in MITK: \code #include #include #include #include [...] vtkSmartPointer vtkLUT = vtkSmartPointer::New(); vtkLUT->SetRange(100,200); //define your table here vtkLUT->Build(); //pass the table to MITK mitk::LookupTable::Pointer mitkLookupTable = mitk::LookupTable::New(); mitkLookupTable->SetVtkLookupTable(vtkLUT); mitk::LookupTableProperty::Pointer LookupTableProp = mitk::LookupTableProperty::New( mitkLookupTable ); result->SetProperty( "LookupTable", LookupTableProp ); result->Update(); \endcode
  • opacity - Alpha (or transparency) value of the node/image/surface etc.. The range of the opacity is between 0.0 and 1.0. \remark If you are inside a mapper you can use the following code to access the opacity: \code float opacity=1.0f; GetOpacity( opacity, BaseRenderer ); \endcode (The BaseRenderer is usually known inside a mapper). -
  • reslice interpolation - This property takes effect in swivel mode or crosshair rotaiton only. The interpolation modes "Nearest", "Linear", and "Cubic" are available and effect the pixel outcome along the rotated plane. +
  • reslice interpolation - This property takes effect in swivel mode or crosshair rotation only. The interpolation modes "Nearest", "Linear", and "Cubic" are available and effect the pixel outcome along the rotated plane.
  • texture interpolation - This property toggles interpolation of the texture. If enabled, edges between image pixels are blurred. If disabled, edges remain sharp.
  • visible - toggle node/image/surface being rendered at all
\subsection SurfaceRenderingProperties Surface Rendering Properties
  • back color - in 2D, color of the normals outside the surface -
  • back normal lenth (px) - in 2D, length of the normals in pixels +
  • back normal length (px) - in 2D, length of the normals in pixels (When decreasing it the color using the front color is shorter?)
  • color mode - (From VTK) Control how the scalar data is mapped to colors. By default (ColorModeToDefault), unsigned char scalars are treated as colors, and NOT mapped through the lookup table, while everything else is. Setting ColorModeToMapScalars means that all scalar data will be mapped through the lookup table.
  • draw normals 2d - in 2D, toggles the presence of normals
  • front color - in 2D, color of the normals inside the surface -
  • front normal lenth (px) - in 2D, length of the normals in pixels +
  • front normal length (px) - in 2D, length of the normals in pixels (When decreasing it the color using the back color is shorter?)
  • invert normals - in 2D, switch front/back normals
  • line width - in 2D, controls the thickness of the line where the surface intersects the plane (and normals)
  • material.ambientCoefficient - in 3D ambient lighting
  • material.diffuseCoefficient - in 3D scattering of light
  • material.interpolation - Choose which interpolation algorithm to use for surface construction
  • material.representation - Choose the representation to draw the mesh in (Surface, Wireframe, Point Cloud)
  • material.specularCoefficient - in-/decrease non-scattered reflection
  • material.specularPower - control percentage of non-scattered reflection
  • material.wireframeLineWidth - width of the wires if wireframe representation is
  • scalar mode - (From VTK) Control how the filter works with scalar point data and cell attribute data. By default (ScalarModeToDefault), the filter will use point data, and if no point data is available, then cell data is used. Alternatively you can explicitly set the filter to use point data (ScalarModeToUsePointData) or cell data (ScalarModeToUseCellData). You can also choose to get the scalars from an array in point field data (ScalarModeToUsePointFieldData) or cell field data (ScalarModeToUseCellFieldData). If scalars are coming from a field data array, you must call SelectColorArray before you call GetColors. When ScalarMode is set to use Field Data (ScalarModeToFieldData), you must call SelectColorArray to choose the field data array to be used to color cells. In this mode, if the poly data has triangle strips, the field data is treated as the celldata for each mini-cell formed by a triangle in the strip rather than the entire strip.
  • scalar visibility - (From VTK) Turn on/off flag to control whether scalar data is used to color objects.
  • selected - whether the node is selected
  • shader - which shader to use for surface rendering, currently the options are "fixed" and "mitkShaderLightning"
\subsection VolumeRenderingProperties Volume Rendering Properties
  • TransferFunction - contains transfer function for use in coloring image
  • volumerendering - Should the volume be rendered or not
  • volumerendering.ambient - same as cpu with gpu
  • volumerendering.diffuse - same as cpu with gpu
  • volumerendering.specular - same as cpu with gpu
  • volumerendering.specular.power - same as cpu with gpu
\subsection PointSetProperties Point Set Properties
  • close contour - Toggles whether the first and the last point of a contour (connecting pieces between following points of a pointset) are connected.
  • contourcolor - Determines the color of the contour (connecting pieces between following points of a pointset). Visible only if "show contour" is active.
  • contoursize - Represents the diameter of the contour (which is kind of a tube between the following points of a pointset). Visible only if "show contour" is active.
  • distance decimal digits - Sets the number of decimal places for the euclidean point to point distance which can be displayed by activating "show distances".
  • point 2D size - The positions of points in the 2D view are represented by crosses. "point 2D size" determines the size of this crosses.
  • point line width - The positions of points in the 2D view are represented by crosses. "point line width" determines the thickness of this crosses.
  • pointsize - The positions of points in the 3D view are represented by spheres. "pointsize" determines the diameter (size) of this spheres.
  • selectedcolor - Sets the color for selected points from a pointset.
  • show angles - If "show contour" is active the angles between two contour parts can be shown.
  • show contour - Connects following points of a pointset by drawing connecting pieces between this points.
  • show distance lines - Shows all angles and lines of the contour (in 2D views) even if they are not on the view's current slice.
  • show distances - Draws lines between following points (in 2D views) and displays the euclidean distance between this points.
  • show points - Toggles if the points are visible or not in the view.
  • updateDataOnRender - If "true" the pointset is updated before rendering. If the pointset is part of a filter pipeline this also causes an update to the pipeline which sometimes may be not desired so it can be switched of by setting it to false.
\subsection Geometry2DProperties Geometry2D Properties
  • draw edges - Determines if tubes should be drawn around the edges of the 2D plane. Default is true.
Information on properties not in this list can be found in the appropriate module. \subsection PropertiesPageSeeAlso See Also
  • \subpage PlanarPropertiesPage
  • \subpage SegmentationPropertiesPage
*/ diff --git a/Documentation/Doxygen/3-DeveloperManual/Concepts/QVTKRendering.dox b/Documentation/Doxygen/3-DeveloperManual/Concepts/QVTKRendering.dox index beac96cfda..20900d7a8f 100644 --- a/Documentation/Doxygen/3-DeveloperManual/Concepts/QVTKRendering.dox +++ b/Documentation/Doxygen/3-DeveloperManual/Concepts/QVTKRendering.dox @@ -1,128 +1,128 @@ /** \page QVTKRendering Rendering Concept \tableofcontents The MITK rendering pipeline is derived from the VTK rendering pipeline. \section QVTKRendering_Pipeline_VTK VTK Rendering Pipeline \imageMacro{RenderingOverviewVTK.png,"Rendering in VTK",12} In VTK, the vtkRenderWindow coordinates the rendering process. Several vtkRenderers may be associated to one vtkRenderWindow. All visible objects, which can exist in a rendered scene (2D and 3D scene), inherit from vtkProp (or any subclass e.g. vtkActor). A vtkPropAssembly is an assembly of several vtkProps, which appears like one single vtkProp. MITK uses a new interface class, the "vtkMitkRenderProp", which is inherited from vtkProp. Similar to a vtkPropAssembly, all MITK rendering stuff is performed via this interface class. Thus, the MITK rendering process is completely integrated into the VTK rendering pipeline. From VTK point of view, MITK renders like a custom vtkProp object. More information about the VTK rendering pipeline can be found at https://vtk.org/ and in the several VTK books. \section QVTKRendering_Pipeline_MITK MITK Rendering Pipeline This process is tightly connected to VTK, which makes it straight forward and simple. We use the above mentioned "vtkMitkRenderProp" in conjunction with the mitk::VtkPropRenderer for integration into the VTK pipeline. The QmitkRenderWindow does not inherit from mitk::RenderWindow, but from the QVTKWidget, which is provided by VTK. The main classes of the MITK rendering process can be illustrated like this: \imageMacro{qVtkRenderingClassOverview.png,"Rendering in MITK",16} A render request to the vtkRenderWindow does not only update the VTK pipeline, but also the MITK pipeline. However, the mitk::RenderingManager still coordinates the rendering update behavior. -Update requests should be sent to the RenderingManager, which then, if needed, will request an update of the overall vtkRenderWindow. The vtkRenderWindow then starts to call the Render() function of all vtkRenderers, which are associated to the vtkRenderWindow. Currently, MITK uses specific vtkRenderers (outside the standard MITK rendering pipeline) for purposes, like displaying a gradient background (mitk::GradientBackground), displaying video sources (QmitkVideoBackround and mitk::VideoSource), or displaying a (department) logo (mitk::ManufacturerLogo), etc.. +Update requests should be sent to the RenderingManager, which then, if needed, will request an update of the overall vtkRenderWindow. The vtkRenderWindow then starts to call the Render() function of all vtkRenderers, which are associated to the vtkRenderWindow. Currently, MITK uses specific vtkRenderers (outside the standard MITK rendering pipeline) for purposes, like displaying a gradient background (mitk::GradientBackground), displaying video sources (QmitkVideoBackground and mitk::VideoSource), or displaying a (department) logo (mitk::ManufacturerLogo), etc.. Despite these specific renderers, a kind of "SceneRenderer" is member of each QmitkRenderWindow. This vtkRenderer is associated with the custom vtkMitkRenderProp and is responsible for the MITK rendering. The vtkRenderer calls four different functions in vtkMitkRenderProp, namely RenderOpaqueGeometry(), RenderTranslucentPolygonalGeometry(), RenderVolumetricGeometry() and RenderOverlay(). These function calls are forwarded to the mitk::VtkPropRenderer. Then, depending on the mapper type (OpenGL- or VTK-based), OpenGL is enabled or disabled. In the case of OpenGL rendering, the Paint()-method of each individual mapper is called. If the mapper is VTK-based, the four function calls are forwarded to mitk::VtkMapper and within these methods the corresponding VtkProp is evaluated. Both strategies are illustrated in the sequence diagrams below: \imageMacro{qVtkRenderingSequenceVTK.png,"Sequence diagram for MITK VTK rendering",16} In MITK, VTK-based mapper are more common and we recommend on implementing VTK-based mappers. However, MITK supports OpenGL-based mappers as well. \imageMacro{qVtkRenderingSequenceGL.png,"Sequence diagram for MITK OpenGL rendering",16} \section QVTKRendering_Mapper MITK Mapper Architecture Mappers are used to transform the input data in tangible primitives, such as surfaces, points, lines, etc. The base class of all mappers is mitk::Mapper. The mapper hierarchy reflects the two possible ways to render in MITK: Subclasses of mitk::Mapper control the creation of rendering primitives that interface to the graphics library (e.g. via OpenGL, vtk). The mapper architecture is illustrated in the following UML diagram: \imageMacro{qVtkRenderingMapper.jpg,"Mapper architecture",16} mitk::Mapper::Update() calls the time step of the input data for the specified renderer and checks whether the time step is valid and calls method mitk::Mapper::GenerateDataForRenderer(), which is reimplemented in the individual mappers and should be used to generate primitives. mitk::Mapper::SetDefaultProperties() should be used to define mapper-specific properties. \section QVTKRendering_programmerGuide User Guide: Programming hints for rendering related stuff (in plugins) \li The QmitkRenderWindow can be accessed like this: this->GetRenderWindowPart()->GetRenderWindow("axial"); \li The vtkRenderWindow can be accessed like this: this->GetRenderWindowPart()->GetRenderWindow("axial")->GetVtkRenderWindow(); \li The mitkBaseRenderer can be accessed like this: mitk::BaseRenderer* renderer = mitk::BaseRenderer::GetInstance(this->GetRenderWindowPart()->GetRenderWindow("sagittal")->GetRenderWindow()); \li An update request of the overall QmitkStdMultiWidget can be performed with: mitk::RenderingManager::GetInstance()->RequestUpdateAll() or alternatively with the shorthand this->RequestRenderWindowUpdate(); \li A single QmitkRenderWindow update request can be done like this: mitk::RenderingManager::GetInstance()->RequestUpdate(this->GetRenderWindowPart()->GetRenderWindow("axial")->GetVtkRenderWindow()); \li be aware that GetRenderWindowPart() can return null if the editor is closed. This is a common reason for nullpointer exceptions. \note The usage of ForceImmediateUpdateAll() is not desired in most common use-cases. \subsection QVTKRendering_distinctRenderWindow Setting up a distinct Rendering-Pipeline It is sometimes desired to have one (or more) QmitkRenderWindows that are managed totally independent of the 'usual' renderwindows defined by the QmitkStdMultiWidget. This may include the data that is rendered as well as possible interactions. In order to achieve this, a set of objects is needed: \li mitk::RenderingManager -> Manages the rendering \li mitk::DataStorage -> Manages the data that is rendered \li mitk::GlobalInteraction -> Manages all interaction \li QmitkRenderWindow -> Actually visualizes the data The actual setup, respectively the connection, of these classes is rather simple: \code // create a new instance of mitk::RenderingManager mitk::RenderingManager::Pointer renderingManager = mitk::RenderingManager::New(); // create new instances of DataStorage and GlobalInteraction mitk::DataStorage::Pointer dataStorage = mitk::DataStorage::New(); mitk::GlobalInteraction::Pointer globalInteraction = mitk::GlobalInteraction::New(); // add both to the RenderingManager renderingManager->SetDataStorage( dataStorage ); renderingManager->SetGlobalInteraction( globalInteraction ); // now create a new QmitkRenderWindow with this renderingManager as parameter QmitkRenderWindow* renderWindow = new QmitkRenderWindow( parent, "name", renderer, renderingManager ); \endcode That is basically all you need to setup your own rendering pipeline. Obviously you have to add all data you want to render to your new DataStorage. If you want to interact with this renderwindow, you will also have to add additional Interactors/Listeners. \note Dynamic casts of a mitk::BaseRenderer class to an OpenGLRenderer (or now, to an VtkPropRenderer) should be avoided. The "MITK Scene" vtkRenderer and the vtkRenderWindow as well, are therefore now included in the mitk::BaseRenderer. \subsection QVTKRendering_userGuideMapper How to write your own Mapper If you want to write your own mapper, you first need to decide whether you want to write a VTK-based mapper or a GL-based mapper. We recommend to write a VTK-based mapper, as VTK is easy to learn and some GL-based mappers can have unexpected site effects. However, you need to derive from the respective classes. In the following we provide some programming hints for writing a Vtk-based mapper: \li include mitkLocalStorageHandler.h and derive from class BaseLocalStorage as a nested class in your own mapper. The LocalStorage instance should -contain all VTK ressources such as actors, textures, mappers, polydata etc. +contain all VTK resources such as actors, textures, mappers, polydata etc. The LocalStorageHandler is responsible for providing a LocalStorage to a concrete mitk::Mapper subclass. Each RenderWindow / mitk::BaseRenderer is -assigned its own LocalStorage instance so that all contained ressources (actors, shaders, textures, ...) are provided individually per window. +assigned its own LocalStorage instance so that all contained resources (actors, shaders, textures, ...) are provided individually per window. \li GenerateDataForRenderer() should be reimplemented in order to generate the primitives that should be rendered. This method is called in each Mapper::Update() pass, thus, all primitives that are rendered are recomputed. Employ LocalStorage::IsGenerateDataRequired() to determine whether it is necessary to generate the primitives again. It is not necessary to generate them again in case the scene has just been translated or rotated. \li For 2D mappers, it is necessary to determine the 3D primitives close to the current plane that should be drawn. Use planeGeometry = renderer->GetSliceNavigationController()->GetCurrentPlaneGeometry() to get the current plane. The distance to it can be determined by using planeGeometry->DistanceFromPlane(point). \li Reimplement GetVtkProp(), that should return the specific VtkProp generated in GenerateDataForRender() (e.g. a single actor or a propassembly, which is a combination of different actors). The VtkProp is picked up in one of the four render passes and thus integrated into the VTK render pipeline. \li SetDefaultProperties() should be used to define mapper-specific properties. */ diff --git a/Documentation/Doxygen/3-DeveloperManual/Concepts/ReaderWriterPage.md b/Documentation/Doxygen/3-DeveloperManual/Concepts/ReaderWriterPage.md index 74c6e26447..6ad588bd02 100644 --- a/Documentation/Doxygen/3-DeveloperManual/Concepts/ReaderWriterPage.md +++ b/Documentation/Doxygen/3-DeveloperManual/Concepts/ReaderWriterPage.md @@ -1,176 +1,176 @@ Reader and Writer {#ReaderWriterPage} ================= This page is work in progress and will introduce you to the IO-System of MITK. [TOC] ## Introductory slides Several Talks have been given on the IO-System. The following list should provide you with a good starting point. - Introduction to MimeTypes: https://www.mitk.org/images/e/e8/MimeTypes.pdf (by Caspar J. Goch) - Introduction to the IO-System: https://www.mitk.org/images/0/0b/Newio.pdf (by Keno März) - IO-Streams and the new IO System: https://www.mitk.org/images/9/95/Streams.pdf (by Keno März) ## Quick start: Reading and writing files using IOUtil mitk::IOUtil class provides convenience methods for loading data into a data storage or just returning BaseData objects without user interaction. The mitk::IOUtil::Save() and mitk::IOUtil::Load() methods cover the typical use cases and automatically select the best matching mitk::IFileReader and mitk::IFileWriter instance. In most cases, this is the easiest way to read or write a file. // load files directly into datastorage mitk::IOUtil::Load("/path/to/my/file.nrrd",*ds); // load basedata into local vector std::vector< mitk::Basedata::Pointer > basedatas; basedatas = mitk::IOUtil::Load("/path/to/my/file.nrrd"); // write basedata to file (here: surface as PLY mitk::IOUtil::Save(mySurface, "/Save/surface/here.ply"); // write basedata to file (here: surface as STL mitk::IOUtil::Save(mySurface, "/Save/surface/here.stl"); When reading a file using IOUtil, the IO-Framework first determines the mime-type of the given file. Afterwards, the best reader is selected internally, instantiated, and executed. The resulting BaseData Objects are returned to the developer as the method result. ## Quick start: Creating your own reader or writer If you implement a new BaseData, usually matching readers and writers are required. The following guide will help you to quickly set up these up and get them working in MITK. Create new classes for reader and writers. Optimally, place them in an extra IO-Module that is configured as autoload. This way, they are available to the application from the start. If you are working with common data, the appropriate module is MitkIOExt. You can either extend mitk::AbstractFileIO, which will allow you to implement a class with reader and writer abilities, or you can extend mitk::AbstractFileReader or mitk::AbstractFileWriter specifically. \imageMacro{reader_writer_classes.png,"",16} Implement the given Methods. A good example on how to write a simple reader and writer is the mitkPointSetReaderService.cpp and mitkPointSetWriterService.cpp class, from which you can take implementation cues. The following is a simplified version of the header file: namespace mitk { class PointSetReaderService: public AbstractFileReader // 2) Extend the Abstract File Reader { public: PointSetReaderService(); // 3) Provide Constructor and Destructor virtual ~PointSetReaderService(); // 4) Overwrite the Read Method as seen here using AbstractFileReader::Read; virtual std::vector< itk::SmartPointer > Read(); private: // 5) Provide a clone method PointSetReaderService(const PointSetReaderService& other); virtual PointSetReaderService* Clone() const; }; } ### Example Follow these steps to implement a new Reader: A) Create a new cpp and h file in an appropriate submodule. Usually, a reader or writer should be located in the same module as the BaseData derivate it reads/writes. -B) Extend AbstractFileReader . This is highly recommended because it enables integration of your Reader into the Registery. It will then automatically be used by the application to load this type of files. +B) Extend AbstractFileReader . This is highly recommended because it enables integration of your Reader into the Registry. It will then automatically be used by the application to load this type of files. C) Provide a constructor . It should contain a minimal amount of information and might look like this: mitk::PointSetReaderService::PointSetReaderService() : AbstractFileReader(CustomMimeType(IOMimeTypes::POINTSET_MIMETYPE_NAME()), "MITK Point Set Reader") { RegisterService(); } Note the call to the superclass constructor containing the MIME-type. You can either reuse an existent MIME type here or create your own MIME-type locally . Finally, register the service to make it available to MITK. D) Provide a Clone Method: Readers are clones when the registry requires a new reader. Provide a clone method to accommodate for this. Use the mitkPointSetReaderService.cpp as a reference if necessary. E) Instantiate it in the module activator. Open the module activator and make sure that the new Reader/Writer is instantiated and held somewhere in the code. Also, unregister the reader/writer in the unload function if necessary. ## Reader/Writer Options Options are a powerful concept to modify Reader/Writer behaviour and to automate user interaction with minimal development overhead. In principal, options are represented by a simple map and a few getter and setter functions: typedef std::map Options; virtual Options GetOptions(); virtual void SetOptions(const Options& options); virtual us::Any GetOption(const std::string& name); virtual void SetOptions(const Options& options); In its constructor, a reader or writer can set its options as a number map entries consisting of an human-readable option name and a default value. When a user tries to read/write a file via the interface, an options menu is generate from the map. \imageMacro{io_options.png,"The options dialog shown by the raw reader",5} The user can then modify these options, which are automatically set in the reader/writer. You can use raw-images to see this behaviour in action. ## Mime Types The MimeType class provides meta-data about a specific data format. Every mitk::IFileReader and mitk::IFileWriter instance must be associated with exactly one mime-type via a service property. \imageMacro{mimetypes.png,"",14} Mime-type are used for categorizing data formats and creating filter strings in file open and save dialogs. Hence they effectively control the accessible set of data formats from a graphical user interface. Minimally, mime-types should provide a name and a list of handled extensions in lower case. Additionally, it is highly encouraged to set a category and a comment which will provide user-readable strings for user interaction. It is important to understand the difference between mitk::MimeType and mitk::CustomMimeType. The former is an immutable stack object and can be pulled from mitk::MimeTypeProvider. it should be used for all interaction with MimeTypes. mitk::CustomMimeType is the heap-object pendant which is wrapped by mitk::MimeType and should exclusively be used for registration purposes, i.e. when you register a new MimeType. ## Additional Capabilities You can extend your reader writer with useful capabilities. All of these are optional + Priority: Reader use a ranking with Get- and SetRanking in order to signify how well they are suited to read a file. If several readers are able to read a file, the one with the highest Ranking level will be chosen. + ProgressCallbacks : Readers are executed in a thread automatically. If the reader implements callbacks, the progress bar will be more accurate during loading of files. Note: Progress callbacks are work in progress. ## Ranking strategies Todo. ## Convenience classes Developers usually do not interact with the service registry directly to retrieve and select a matching mitk::IFileReader or mitk::IFileWriter instance. ### %QmitkIOUtil The QmitkIOUtil class is a wrapper around mitk::IOUtil, providing file open and save dialogues for selecting a file name from a within a graphical user interface. ### FileReaderRegistry and FileWriterRegistry Access to mitk::IFileReader and mitk::IFileWriter objects and their service references. ## Integrating external I/O mechanisms The MITK I/O system integrates with several external I/O systems. ### ITK Reader and Writer The I/O classes from ITK are automatically added to the service registry of MITK. They can be transparently used via the mitk::IFileReader and mitk::IFileWriter interfaces or the mitk::IOUtil methods. ### VTK Reader and Writer VTK does not provide a mechanism to enumerate all available I/O classes. Hence MITK provides manual integration of a specific set of VTK readers and writers. ## Error handling and recovery Todo. diff --git a/Documentation/Doxygen/3-DeveloperManual/Concepts/RenderingTests.dox b/Documentation/Doxygen/3-DeveloperManual/Concepts/RenderingTests.dox index 134aeec00b..cad39f548e 100644 --- a/Documentation/Doxygen/3-DeveloperManual/Concepts/RenderingTests.dox +++ b/Documentation/Doxygen/3-DeveloperManual/Concepts/RenderingTests.dox @@ -1,97 +1,97 @@ namespace mitk{ /** \page RenderingTests Automatic Rendering Tests Available sections: -# \ref RenderingTests_WhatIsARenderingTest "What is an automatic rendering test?" -# \ref RenderingTests_HowToCreateATest "How to create a rendering test" \section RenderingTests_WhatIsARenderingTest What is an automatic rendering test? An automatic rendering test is a powerful tool to test rendering results automatically via dashboard. Regarding rendering lots of different sources influence the output on the screen (e.g. different -mappers, renderes, camera settings or the algorithm creating the data). Thus, during the rendering +mappers, renderers, camera settings or the algorithm creating the data). Thus, during the rendering process of an image many different classes are involved and can have impact on the output. A minor change in an important class (e.g. mitkVtkPropRenderer) can have major impact on the actual rendering. An automatic rendering test takes an arbitrary object as input (e.g. image, surface, point set), renders this into an mitkRenderWindow, makes a screen shot of that renderwindow and finally compares that screen shot to a given reference. Of course, the reference has to be defined by the user. Internally, a VTK test method is used to compare both screen shots and measure differences. In case of failure, a difference can be generated to show exactly which pixels are rendered incorrectly. Implementing automatic rendering tests for algorithms ensures that algorithms deliver the same output as they used to do in previous version of MITK. \section RenderingTests_HowToCreateATest How to create your own automatic rendering test To create an automatic rendering test you should use an existing test as example (e.g. mitkImageVtkMapper2DTest). \subsection RenderingTests_HowToCreateATest_Sub1 Adding the test to CMake Like adding any test with parameters to CMake, you have to add a custom test to the files.cmake and the corresponding CMakeLists.txt: For instance a test for the mitkImageVtkMapper2D has to be added like this: files.cmake \code set(MODULE_CUSTOM_TESTS ... mitkImageVtkMapper2D.cpp ) \endcode CMakeLists.txt \code mitkAddCustomModuleTest(mitkImageVtkMapper2D_rgbaImage640x480 mitkImageVtkMapper2D #custom name of the test and executable ${MITK_DATA_DIR}/RenderingTestData/rgbaImage.png #input image to load in data storage -V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/rgbaImage640x480REF.png #corresponding reference screenshot ) \endcode The first parameter defines a custom name for the test. This is a feature to distinguish between tests with different inputs. In this example the test is named mitkImageVtkMapper2D_rgbaImage640x480 to show that this test is using the test image rbgaImage640x480 as input. The next parameters sets test executable name (i.e. the name of the test class). Here: mitkImageVtkMapper2D. The next parameter(s) are used to pass the input to the test. For instance, it is possible to set multiple objects as input for a test (e.g. /path/to/img1.jpg /path/to/img2.nrrd /path/to/pointset.mps). All test data for core tests should be placed into the MITK-DATA repository inside the folder: ${MITK_DATA_DIR}/RenderingTestData/. It is possible to create other folders for different modules or bundles. The option -V defines the path to the reference screen shot and is internally used by VTK. The reference screen shot is highly important and has to be proven if is correct. The mitkRenderingTestHelper offers means to capture a screen shot of a renderwindow. Capturing a reference screen shot should happen just once and NOT be a permanent part of the test. It is also possible to set the option -T /path/to/directory/. This option is internally used by VTK -to save a difference image. This is meant for debugging and should not be used for the final implemenation of a test. +to save a difference image. This is meant for debugging and should not be used for the final implementation of a test. \subsection RenderingTests_HowToCreateATest_Sub2 Coding the test Writing the test code is pretty straight forward. In the example of the mitkImageVtkMapper2DTest the input parameters are added to a datastorage and rendered into a render window via the mitkRenderingTestHelper. Last, the vtkTesting macro is called to compare the given reference to the data rendered in the renderwindow: \code int retVal = vtkRegressionTestImage( renderingHelper.GetVtkRenderWindow() ); //retVal meanings: (see VTK/Rendering/vtkTesting.h) //0 = test failed //1 = test passed //2 = test not run //3 = something with vtkInteraction MITK_TEST_CONDITION( retVal == 1, "VTK test result positive" ); \endcode If the content of the previously filled renderwindow does not equal the reference, the test will fail. Feel free to modify the data before rendering. E.g. create a surface from the loaded image and render that surface afterwards or add compute QBalls for an image and render those. Happy testing! */ } diff --git a/Documentation/Doxygen/3-DeveloperManual/Concepts/Selection.dox b/Documentation/Doxygen/3-DeveloperManual/Concepts/Selection.dox index 646405bd73..6ad7e13a74 100644 --- a/Documentation/Doxygen/3-DeveloperManual/Concepts/Selection.dox +++ b/Documentation/Doxygen/3-DeveloperManual/Concepts/Selection.dox @@ -1,302 +1,302 @@ /** \page SelectionConceptPage Selection Concept \tableofcontents Selecting data nodes is frequently done when working with the MITK workbench. Most of the views in MITK require one or more selected data nodes that will be somehow processed by the underlying algorithms, such as image segmentation, image registration, basic image processing, image statistics computation etc.. -In order to overcome some disadvantages of the old data node seletion concept, the following new selection concept has been implemented. +In order to overcome some disadvantages of the old data node selection concept, the following new selection concept has been implemented.
This page introduces the concept and the corresponding implementation. The document starts with a brief explanation of the disadvantages of the old concepts and justifies a need for the new concept. \section Migration Migration idea / background The old data node selection concept is a mixture of a \a global and a \a local selection concept: \subsection GlobalSelection Global selection The global selection was realized by the data manager view: Selecting one or more data nodes in the data manager view lead to a propagation of the selected nodes via the blueberry framework. Other views that derive from QmitkAbstractView received the new selection and processed it individually. All activated views were able to receive the new selection successively, thus it is called a \a global selection. \subsection LocalSelection Local selection The local selection was realized by Qt elements inside a view. An example of such an element is the QmitkDataStorageComboBox. This combo box contains the nodes from the data storage and allows to select one of it. A combo box can be included in a view multiple times. Additionally a node predicate can be set for a combo box so that only a filtered subset of all nodes in the data storage is contained in the combo box. A selection inside a combo box does not affect the selection of another combo box (in the same or another view), thus it is called a \a local selection.
Both concepts have reached their functional limit: -# the global selection does not offer the functionality to filter data nodes -# the global selection does not allow to select different data nodes in different views -# the selection order for multi-node selection might be important, which can be cumbersome to achieve via the data manager -# the local selection does not offer enough flexibility: -# it does not allow to propagate data node selections -# it does not allow multi node selection (multiple combo boxes are needed) -# it does not show the visibility / hierarchy of the data nodes The latest trend was to change the design of the view of the MITK workbench so that the local concept (using the QmitkDataStorageComboBox) was used wherever possible. This was done since the problems of the global selection concept where severe. In cases where the local concept was used positive feedback was received. \section NewSelectionConcept New selection concept The new selection concept is based on the idea of a local selection concept. This accounts for some fundamental requirements, such as individual data node selection for each view and data node filtering. Furthermore it clearly defines the responsibility of the data manager view: the data manager is only used to manage the data to render, e.g. defining the node hierarchy (rendering order) or setting the color or transparency of the pixel data. Additionally the new local selection concept is also able to overcome the problems of the previous local concept: It allows data node selection propagation, multi node selection and is able to show additional information such as data node hierarchy or node visibility. How this is achieved will be explained in the following sections. \subsection BasicIdea Basic idea The basic idea is to provide a set of classes that define the basic functionality of the new selection concept. These classes allow the use of existing and the development of new data storage inspectors or node selection widgets.
\imageMacro{selection_concept_class_diagram.png, "", 16}
Included in this concept is a set of basic data storage inspectors and node selection widgets that are frequently needed. A developer can extend these classes to create own data storage inspectors or node selection widgets to customize a plugins behavior and design. The following paragraphs further describe the most important classes: \b QmitkAbstractDataStorageModel The QmitkAbstractDataStorageModel extends the QAbstractItemModel to provide a custom item model for the data storage. This custom item model extends the Qt-model functionality in order to accept an mitk::DataStorage and an mitk::NodePredicateBase. The data storage is used to define on which data storage the model operates. In order to update the data storage model if the data storage changes, the QmitkAbstractDataStorageModel provides four pure virtual functions, QAbstractItemModel::DataStorageChanged, QAbstractItemModel::NodeAdded, QAbstractItemModel::NodeChanged and QAbstractItemModel::NodeRemoved. This abstract model calls the first function if the data storage has been reset using the SetDataStorage-function (i.e. to a new data storage or to a nullptr). The last three functions are connected to the corresponding events of the data storage (AddNodeEvent, ChangedNodeEvent and RemoveNodeEvent). The node predicate can be set and used to filter the data storage, so that only a subset of the contained data nodes is represented by the data storage model. In order to update the data storage model if the node predicate changes, the QmitkAbstractDataStorageModel provides a pure virtual function, QAbstractItemModel::NodePredicateChanged. This abstract model calls this function if the node predicate has been reset using the SetNodePredicate-function (i.e. to a new node predicate or to a nullptr). Any subclass of this abstract data storage model can define its own functionality by overriding the five corresponding functions. The abstract data storage model does not implement any Qt model functionality, so the functions of the QAbstractItemModel have to be overwritten in the subclasses, according to the model type (e.g. list model, table model). \b QmitkAbstractDataStorageInspector The QmitkAbstractDataStorageInspector serves as a base class that can be derived from to provide a custom view onto the data storage using a QmitkAbstractDataStorageModel derivative. This custom widget extends the QWidget functionality in order to accept an mitk::DataStorage and an mitk::NodePredicateBase. The data storage is used to define which data storage the widget should inspect. The node predicate can be set and later be used to filter the data storage, so that only a subset of the contained data nodes is inspected by the data storage inspector. The data storage and the node predicate can be set by the corresponding setter, which in turn calls the pure virtual function QmitkAbstractDataStorageInspector::Initialize. Any subclass of this abstract data storage inspector can now define its own functionality inside the Initialize-function to define what happens if the data storage or the node predicate is (re-)set. Typically an inspector will forward the data storage and the node predicate to the data storage model to make it aware of the data it should hold. Additionally a data storage inspector holds a QmitkModelViewSelectionConnector and initializes it with a QAbstractItemView derived class (e.g. a QListView) and the data storage model. The idea behind this connector-class will be explained in the next section. Furthermore the abstract class provides a QSignal, CurrentSelectionChanged(NodeList), that is emitted if the selection of the underlying data storage model has changed. The abstract class also provides two QSlots, SetSelectOnlyVisibleNodes(bool) and SetCurrentSelection(NodeList). Calling these slots will forward the given arguments to the model view selection connector member which in turn can manipulate the data storage models selection. \b QmitkModelViewSelectionConnector The QmitkModelViewSelectionConnector is used to transform a model selection into a list of selected data nodes and vice versa. The class accepts a QAbstractItemView with a corresponding QmitkAbstractDataStorageModel that operates on a data storage. The connector class provides a QSlot, QmitkModelViewSelectionConnector::ChangeModelSelection(const QItemSelection\&, const QItemSelection\&) that is called if the selectionChanged-signal of the selection model of the given QAbstractItemView is emitted. This indicates a change in the model selection. The slot itself will emit a QSignal, QmitkModelViewSelectionConnector::CurrentSelectionChanged(QList), where the selection of the item view has been transformed to a list of selected data nodes. The transformation is done by using the member functions QmitkModelViewSelectionConnector::GetSelectedNodes and QmitkModelViewSelectionConnector::GetInternalSelectedNodes: The selected indices of the item view's selection model are used to receive the corresponding data node from the data storage model. Additionally the connector class provides a function QmitkModelViewSelectionConnector::SetCurrentSelection(QList), which can be used to change the selection of the QAbstractItemView from outside of this class. The nodes of the list are compared to the nodes that are valid for the data storage model, according to the current node predicate. If the given nodes are valid the corresponding indices are selected in the view. If the given list is equal to the current selection, nothing happens. Using the QmitkModelViewSelectionConnector::SetSelectOnlyVisibleNodes(bool selectOnlyVisibleNodes) function it is possible to set a variable that defines if only those nodes should be de-/selectable that are included in the list of filtered data nodes. This means that a new selection ca be received, which might change the current selection of the view. However, those nodes that are not visible, as defined by the node predicate of the data storage model, will not be de-/selectable. \b \anchor QmitkSelectionServiceConnector_ref QmitkSelectionServiceConnector The QmitkSelectionServiceConnector is used to interact with the global selection bus: It provides a private QSlot, QmitkSelectionServiceConnector::OnServiceSelectionChanged(const berry::IWorkbenchPart::Pointer&, const berry::ISelection::ConstPointer&), which is internally called if the selection of the selection bus has been changed. This slot transforms the berry selection into a list of selected data nodes and emits the QSignal QmitkSelectionServiceConnector::ServiceSelectionChanged(QList). Typically this signal is used to react in another class on the change of selected nodes from the selection bus, e.g. inside the QmitkModelViewSelectionConnector to change the selection of an item view via the selection bus. Additionally the connector class provides a function, QmitkSelectionServiceConnector::SetAsSelectionProvider(QmitkDataNodeSelectionProvider*), which can be used to set the connector as a provider for data node selections that will be sent via the global selection bus. This way a QmitkModelViewSelectionConnector can propagate its selection to all listeners of the selection bus via the QmitkSelectionServiceConnector. The following code shows how the two connector classes can be connected: \code void QmitkDataStorageViewerTestView::SetAsSelectionProvider(bool enable) { if (enable) { m_SelectionServiceConnector->SetAsSelectionProvider(GetSite()->GetSelectionProvider().Cast().GetPointer()); connect(m_ModelViewSelectionConnector.get(), SIGNAL(CurrentSelectionChanged(QList)), m_SelectionServiceConnector.get(), SLOT(ChangeServiceSelection(QList))); } else { m_SelectionServiceConnector->RemoveAsSelectionProvider(); disconnect(m_ModelViewSelectionConnector.get(), SIGNAL(CurrentSelectionChanged(QList)), m_SelectionServiceConnector.get(), SLOT(ChangeServiceSelection(QList))); } } \endcode
\code void QmitkDataStorageViewerTestView::SetAsSelectionListener(bool enable) { if (enable) { m_SelectionServiceConnector->AddPostSelectionListener(GetSite()->GetWorkbenchWindow()->GetSelectionService()); connect(m_SelectionServiceConnector.get(), SIGNAL(ServiceSelectionChanged(QList)), m_ModelViewSelectionConnector.get(), SLOT(SetCurrentSelection(QList))); } else { m_SelectionServiceConnector->RemovePostSelectionListener(); disconnect(m_SelectionServiceConnector.get(), SIGNAL(ServiceSelectionChanged(QList)), m_ModelViewSelectionConnector.get(), SLOT(SetCurrentSelection(QList))); } } \endcode \subsection DataStorageInspectors The data storage inspectors \b \anchor QmitkNodeSelectionDialog_ref QmitkNodeSelectionDialog The QmitkNodeSelectionDialog is the main widget to be used in order to select data nodes from the data storage. It is a pop-up-dialog that can be included in any other widget, such as the QmitkSingleNodeSelectionWidget or the QmitkMultiNodeSelectionWidget. The node selection dialog serves as a container for different QmitkAbstractDataStorageInspector: It uses a QTabWidget that holds the different data storage inspectors. These inspectors are received by using the mitk::DataStorageInspectorGenerator class. Each data storage inspector is then added to the tab widget and its \a CurrentSelectionChanged-signal is connected to the dialog's \a OnSelectionChanged-slot. If the selection inside a data storage inspector changes, the slot sets the selected nodes of the dialog and propagates this selection to all its known data storage inspectors. This way all inspectors that are currently held in the tab widget show the same selection. In order to dynamically add and receive new data storage inspectors the microservice approach is used: Each concrete implementation of a data storage inspector provider registers itself as an mitk::IDataStorageInspectorProvider via the service registry. For this the QmitkDataStorageInspectorProviderBase can be used. Any class can use the mitk::DataStorageInspectorGenerator to either receive all registered data storage inspector providers or a specific one. A provider in turn provides a specific data storage inspector, which can be used to display the data nodes of the data storage. The user can define which of the registered data storage inspectors should be available when using a QmitkNodeSelectionDialog: Using the workbench preferences one can select the Node Selection entry and enable or disable certain data storage inspectors. \b mitk::IDataStorageInspectorProvider The mitk::IDataStorageInspectorProvider declares the service interface for all data storage inspector providers. It must be implemented to override the mitk::IDataStorageInspectorProvider::CreateInspector-function. This function should return the concrete data storage inspector created by the provider. In order to simplify the use of this interface and to add some basic functionality, the QmitkDataStorageInspectorProviderBase class should be used. \b QmitkDataStorageInspectorProviderBase The QmitkDataStorageInspectorProviderBase implements the mitk::IDataStorageInspectorProvider-interface and is a template implementation: It can be used with a concrete implementation of the QmitkAbstractDataStorageInspector; the data storage inspector that the provider base should create. When the provider base is initialized it registers itself as an mitk::IDataStorageInspectorProvider service reference. The provider base overrides the mitk::IDataStorageInspectorProvider::CreateInspector-function of the interface to create and return a concrete instance of the provided data storage inspector. This way each concrete data storage inspector can be created by receiving the corresponding data storage inspector provider as a service and using the mitk::IDataStorageInspectorProvider::CreateInspector-function. \b mitk::DataStorageInspectorGenerator The mitk::DataStorageInspectorGenerator simply provides two static functions to receive all or a specific data storage provider. These providers have been registered before as an mitk::IDataStorageInspectorProvider service. Using the microservice approach the provider can be received without any further knowledge about the concrete provider. This way a developer can easily add their own concrete data storage inspector by creating a new QmitkDataStorageInspectorProviderBase with the concrete inspector as the template argument. \subsection CustomDataStorageInspector Adding a custom data storage inspector As mentioned above, the microservice approach is used inside the mitk::DataStorageInspectorGenerator to receive all known or a specific data storage provider. In order to let the microservice know about a new custom data storage inspector, it has to be provided by a data storage provider. This data storage provider has to be registered as an mitk::IDataStorageInspectorProvider service. The new selection concept provides the QmitkDataStorageInspectorProviderBase class (see above), which automatically registers the data storage provider as a provider service. For this the following constructor can be used: \code QmitkDataStorageInspectorProviderBase(const std::string& id, const std::string& displayName, const std::string& desc= "") \endcode The constructor can be used like this: \code mitk::IDataStorageInspectorProvider* dataStorageInspectorProvider = new QmitkDataStorageInspectorProviderBase("org.mitk.QmitkDataStorageListInspector", "Simple list", "Displays the filtered content of the data storage in a simple list.")); \endcode This registers a new QmitkDataStorageInspectorProviderBase instance as an mitk::IDataStorageInspectorProvider service. The data storage provider and its specific data storage inspector can later be received using the following code: \code mitk::IDataStorageInspectorProvider* dataStorageProvider = mitk::DataStorageInspectorGenerator::GetProvider("org.mitk.QmitkDataStorageListInspector"); QmitkAbstractDataStorageInspector* dataStorageInspector = dataStorageProvider->CreateInspector(); \endcode In the code snippets the QmitkDataStorageListInspector class was used. This is an example of a concrete data storage inspector which is provided by the presented selection concept. It is a frequently used data storage inspector and already registered via a corresponding provider base. This is done inside the mitk::QtWidgetsActivator class; a class that is used during module activation. Currently four frequently used custom data storage inspectors are already registered. The following sections describe these four frequently used basic inspectors. \subsubsection QmitkDataStorageListInspector The QmitkDataStorageListInspector is an example of a concrete data storage inspector, subclassing the QmitkAbstractDataStorageInspector. Its only purpose is to provide a custom model-view pair that represents a list view onto the data storage. The view is a simple QListView. The model is a custom model derived from the above mentioned QmitkAbstractDataStorageModel: The QmitkDataStorageDefaultListModel overrides the five pure virtual functions to react on changes of the data storage or the node predicate: each function calls the private UpdateModelData-function, which will simply retrieve the currently available (possibly filtered) data nodes of the data storage. Additionally the default list model needs to override some functions of the QAbstractItemModel in order to represent a list model. One important function to override is the data(const QModelIndex\& index, int role = Qt::DisplayRole)}-function: This function is used by Qt to define what has to be displayed in the list view. It is important that a custom model returns an mitk::DataNode::Pointer object for a model index when the role is mitkDataNodeRole. \subsubsection QmitkDataStorageTreeInspector The QmitkDataStorageTreeInspector is another example of a concrete data storage inspector, subclassing the QmitkAbstractDataStorageInspector. Its only purpose is to provide a custom model-view pair that represents a tree view onto the data storage. The view is a simple QTreeView. The model is a custom model derived from the above mentioned QmitkAbstractDataStorageModel: The QmitkDataStorageSimpleTreeModel needs to override some functions of the QAbstractItemModel in order to represent a tree model. Again, one important function to override is the data(const QModelIndex\& index, int role = Qt::DisplayRole)}-function. It is important that the simple tree model returns an mitk::DataNode::Pointer object for a model index when the role is mitkDataNodeRole. The QmitkDataStorageTreeInspector is designed to reflect the classical data manager view. \subsubsection QmitkDataStorageSelectionHistoryInspector The QmitkDataStorageSelectionHistoryInspector is a concrete data storage inspector that allows to view the recently used nodes of the data storage. It uses the same QmitkDataStorageDefaultListModel as the QmitkDataStorageListInspector. The QmitkDataStorageSelectionHistoryInspector provides a function to add nodes to the selection history. The selection history will be displayed in chronological order and a selected node will only appear once in the history list. \subsubsection QmitkDataStorageFavoriteNodesInspector The QmitkDataStorageFavoriteNodesInspector is a concrete data storage inspector that allows to view a user's favorite nodes of the data storage. It extends the QmitkDataStorageListInspector. The QmitkNodeSelectionDialog provides a function to set nodes as favorite nodes using a specific node property. It allows the QmitkDataStorageFavoriteNodesInspector to use a customized node predicate to filter nodes according to their favorite-property-state. This favorite-property-state is defined by the node property "org.mitk.selection.favorite" The QmitkDataStorageFavoriteNodesInspector provides a function to unset nodes as favorite nodes by setting this specific node property to "false", so that they won't appear anymore in the list of favorite nodes. \subsection NodeSelectionWidgets The node selection widgets \b QmitkAbstractNodeSelectionWidget The QmitkAbstractNodeSelectionWidget extends the QWidget to provide a custom widget to show data nodes after they have been selected using the QmitkNodeSelectionDialog with its data storage inspectors. This custom widget extends the QWidget functionality in order to accept an mitk::DataStorage and an mitk::NodePredicateBase. The data storage is used to define which data storage should be used by the selection dialog. The node predicate is used to filter the data storage. Any subclass of this abstract node selection widget needs to override the following functions in order to customize its behavior: - void QmitkAbstractNodeSelectionWidget::SetCurrentSelection(NodeList), in order to set the selection of this widget given an external list of selected nodes. This function may be customized to filter the incoming selected nodes. - void QmitkAbstractNodeSelectionWidget::UpdateInfo(), in order to set the information text and enable / disable the control buttons according to the current selection. - void QmitkAbstractNodeSelectionWidget::OnNodePredicateChanged(), in order to react to a change of the used node predicate. Changing the node predicate might alter the selection. This function is called if QmitkAbstractNodeSelectionWidget::SetNodePredicate(mitk::NodePredicateBase*) was called. - void QmitkAbstractNodeSelectionWidget::OnDataStorageChanged(), in order to react to a change of the used data storage. Changing the data storage might alter the selection. This function is called if QmitkAbstractNodeSelectionWidget::SetDataStorage(mitk::DataStorage*) was called. Furthermore QmitkAbstractNodeSelectionWidget::SetSelectOnlyVisibleNodes(bool selectOnlyVisibleNodes) can be overridden to set the modus to (not) include invisible nodes in the selection. Default value is true. \b QmitkSingleNodeSelectionWidget The QmitkSingleNodeSelectionWidget is an example of a concrete node selection widget, subclassing the QmitkAbstractNodeSelectionWidget. It overrides the virtual functions to react on changes of the node selection. If the QmitkNodeSelectionButton of the widget is pressed, a QmitkNodeSelectionDialog is opened as a pop-up for data node selection. This dialog is a \ref QmitkNodeSelectionDialog_ref "QmitkNodeSelectionDialog" (see \ref DataStorageInspectors). It presents the currently known and active data storage inspectors for the user to select a single data node. If a selection is confirmed, the pop-up closes and the selected data node will be shown in the QmitkSingleNodeSelectionWidget. The user will see a thumbnail or an icon of the data node (if available) along with an HTML text of the data node info. The QmitkSingleNodeSelectionWidget is currently used inside plugin views to allow to select specific nodes of the data storage for further processing. A plugin view can connect a function to the CurrentSelectionChanged-signal to immediately react on a new selection of the QmitkSingleNodeSelectionWidget. Using QmitkSingleNodeSelectionWidget::GetSelectedNode the currently selected node of the selection widget can be retrieved anytime. Additionally an auto-selection-mode is available that automatically selects a node from the data storage if such a data storage is set and contains a node that matches the given predicate. The auto-selection-mode only works if the QmitkSingleNodeSelectionWidget does not already show a selected node. \b QmitkMultiNodeSelectionWidget The QmitkMultiNodeSelectionWidget is another example of a concrete node selection widget, subclassing the QmitkAbstractNodeSelectionWidget. It overrides the virtual functions to react on changes of the node selection. If the 'Change selection'-button is pressed a QmitkNodeSelectionDialog is opened as a pop-up for data node selection. This dialog is a \ref QmitkNodeSelectionDialog_ref "QmitkNodeSelectionDialog" (see \ref DataStorageInspectors). It presents the currently known and active data storage inspectors for the user to select a single data node. If a selection is confirmed, the pop-up closes and the selected data nodes will be shown in the QmitkMultiNodeSelectionWidget. Unlike single node selection the multi node selection is presented as a list of all the selected nodes. The QmitkMultiNodeSelectionWidget is currently used inside plugin views to allow to select a set of specific nodes of the data storage for further processing. A plugin view can connect a function to the CurrentSelectionChanged-signal to immediately react on a new selection of the QmitkMultiNodeSelectionWidget. Using QmitkAbstractNodeSelectionWidget::GetSelectedNodes the currently selected node of the selection widget can be retrieved anytime. - QmitkMultiNodeSelectionWidget::SetSelectionCheckFunction allows to define a function that puts a constraint on the node selection (dialog) to only allow specific combinations or seletions of nodes. + QmitkMultiNodeSelectionWidget::SetSelectionCheckFunction allows to define a function that puts a constraint on the node selection (dialog) to only allow specific combinations or selections of nodes. The result of the constraint check has to be either an empty string (indicating a valid node selection) or a string that explains the error / constraint violation. A simple check function can look like this: \code auto checkFunction = [](const QmitkMultiNodeSelectionWidget::NodeList & nodes) { if (!(nodes.size() % 2)) { std::stringstream ss; ss << "

Invalid selection.

The number of selected nodes must be uneven! the current number is " << nodes.size() << ".

"; return ss.str(); } return std::string(); }; multNodeSelectionWidget->SetSelectionCheckFunction(checkFunction); \endcode This check function will be passed to the QmitkNodeSelectionDialog and is used to print an error message, if an invalid selection is made inside the node selection dialog. This will prevent the user from confirming an even selection. If a valid selection is made no error message will be printed and the user can confirm the selection. \b QmitkNodeSelectionButton The QmitkNodeSelectionButton is a customized QPushButton and is used for example inside the QmitkSingleNodeSelectionWidget: It displays the thumbnail or icon of the selected data node (if available) along with an HTML text of the data node info. \section DataStorageViewerTestPlugin The DataStorageViewerTest plugin The \a org.mitk.gui.qt.datastorageviewertest plugin can be enabled and used inside the MITK workbench. It serves as a test / demo plugin with a single view that shows different uses of the new selection concept and its related classes. The following classes are used: -# Top row: A QmitkDataStorageListInspector -# Top right: A QmitkDataStorageTreeInspector -# Bottom left: A QmitkSingleNodeSelectionWidget -# Bottom right: A QmitkMultiNodeSelectionWidget The top row presents the two examples of an implementation of the QmitkAbstractDataStorageInspector. They display the whole content of the given data storage in two different ways. No node predicates are used in this case. Both inspectors come with two checkboxes to set each inspectors as a selection provider and a selection listener. With these checkboxes turned on the QmitkSelectionServiceConnector for each inspector is connected to the QmitkModelViewSelectionConnector of each inspector (as shown in \ref QmitkSelectionServiceConnector_ref "QmitkSelectionServiceConnector").
See how the selection also changes in the inspectors if nodes are selected in the mitk::DataManager and the Set as selection listener-checkbox is checked for the inspector. The bottom row presents the two examples of an implementation of the QmitkAbstractNodeSelectionWidget: They display a specific data node selection that was made by the user. Both widgets come with several checkboxes to modify the behavior of the selection widgets. */ diff --git a/Documentation/Doxygen/3-DeveloperManual/Starting/GettingToKnow/DocumentationGuide.dox b/Documentation/Doxygen/3-DeveloperManual/Starting/GettingToKnow/DocumentationGuide.dox index 353ee16c39..28b6c80176 100644 --- a/Documentation/Doxygen/3-DeveloperManual/Starting/GettingToKnow/DocumentationGuide.dox +++ b/Documentation/Doxygen/3-DeveloperManual/Starting/GettingToKnow/DocumentationGuide.dox @@ -1,84 +1,84 @@ /** \page DocumentationGuide Writing Documentation \section DocumentationGuideCodeGeneral General remarks MITK uses Doxygen for the generation of our user manual pages as well as for the generation of the on- and offline reference manuals. So on the technical side many questions can be answered by the doxygen documentation, such as the list of commands or a few basic doxygen tutorials. Therefore this document is not primarily intended as a guide to using doxygen, the doxygen manual does a much better job of that, but as a guide to use doxygen in MITK and a collection of helpful hints and advise about pittfalls. Also, of course you need to have doxygen installed to generate documentation. \section DocumentationGuideCode Documenting the source code -MITK is a substantial project and encompasses many different source files by many different developers over quite a considerable timeframe. Many of them have written excellent code which can do a lot of things and is very helpful to people who might apply it in wholly unlooked for ways to completely different problems. To facilitate this sharing and reusing of ressources one first and foremost has to know what kind of ressources are already available. +MITK is a substantial project and encompasses many different source files by many different developers over quite a considerable timeframe. Many of them have written excellent code which can do a lot of things and is very helpful to people who might apply it in wholly unlooked for ways to completely different problems. To facilitate this sharing and reusing of resources one first and foremost has to know what kind of resources are already available. Few people write code in the intention for it to be difficult to be used by others, but unfortunately what might seem a very efficient and easily understandable piece of code to the author might be nigh unreadable for someone else. Very often it does not in fact matter whether the code itself is understandable, as long as it one can get the information what a function is supposed to do. While comments in the source file help a lot to gain this knowledge in can get quite tedious go through every file looking for the right tool. This is where using doxygen pays off by giving a short comment in the header file a reference manual is automatically generated. While doxygen support several different manners of documentation, the MITK documentation should keep a uniform documentation style: \warning{ Use only the \verbatim /** ... */ \endverbatim style for documentation. } In dire emergencies you may consider commenting via the /// style, others must never be used. An example: \verbatim /** \brief Brief description what the commented part does. * * More detailed description. This can be as long as you like, * whereas the brief description should never be more than one sentence. */ \endverbatim See \subpage DocumentationExample for an exemplary documentation of a class. \subsection DocumentationGuideCodeHints Helpful hints:
  • Always put comments intended for doxygen in the header files.
\section DocumentationGuideManual Writing user manuals While the usage of your view/perspective/application might seem obvious and accessible to you, to most people it is not. Writing a good manual is key for this. It is very difficult to write a manual which is too comprehensive, most often if something can be done in a wrong way, somebody will see this as the only one. For MITK purposes you should put your documentation in BUNDLEPATH/documentation/UserManual/QmitkMyViewName.dox . Give them a unique name and remember to only use alphanumeric characters and underscores. Identifiers like "org.placeA.x" and "org.placeB.y" look identical to doxygen because it only parses the "org". Use "org_placeA_x" instead. Regarding images: Doxygen looks for images globally. To avoid confusion, include the plugin name into the image. E.g. "Checkboxes.png" is a bad name while "QmitkIGTTracking_Checkboxes.png" is a good name. Include images only via the image Macro! This way it is automatically included in the pdf export of the documentation. \verbatim // The Macro hast he following form (note the braces!): \imageMacro{imagePath, "Image Description", Image size in cm} // e.g.: \imageMacro{QmitkIGTTracking_Checkboxes.png, "Some beautiful Checkboxes!", 5} // If you must use commas in the description, escape them! \imageMacro{QmitkIGTTracking_Checkboxes.png, "Some beautiful Checkboxes\, you have never seen better ones! ", 5} \endverbatim Image size is only used for the pdf export, but images won't be displayed at all if it is missing. Maximum is 16. The nightly generated HTML documentation and the Qt Help System documentation can contain different content using the isHTML command. \subsection DocumentationGuideManualHints Helpful hints:
  • Do not use fullstops (".") in identifiers, it throws doxygen off
  • Think were your page should go in the MITK help page structure and declare it as a subpage accordingly
  • Use the imageMacro instead of the image command
  • Use structuring elements, such as sections and subsections
  • Use references to allow for fast navigation
  • Images, pictures and sketches are great, use them
  • Use visual help like remark, paragraph and warning
  • BLUEBERRY_USE_QT_HELP should be set to ON
  • The plug-in org.blueberry.ui.qt.help should be set to ON
*/ diff --git a/Documentation/Doxygen/3-DeveloperManual/Starting/GettingToKnow/StyleGuideAndNotes.dox b/Documentation/Doxygen/3-DeveloperManual/Starting/GettingToKnow/StyleGuideAndNotes.dox index 6ad26976e2..4cb7045a6e 100644 --- a/Documentation/Doxygen/3-DeveloperManual/Starting/GettingToKnow/StyleGuideAndNotes.dox +++ b/Documentation/Doxygen/3-DeveloperManual/Starting/GettingToKnow/StyleGuideAndNotes.dox @@ -1,466 +1,466 @@ /** \page StyleGuideAndNotesPage The MITK Style Guide and Technical Notes \tableofcontents The following document is a description of the accepted coding style for the Medical Imaging Interaction Toolkit (MITK). Developers who wish to contribute code to MITK should read and adhere to the standards described here. \section StyleGuideAndNotesPage_NameConventions Naming Conventions \li Using case change to indicate separate words @code ImageFilter PixelType DataStorage NodePredicateProperty @endcode \li Underscores are not used e.g. Image_Filer, _Node \li Variable names should convey the meaning behind the code @code BoundingBox::Pointer boundingBox = BoundingBox::New(); @endcode \li Names are generally spelled out @code mitk::DataNode* node; @endcode \li Abbreviation are allowable when in common use e.g. ROI for Region of Interest \subsection StyleGuideAndNotesPage_NamingClasses Naming Classes \li Classes are named beginning with a capital letter \li Classes are named according to the following general rule: @code class name = @endcode \li Examples of concepts \n Accessor: Access and convert between types e.g. NullScalarAccessor \n Container: A container of objects such as points or images e.g. VectorContainer \n Filter: A class that participates in the data processing pipeline e.g. AddImageFilter \n Mapper: Transform data from one form into another e.g. ContourMapper2D \n Reader/Writer: A class that reads/writes a single data object e.g. VtkSurfaceReader \n \subsection StyleGuideAndNotesPage_NamingFiles Naming Files \li MITK classes like @a ExampleClass should be in namespace @a mitk and their corresponding files should be named @a mitkExampleClass.h/.cpp. @code mitk::DataStorage @endcode \li Qt specific MITK classes like @a QmitkListView should have the prefix Qmitk in their class names and their corresponding files should be named @a QmitkListView.h/.cpp. @code QmitkDataStorageComboBox @endcode \li Header Files ends with an .h and \li Implementation Files with an .cpp or .txx for a template class \subsection StyleGuideAndNotesPage_NamingMethodsandFunctions Naming Methods and Functions \li Functions and methods are named beginning with a capital letter \li Referring to class methods in code, an explicit this-> pointer should be used @code mitk::DataStorage::SetOfObjects::ConstPointer all = this->GetAll(); @endcode \subsection StyleGuideAndNotesPage_NamingSignalSlots Naming Signal/Slots Methods and Functions \li Slots are named according to the following general rule @code On[variable name who send the signal][signal](); @endcode \li Example @code connect( loadImagePushButton, SIGNAL( clicked(bool ) ), SLOT( OnLoadImagePushButtonClicked( bool ) ) ); void mitk::Image::OnLoadImagePushButtonClicked( bool ) { ... Do something ... } @endcode \li Signals are named according to the following general rule @code Signal[MethodName](); @endcode \li Example @code emit SignalFinishedSegmentation(); @endcode \subsection StyleGuideAndNotesPage_NamingClassDataMembers Naming Class Data Members \li Class data members are prefixed with m_ @code m_Volumes m_OffsetTable m_ImageMask @endcode \li An exception to this rule, Qt class Data members are not prefixed and begin with a lower-case letter @code loadImageButton closeImageAction @endcode \subsection StyleGuideAndNotesPage_NamingLocalVariables Naming Local Variables \li Local variables first letter is lower-case @code offset data slicesIt @endcode \subsection StyleGuideAndNotesPage_NamingQtVariables Naming Qt Variables \li GUI variables ends with name of used QT tool. @code QPushButton* loadImagePushButton; QAction* closeImageAction; QCheckBox* hideImageCheckBox; QRadioButton* binaryImageRadioButton; @endcode \subsection StyleGuideAndNotesPage_NamingTypedefs Naming Typedefs \li Typedefs must end in the word Type @code typedef TPixel PixelType; typedef itk::Image< TPixel, VImageDimension > ImageType; typedef std::list ImageListType; @endcode \section StyleGuideAndNotesPage_Pointer Pointer \subsection StyleGuideAndNotesPage_DeclarationofPointers Declaration of Pointers \li Position of * pointers are connected with the variable @code int *counter; @endcode \li Analog to references @code int &counter; @endcode \subsection StyleGuideAndNotesPage_SmartPointer SmartPointer \li SmartPointers must be used for classes that have itk::Object as a base class. \li Assignment of a just created instance to a normal pointer results in a crash, since the reference count is decreased immediately to zero and the object is destroyed. @code itk::Object::Pointer object = itk::Object::New(); @endcode \li Static declarations are also forbidden and result into an exception when the scope of the variable is left, because the destructor is called while the reference count is still greater than zero. \li Note that using smart pointers requires using real (normal) pointers when setting input. If you want to return a newly created smart pointer that is not also kept within the class (e.g., if you write a Clone method), you have to return a smart pointer on output (compare itkMacro.h). If the smart pointer is kept within the class, returning a real (normal) pointer is sufficient. \li Testing a SmartPointer against NULL is done with the IsNull() and Is- NotNull() methods. A simple ==NULL issues a warning. \section StyleGuideAndNotesPage_Namespace Namespace \li MITK classes should be in namespace @a mitk @code mitk::Image::Pointer mitk::ImageGenerator::MakeImage() { // already in namespace mitk here! Image::Pointer image = mitk::Image::New(); ImageDecorator::Pointer decorator = mitk::ImageDecorator::New(); d->Decorate( image ); return image; } @endcode \li Constants in MITK for mitk::Operation and mitk::Action are set in namespace, so don't forget to add prefix mitk:: @code switch (actionId) { case mitk::AcMOVESELECTED: ....Do something ... break; default: break; } @endcode \section StyleGuideAndNotesPage_CodeLayoutandIndentation Code Layout and Indentation \subsection StyleGuideAndNotesPage_GeneralLayout General Layout \li Each line of code should take no more than 120 characters. \li Use lots of whitespace to separate logical blocks of code, intermixed with comments -\li DO NOT USE TABS. The standard indention is 2 spaces (see ITK Style Guide). Configure your +\li DO NOT USE TABS. The standard indentation is 2 spaces (see ITK Style Guide). Configure your editor accordingly. \li DO NOT USE trailing whitespaces \li Declaration of variables should be one declaration per line @code int sliceNumber; char* stringName; ImageType::Pointer image; @endcode \subsection StyleGuideAndNotesPage_ClassLayout Class Layout \li Copyright @code /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ @endcode \li Includes [A .. Z] @code #include "... .h" @endcode \li Namespace @code namespace mitk { @endcode DO NOT litter your header with "using namespace;"! \li Class (Template) @code template class ClassName : public ImageBase { @endcode \li Typedefs @code public: ....typedefs.... @endcode \li Methods @code public: ....methods.... protected: ....methods.... private: ....methods.... @endcode \li QT Signals @code signals: Signal...(); @endcode \li QT Slots @code public slots: On...(); protected slots: On...(); @endcode \li Data Member @code private/protected: ....class data members.... }; } #endif @endcode \section StyleGuideAndNotesPage_UseofBraces Use of Braces \li Used to delimit the scope of an if, for, while, switch. \li Braces are placed on a line by themselves: @code for ( unsigned int i = 0; i < 3; ++i ) { ... do something ... } @endcode or @code if ( condition ) { ... do something ... } else if ( other condition ) { ... do something ... } else { ... do something ... } @endcode \li You can choose to use braces on a line with a code block when the block consists of a single line: @code if ( condition ) { foo = 1; } else if ( condition2 ) { foo = 3; } else { return; } @endcode or @code for ( unsigned int i = 0; i < 3; ++i) { x[i] = 0.0; } @endcode \section StyleGuideAndNotesPage_IncludeGuards Include Guards \li \#include guard is a particular construct used to avoid the problem of double inclusion when dealing with the \#include directive. \li Naming convention for \#include guards is: ClassName_h \li Following example demonstrates a problem that can arise if \#include guards are missing: Here, the file child.cpp has indirectly included two copies of the text in the header file grandfather.h. This causes a compilation error, since the structure type foo is apparently defined twice. @code grandfather.h struct foo { int m Member; }; father.h #include "grandfather.h" child.h #include "grandfather.h" #include "father.h" @endcode \subsection StyleGuideAndNotesPage_Useofincludeguards Use of \#include guards \li Here, the first inclusion of grandfather.h causes the macro grandfather h to be defined. Then, when child.cpp includes grandfather.h the second time, the \#ifndef test fails, and the preprocessor skips down to the \#endif, thus avoiding the second definition of struct foo. The program compiles correctly. @code grandfather.h #ifndef grandfather h #define grandfather h struct foo { int m Member; }; father.h #include "grandfather.h" child.h #include "grandfather.h" #include "father.h" @endcode \section StyleGuideAndNotesPage_TechnicalNotes Some Technical Notes \li Use forward declarations in header files wherever possible. Only include those header files in a header file that are really necessary. Include the rest in the implementation file. \li For classes inheriting directly or indirectly from @a itk::LightObject (most of the MITK-classes do so), the class definition should include the mitkClassMacro. Additionally, if the class can be instantiated (normally the case, if the class is not abstract) and has @em only a constructor without parameters, the constructor should be declared protected and the @a itkFactorylessNewMacro should be used to create a @a New() method for instantiation. Here is an example: @code class ExampleClass : public SuperClassOfTheExampleClass { public: mitkClassMacro(ExampleClass, SuperClassOfTheExampleClass) itkFactorylessNewMacro(Self) [...] protected: ExampleClass(); virtual ~ExampleClass(); } @endcode \li Set- and Get-methods can be created with the macros @a itkSetObjectMacro(name,type) and @a itkGetObjectMacro(name,type), respectively, if the @a type is derived from @a itk::LightObject or @a itk::Object. \li When using inner classes of a parent class which is templated, you have to use the keyword @a typename for gcc 3.x and standard compliance. For example, @a TreeChangeListener is an inner class of @a Tree, therefore use: @code class LinkedTree : public Tree { public: typedef typename LinkedTree::TreeChangeListener TreeChangeListener; [...] } @endcode Another example: @code typename std::vector::iterator pos = treeChangeListenerList.begin(); @endcode @a iterator is an inner class of @a vector. \li Constants in MITK for mitk::Operation and mitk::Action are set in namespace, so don't forget to add prefix @a mitk:: @code switch (actionId) { case mitk::AcMOVESELECTED: @endcode Prefixes for the constants are to be used like corresponding others. See file @a Interactions\\mitkBaseInteraction\\mitkInteractionConst.h for further details. \section StyleGuideAndNotesPage_AutomaticCodeFormatting Automatic Code Formatting We offer a .clang-format file, which can be used to automatically format code acceptably. For an explanation of the different options check out https://clang.llvm.org/docs/ClangFormatStyleOptions.html */ diff --git a/Documentation/Doxygen/3-DeveloperManual/Starting/GettingToKnow/Tutorial/Step05.dox b/Documentation/Doxygen/3-DeveloperManual/Starting/GettingToKnow/Tutorial/Step05.dox index 96ff8fc34f..ae31c5ecaa 100644 --- a/Documentation/Doxygen/3-DeveloperManual/Starting/GettingToKnow/Tutorial/Step05.dox +++ b/Documentation/Doxygen/3-DeveloperManual/Starting/GettingToKnow/Tutorial/Step05.dox @@ -1,38 +1,38 @@ /** \page Step05Page MITK Tutorial - Step 5: Interactively add points \li Examples/Tutorial/Step5 \li https://www.mitk.org/download/tutorial-data/Pic3D.nrrd (image) \n https://www.mitk.org/download/tutorial-data/lungs.vtk (surface) In addition to Step 4 where 3 views were created on the data, we now want to interactively add points. A node containing a PointSet as data is added to the data tree and a PointSetDataInteractor is associated with the node, which handles the interaction. The @em interaction @em pattern is defined in a state-machine, stored in an external XML file. Thus, we need to load a state-machine. - A state machine describes interaction pattern with different states (states beeing something like "a point is selected") and transitions to these states (e.g. "select a point"). + A state machine describes interaction pattern with different states (states being something like "a point is selected") and transitions to these states (e.g. "select a point"). These transitions are associated with actions. In this way it is possible to model complex interaction schemes. By what these transitions and actions are triggered is described in a configuration file. It maps user events to identifiers that are used in the state machine patterns. In this way the user interaction can be changed by simply loading a different configuration file for a state machine, and the user may add points now with a right click instead of left click + SHIFT, as in our case. Therefore after loading the state machine pattern the PointSetDataInteractor is also given a event configuration file. More information about interaction in MITK can be found \ref InteractionPage "here". In order to add a point the shift key has to be pressed while left clicking in a render window. You can also move points or remove them (left click while pressing ALT). \image html step5_result.png \dontinclude Step5.cpp A PointSet and a node for it have to be created to be able to interactively adding points: \skipline mitk::PointSet \until interactor->SetDataNode(pointSetNode) \ref Step04Page "[Previous step]" \ref Step06Page "[Next step]" \ref TutorialPage "[Main tutorial page]" */ diff --git a/Documentation/Doxygen/3-DeveloperManual/Starting/GettingToKnow/Tutorial/Step06.dox b/Documentation/Doxygen/3-DeveloperManual/Starting/GettingToKnow/Tutorial/Step06.dox index 291199950c..fb53b1fe14 100644 --- a/Documentation/Doxygen/3-DeveloperManual/Starting/GettingToKnow/Tutorial/Step06.dox +++ b/Documentation/Doxygen/3-DeveloperManual/Starting/GettingToKnow/Tutorial/Step06.dox @@ -1,110 +1,110 @@ /** \page Step06Page MITK Tutorial - Step 6: Use an interactive region-grower The source is now split among several files: \li Examples/Tutorial/Step6 \li https://www.mitk.org/download/tutorial-data/Pic3D.nrrd (image) In this step the program is enhanced by the possibility to start a region-grower at interactively added points. We will see how MITK images can be accessed as ITK images. We now load the image file Pic3D.nrrd only since the surface will be the result of the region-growing. Add points in the image by pressing SHIFT+left mouse key, then adjust the thresholds and press 'Start region growing'. \imageMacro{step6_result.png,"",13.55} The class Step6 inherits from QWidget and provides methods for setting up the widgets. %Step6RegionGrowing.cpp contains a method for performing the region-growing. %Step6main.cpp contains main. Like in ITK and VTK class member names start with m_ followed by the proper member name starting with a capital letter (e.g. m_Tree). Function names start with capital letters. To learn more about style conventions in MITK read \ref StyleGuideAndNotesPage "The MITK Style Guide". \dontinclude Step6.cpp The widgets are initialized as in the previous steps but with an additional QVBox for a button to start the segmentation: \skipline Create controlsParent \until hlayout->addWidget(m_LineEditThresholdMax) This creates a button to start the segmentation and its clicked() signal is connected to the method StartRegionGrowing(): \dontinclude Step6.cpp \skipline QPushButton *startButton \skipline connect(startButton \section AccessMTIKImagesAsITKImagesSection Access MITK images as ITK images ITK images are templated whereas mitk::Images are not. To use ITK filters with MITK images, we have to convert from MITK to ITK. To do so, first define an access method, which is templated as an ITK image is: \code template MyAccessMethod(itk::Image* itkImage) { ... } \endcode If you don't understand this template syntax, you should read any C++ text book. Understanding template syntax is crucial to successfully using ITK. To call this templated method with an (untemplated) mitk::Image, you can use the AccessByItk macro from mitkImageAccessByItk.h. This macro checks for -the actual image type of the mitk::Image and does any neccessary conversions. Look into "Modules / Adaptor classes" for more information. +the actual image type of the mitk::Image and does any necessary conversions. Look into "Modules / Adaptor classes" for more information. \code AccessByItk(mitkImage, MyAccessMethod) \endcode \dontinclude Step6RegionGrowing.txx In this step our access method is called RegionGrowing() (defined in %Step6RegionGrowing.txx ): \skipline template \until } //RegionGrowing() Additionally the access function has to be instantiated for all datatypes and two/three dimensions as some compilers have memory problems without this explicit instantiation, some even need instantiations in separate files for 2D/3D: \n For 2D in %Step6RegionGrowing1.cpp : \dontinclude Step6RegionGrowing1.cpp \skipline InstantiateAccessFunctionForFixedDimension ... and for 3D in %Step6RegionGrowing2.cpp : \dontinclude Step6RegionGrowing2.cpp \skipline InstantiateAccessFunctionForFixedDimension \dontinclude Step6.cpp The method StartRegionGrowing() finally calls our access method RegionGrowing(): \skipline Step6::StartRegionGrowing \until } \section ConvertingITKMITKSection Converting ITK images to MITK images and vice versa In some cases it is useful to simply convert between ITK and MITK images. The direction ITK to MITK is easy, since mitk::Image can handle most data types. The direction MITK to ITK is more critical, since ITK images have to be instantiated with a fixed pixel type and fixed dimension at compile time. \li \code mitk::Image mitk::ImportItkImage(itk::Image<...>) \endcode \li \code mitk::CastToItkImage(mitkImage, itk::Image<...>) \endcode \section ConnectingMITKToVTKSection Connecting MITK images to VTK Images are not converted or copied: The data array is just accessed via an encapsulating VTK object. \li \code vtkImageData* mitk::Image::GetVtkImageData(int time = 0) \endcode \section SurfacesMITKToVTKSection MITK Surfaces to VTK and vice versa Again: not a conversion, just accessing. \li \code vtkPolyData* mitk::Surface::GetVtkPolyData(int time = 0) \endcode \li \code mitk::Surface::SetVtkPolyData(vtkPolyData*, int time = 0) \endcode \ref Step05Page "[Previous step]" \ref Step07Page "[Next step]" \ref TutorialPage "[Main tutorial page]" */ diff --git a/Documentation/Doxygen/3-DeveloperManual/Starting/GettingToKnow/Tutorial/Step10.dox b/Documentation/Doxygen/3-DeveloperManual/Starting/GettingToKnow/Tutorial/Step10.dox index 648fcc9361..d8f233136f 100644 --- a/Documentation/Doxygen/3-DeveloperManual/Starting/GettingToKnow/Tutorial/Step10.dox +++ b/Documentation/Doxygen/3-DeveloperManual/Starting/GettingToKnow/Tutorial/Step10.dox @@ -1,501 +1,501 @@ /** \page Step10Page MITK Tutorial - Step 10: Adding new Interaction \tableofcontents \section HowToUseDataInteractor Step 01 - How to use an existing DataInteractor in your Module/Plugin/... MITK provides finished DataInteractors for a variety of tasks, they can be found in Core/Code/Interactions. They can be used with state machine patterns and config files located under Core/Code/Resources/Interactions. A mitk::DataInteractor consists of four parts. The class describing the functionality and two XML files; one describes the state machine pattern, that is the workflow of an interaction and the second describes the user events which trigger an action. Lastly every mitk::DataInteractor works on a mitk::DataNode in which it stores and manipulates data. To use a mitk::DataInteractor these parts have to be brought together. ** TODO add code of mitk::PointSetDataInteractor Plugin .. This code demonstrates the use of an existing mitk::DataInteractor exemplary for the mitk::PointSetDataInteractor: First we need a mitk::DataNode in which the PointSets is stored. It has to be added to the mitk::DataStorage. \code mitk::DataNode::Pointer dataNode = mitk::DataNode::New(); GetDataStorage()->Add(dataNode.GetPointer()); \endcode Then we create an instance of a mitk::PointSetDataInteractor and load a predefined statemachine pattern as well as a configuration for it: \code m_CurrentInteractor = mitk::PointSetDataInteractor::New(); m_CurrentInteractor->LoadStateMachine("PointSet.xml"); m_CurrentInteractor->SetEventConfig("PointSetConfig.xml"); \endcode Lastly the mitk::DataNode is added to the mitk::DataInteractor \code m_CurrentInteractor->SetDataNode(dataNode); \endcode now the mitk::DataInteractor is ready for usage. \section HowToModifyInteraction Step 02 - How to modify the behaviour of a DataInteractor The behavior of a mitk::DataInteractor is determined by two aspects. One, the state machine pattern which describes the flow/order of actions that are performed. Secondly the configuration which determines which user interaction triggers the actions. \subsection ModifyDisplayInteractionBehavior How to modify the display interaction behavior Sometimes it may be desirable to change the behaviour of the mitk::DisplayActionEventBroadcast which controls zooming, panning and scrolling, e.g. when a Tool is activated that reacts to the same events. Changing the behavior of the DisplayActionEventBroadcast (or possibly any other EventHandler) can be achieved from anywhere in the code by requesting the InteractionEventObserver and assigning an alternative configuration to it, as demonstrated in this example: \code std::ifstream* configStream = new std::ifstream( #path to alternative configuration file# ); mitk::EventConfig newConfig(configStream); // Requesting all registered EventObservers std::list listEventObserver = GetModuleContext()->GetServiceReferences(); for (std::list::iterator it = listEventObserver.begin(); it != listEventObserver.end(); ++it) { auto* displayActionEventBroadcast = dynamic_cast(GetModuleContext()->GetService(*it)); // filtering: only adjust the displayActionEventBroadcast if (nullptr != displayActionEventBroadcast) { displayActionEventBroadcast->SetEventConfig(newConfig); } } \endcode \section SectionImplementationDataInteractor How to implement a new DataInteractor This second part of the tutorial step goes beyond the activation of an interactor, that modifies data by user interaction) as shown above. It shows what needs to be implemented to add a new way of interaction within your MITK application. Please see \ref DataInteractionPage as an introduction to the MITK interaction mechanism. This tutorial is structured as follows: The first section deals with config files, describing all the parameters of events and how to use them in a configuration file. In the second section the basics are described that are needed to write a state machine pattern. The last section -deals with brining configuration, state machine pattern and code together and gives an exemplary implementation of a mitk::DataInteractor. +deals with bringing configuration, state machine pattern and code together and gives an exemplary implementation of a mitk::DataInteractor. \section ConfigFileDescriptionSection How to create a Config-File \subsection EventDescriptionSection Event Description Events are described by their parameters. Each event type has its own set of parameters that can be set in the configuration file. If a parameter is omitted it is set to its default value. All possible parameters are listed and described below. Event parameters are also described in the documentation of the event class itself. Mandatory for each event description is the event class and the event variant. The parameters of an event are set by attribute tags. \note Refer to \ref EventClassSection for the meaning of event class. \b Mouse \b Buttons \n mitk::InteractionEvent::MouseButtons represent the buttons. They can be used for two attributes. First the EventButton attribute which describes the button that triggered the event, this always is a single button. Secondly the ButtonState attribute that describes which buttons were pressed at the moment the event has been generated. For example assume the right mouse button and the middle mouse button are already pressed, now the left mouse button is pressed too and generates a second event, this would be described as follows: \code \endcode Note: Technically the LeftMouseButton is also pressed and should be listed in the ButtonState, but this is taken care of by the mitk::EventFactory . Key Events \n mitk::InteractionKeyEvent represents a pressed key, which key is pressed is provided with the Key attribute like this \code \endcode or \code \endcode \note Key Events do not require an explicit configuration, for all key events there exists a predefined event variant with the name 'Std' + value, that is key a is named 'StdA'. The names for special keys are listed here: \dontinclude mitkInteractionEvent.h \skipline // Special Keys \until // End special keys Modifier Keys \n mitk::InteractionEvent::ModifierKeys represent the combination of pressed modifier keys, several modifier keys pressed at the same time are denoted by listing them all separated by commas. \code \endcode \b ScrollDirection \n This attribute is unique to the mitk::MouseWheelEvent and describes the direction in which the mouse wheel is rotated. In the event description actual only the direction is provided, but the event is generated with the actual value, and this value can be retrieved from the object. \code \endcode \subsection ExamplesSection Examples Examples for key events: \code \endcode Examples for MousePress events: \code \endcode There exists a standard configuration file for the most common events called GlobalConfig.xml that can be used to as a default and can be extended by a specific definition. \subsection ParameterDescriptionSection Parameter Description It is also possible to store parameters in the config file. Those are stored using the param-tag, like this: \code \endcode Within the application these properties can then be access via a mitk::PropertyList like this: \code // sm - state machine loaded with config file example2 mitk::PropertyList::Pointer properties = GetAttributes(); std::string prop1; properties->GetStringProperty("property1",prop1); \endcode \section HowToStateMachine HowTo Write a State Machine A state machine pattern is described in a XML file. \subsection StateSection States States are described using the state-tag. Each state has to have a name. Exactly one state has to be a start state in each state machine to indicate the state in which the state machine is set when it is constructed. So a valid, but rather useless state machine would like like this: \code \endcode Optionally a state can be assigned a special mode that influences the event distribution. These modes are GRAB_INPUT , PREFER_INPUT and REGULAR (where REGULAR is default and does not need to be indicated). See \ref DataInteractionTechnicalPage_DispatcherEventDistSection for a description of these modes. Use the special modes only when necessary as they prevent other DataInteractors to receive events. \code \endcode \subsection TransitionSection Transitions Transitions are part of a state and describe all possible state switches, and are therefore important for modeling an interaction scheme. Transitions consist a part that describes the event which triggers the transition (event class and event variant) and a target which is state to which the state machine switches after executing a transition. An event class describes the event type (see mitk::InteractionEvent for the different classes) and the event variant is a specification thereof and the exact description is taken from a config file. Together they determine which event can trigger this transition. For example this state machine will switch from state A to state B when the StdMousePressPrimaryButton event (left mouse button is pressed) occurs. \subsection EventClassSection Event Class -The event class description supports the polymorphism of the event classes. Therefore state machine patters should be written in the most +The event class description supports the polymorphism of the event classes. Therefore state machine patterns should be written in the most general ways possible. So for a given class hierarchy like this: \dot digraph { node [shape=record, fontname=Helvetica, fontsize=10]; a [ label="{InteractionPositionEvent}"]; b [ label="{MousePressEvent}" ]; c [ label="MouseReleaseEvent" ]; d [ label="TouchEvent", style=dotted ]; a -> b; a -> c; a -> d; } \enddot in the state machine pattern the mitk::InteractionPositionEvent can be declared as event class to restrict to the events which hold a position information. The actual implementation is then given in the configuration file. In this case it allows to define events of the classes mitk::InteractionPositionEvent itself, or mitk::MousePressEvent, mitk::MouseReleaseEvent, mitk::TouchEvent. This has the advantage that the patterns remain the same no matter what input devices are used, and the state machine patterns can be configured for newly added event classes as long as they match the class hierarchy (this ensures they hold the necessary properties). \code \endcode \subsection ActionSection Actions Actions can be added to transitions and represent functions in the mitk::DataInteractor that are executed on taking a transition. The following simple state machine will listen for left mouse clicks and execute two actions (and actually never stop). \code \endcode In order to tell the mitk::DataInteractor which function to execute these actions are made known to the mitk::DataInteractor using the CONNECT_FUNCTION macro. This example assumes that there exists an ExampleInteractor which inherits from mitkDataInteractor. This class implements the functions AddPoint and CountClicks. The actions are introduced by implementing the virtual method ConnectActionsAndFunctions(): \code void mitk::ExampleInteractor::ConnectActionsAndFunctions() { CONNECT_FUNCTION("addPoint", AddPoint); CONNECT_FUNCTION("countClicks", CountClicks); } \endcode \subsection ConditionSection Conditions Conditions can be added to transitions and represent functions in the mitk::DataInteractor that are executed on taking a transition. A condition is used to determine if a following action should be executed or not. \code \endcode In order to tell the mitk::DataInteractor which function to execute these conditions are made known to the mitk::DataInteractor using the CONNECT_CONDITION macro. The ConnectActionsAndFunctions() method has to be augmented accordingly: \code void mitk::ExampleInteractor::ConnectActionsAndFunctions() { CONNECT_CONDITION("checkPoint", CheckPoint); CONNECT_FUNCTION("addPoint", AddPoint); CONNECT_FUNCTION("countClicks", CountClicks); } \endcode \section ReferenceToIncludeFiles Integration of the pattern and configuration files The usage of custom files slightly differs from the existing ones. Custom pattern and config files have to be stored in the /Resources/Interactions directory of the Module that they were designed for. When loading files from a module location into an interactor, the module has to be supplied as a parameter: \code m_CurrentInteractor = mitk::CustomDataInteractor::New(); m_CurrentInteractor->LoadStateMachine("CustomStateMachinePattern.xml", us::GetModuleContext()->GetModule()); m_CurrentInteractor->SetEventConfig("CustomConfig.xml", us::GetModuleContext()->GetModule()); \endcode See \ref IncludeFiles for a description. \section HowToDataInteractor Implementation of a new mitk::DataInteractor DataInteractors are to inherit from mitk::DataInteractor. Their functionality is implemented in functions that follow this interface: For Actions: \code bool SomeFunctionality(StateMachineAction* , InteractionEvent*); \endcode For Conditions: \code bool SomeFunctionality(const InteractionEvent*); \endcode Your functions are connected with actions and conditions by implementing the function ConnectActionsAndFunctions(), e.g. \code void mitk::ExampleInteractor::ConnectActionsAndFunctions() { CONNECT_CONDITION("checkPoint", CheckPoint); CONNECT_FUNCTION("addPoint", AddPoint); CONNECT_FUNCTION("enoughPoints", EnoughPoints); } \endcode Now all that is left is to write a state machine pattern and a config file as is described in the tutorials. To provide a useful example the mitk::PointSetDataInteractor is annotated with comments that describe the important parts for an implementation of a mitk::DataInteractor. This step assumes knowledge of the Interaction concept described in \ref DataInteractionPage and some background of the implementation. Please refer to these pages before proceeding. Now all that is left it to write a state machine pattern and a config file as is described in the tutorials. \subsection ExampleInternalEvent Example Interactor using InternalEvent A useful tool in creating DataInteractors is mitk::InternalEvent which allows the mitk::DataInteractor to send signals on its own. The following will describe how to build a mitk::DataInteractor that allows to add points until a certain number of points is reached. The number of accepted points is provided in the config file as a parameter. So we start by writing a state machine pattern that add points until it receives an mitk::InternalEvent telling it, that enough points have been added. \code <--! dead state, nothing happens any more, once we reached this --> \endcode In our config file we set the number of maximal points to 10, and define AddPointClick as a right mouse click with the ctrl button pressed. \code \endcode The implementation is described in the following. \see Step10.h \see Step10.cpp \dontinclude Step10.h Implementation of protected functions: \skipline protected: \until virtual void ConfigurationChanged(); ConnectActionsAndFunctions - Is inherited from mitk::InteractionStateMachine, here action strings from the xml are connected with functions in the mitk::DataInteractor (as seen above). In our example this looks like this: \dontinclude Step10.cpp \skipline void mitk::ExampleInteractor::ConnectActionsAndFunctions() \until } ConfigurationChanged - Is called whenever a new configuration file is loaded (by the mitk::InteractionEventHandler super class), this function allows to implement initialization code that depends on configuration values. In our example we want to set the limit of allowed points: \dontinclude Step10.cpp \skipline void mitk::ExampleInteractor::ConfigurationChang \until } Next the actual functionality of the DataInteractor is implemented, by providing one function per action, following this prototype described before. \dontinclude Step10.h \skipline private: \until bool CheckPoint(cons \dontinclude Step10.cpp \skipline bool mitk::ExampleInteractor::AddPoint(StateM \until //- If the conditions returns false the calling transition and the included actions will not be executed. If a condition fails the event is considered as untreated, and will be offered to other Interactors. \dontinclude Step10.cpp \skipline bool mitk::ExampleInteractor::CheckPoint( \until //end Here we see an internal event used to signal that the point set reached the maximal number of allowed points. The event is created and added to the Dispatchers event queue. \dontinclude Step10.cpp \skipline // create internal \until positionEvent->GetSender( \note Internal events do not need any mapping to event variants. Their signal name is equivalent with the event variant. There are also two documented classes implementing a mitk::DataInteractor and a mitk::InteractionEventObserver which can be looked at for further understanding: \see mitk::PointSetDataInteractor \see mitk::DisplayActionEventBroadcast Have fun with creating your own interaction and please think about contributing it to MITK! If you meet any difficulties during this step, don't hesitate to ask on the MITK mailing list mitk-users@lists.sourceforge.net! People there are kind and will try to help you. \ref Step09Page "[Previous step]" \ref TutorialPage "[Main tutorial page]" */ diff --git a/Documentation/Doxygen/3-DeveloperManual/Starting/SettingUpMITK/BuildInstructions.dox b/Documentation/Doxygen/3-DeveloperManual/Starting/SettingUpMITK/BuildInstructions.dox index 92ddf13647..7affb69a0b 100644 --- a/Documentation/Doxygen/3-DeveloperManual/Starting/SettingUpMITK/BuildInstructions.dox +++ b/Documentation/Doxygen/3-DeveloperManual/Starting/SettingUpMITK/BuildInstructions.dox @@ -1,229 +1,229 @@ /** \page BuildInstructionsPage Build Instructions \tableofcontents \section BuildInstructions_Introduction Introduction The CMake-based build system of MITK supports a "superbuild" process, meaning that it will download, configure, and build all required third-party libraries (except Qt) automatically. These instructions will show you how to use the MITK superbuild. \note This page explains explicitly how to build MITK itself. If you want to create your own project based on MITK, the process described below is completely automated. Please see \ref HowToNewProject. For more advanced users, the last sections explains how to inject custom build libraries into the superbuild process. \section BuildInstructions_Prerequisites Prerequisites You need: -# Git (there are also numerous third-party graphical clients available). We recommend using Git, but see below for a way how to get the current source code without using it. -# CMake (version \minimumCMakeVersion or higher) -# Qt \minimumQt6Version if you plan to develop Qt-based applications -# If you are using macOS you need an XCode installation and the - Command Line Tools as it provides the neccessary compilers and SDKs + Command Line Tools as it provides the necessary compilers and SDKs To build MITK on Linux, install the following packages, e. g. with APT: \code{.unparsed} sudo apt install build-essential doxygen git graphviz libegl-dev libfreetype6-dev libglu1-mesa-dev libopengl-dev libssl-dev libtiff5-dev libxcomposite1 libxcursor-dev libxcb-cursor-dev libxdamage-dev libxi-dev libxkbcommon-dev libxt-dev mesa-common-dev \endcode For the optional and experimental (!) Python integration, install NumPy and SimpleITK v1.x, e. g.: \code{.unparsed} sudo apt install python3-numpy python3-pip pip3 install SimpleITK \endcode \section BuildInstructions_Qt A note about Qt As we do not provide Qt in the MITK superbuild you need to install Qt manually. The Qt Company provides online installers for all supported platforms. We highly recommend to install Qt to the default location of the installer as it will allow MITK to automatically find Qt without any further action needed. Make sure to also select the following required components: - Qt 5 Compatibility Module - Qt State Machines - Qt WebEngine On Windows, the Qt installer offers a welcome and straight forward way to install OpenSSL. You find it under the Tools node. \section BuildInstructions_Get_Source Get a source tree Since MITK is under active development we recommend to use Git to check out the latest stable release from the homepage. If you decide to use the most current nightly release, make sure to get a stable tree: Check the MITK dashboard before checking out. If the build tree is not clean, you can specify an older revision for the checkout or get a stable tar ball from www.mitk.org. To clone MITK's current Git repository do: \code git clone https://phabricator.mitk.org/source/mitk.git MITK \endcode \section BuildInstructions_Build_With_CMake Build MITK with CMake Create a new directory for the superbuild binary tree, change to it and call CMake: In the shell (assuming your current directory is the same as the one where you issued the git clone command): \code mkdir MITK-superbuild cd MITK-superbuild ccmake ../MITK \endcode If you use Windows or prefer to use the CMake GUI, start the CMake GUI and enter the location of the source tree and binary tree, choose a suitable generator and configure the project. CMake will present you a couple of options, these are the most important ones: - CMAKE_PREFIX_PATH The path to your Qt installation, e.g., C:/Qt/5.12.9/msvc2017_64 or /home/user/Qt/5.12.9/gcc_64 - MITK_USE_BLUEBERRY Build the BlueBerry application framework - MITK_USE_Boost_LIBRARIES If you need binary Boost libraries, specify them here. - MITK_USE_Python3 Enables Python wrapping in MITK. This will also configure ITK and VTK to build Python wrappers. - MITK_USE_Qt6 Build MITK code which depends on Qt 6 If you are satisfied with the configuration of your MITK superbuild, generate the project files with CMake by pressing "Generate". Linux and macOS users usually just enter "make" (optionally supplying the number threads to be used for a parallel build): \code make -j6 \endcode Windows users using Visual Studio can open the generated MITK-superbuild.sln solution file in the MITK-superbuild directory and start the build by building the BUILD_ALL project. \section BuildInstructions_Customize Customize your MITK superbuild The MITK superbuild configures MITK as well as all external libraries. The build directories of these libraries, and of MITK itself are located inside the MITK-superbuild directory. For example, the directory layout may look like: \code MITK-superbuild |- ep "external projects" |-bin |-lib |-include |-src |- MITK-build \endcode To change the configuration of the MITK build itself, choose the MITK-build directory as the binary directory in the CMake GUI (not the MITK-superbuild directory). After generating the project files, build the MITK project by either issuing "make" in the MITK-build directory (Linux, macOS), or by opening MITK-build/MITK.sln (Windows). You may also change the configuration of any project configured via the superbuild process. Make sure to also build the changed project and also the projects which depend on it. \section BuildInstructions_Running Running Applications On Linux, just execute the application you want to run. MITK executables are located in MITK-superbuild/MITK-build/bin On Windows, the PATH environment variable must contain the directories containing the third-party libraries. This is automatically done from Visual Studio. For running the applications directly use the generated batch files in the MITK-superbuild/MITK-build/bin. \section BuildInstructions_Documentation Documentation If you have the Doxygen documentation tool installed, you get a new project (Visual Studio) or "make" target named "doc". You can build this to generate the HTML documentation of MITK in the Documentation/Doxygen directory of your MITK-build binary tree or in the MITK_DOXYGEN_OUTPUT_DIR CMake variable (if specified). \section BuildInstructions_As_Toolkit Use MITK in your own project (as a toolkit) To use MITK in your external project, add the CMake command find_package(MITK REQUIRED) to your CMakeLists.txt and make use of the CMake macros mitk_create_module() and mitk_create_executable() provided by MITK. Here is a very basic example CMakeLists.txt including MITK as a project: \code cmake_minimum_required(VERSION 3.18 FATAL_ERROR) project(MyProject) find_package(MITK 2023.12 REQUIRED) add_executable(MyApp main.cpp) target_link_libraries(MyApp MitkCore) \endcode with the main.cpp being \code #include #include int main() { MITK_INFO << "Hello world!"; return 0; } \endcode \section BuildInstructions_Advanced_Customization Superbuild customization You can inject pre-build third-party libraries into the MITK superbuild by setting certain CMake variables before the first configure step. MITK will then use these third-party libraries instead of downloading and building them by itself. Note that you must take care of configuring those libraries with all options MITK requires. The variables listed below are provided for injecting third-party libraries. Their occurrence in the CMake GUI or in ccmake may depend on specific MITK_USE_* options set to ON. You may also use the variable names below without the EXTERNAL_ prefix, for example when providing their values on a command line call to CMake. - EXTERNAL_BOOST_ROOT Set this variable to your custom Boost installation - EXTERNAL_CTK_DIR Set this variable to your CTK binary tree (the directory containing the CTKConfig.cmake file) - EXTERNAL_CableSwig_DIR Set this variable to your CableSwig binary tree for Python wrapping (the directory containing the CableSwigConfig.cmake file) - EXTERNAL_DCMTK_DIR Set this variable to your DCMTK binary tree (the directory containing the DCMTKConfig.cmake file) - EXTERNAL_GDCM_DIR Set this variable to your GDCM binary tree (the directory containing the GDCMConfig.cmake file) - EXTERNAL_ITK_DIR Set this variable to your ITK binary tree (the directory containing the ITKConfig.cmake file) - EXTERNAL_VTK_DIR Set this variable to your VTK binary tree (the directory containing the VTKConfig.cmake file) To set CMake options before the first configure step is invoked, supply them on the command line, i.e. \code ccmake -DITK_DIR:PATH=/opt/ITK-release ../MITK \endcode */ diff --git a/Documentation/Doxygen/3-DeveloperManual/Starting/Starting.dox b/Documentation/Doxygen/3-DeveloperManual/Starting/Starting.dox index 4bc71644b8..1f584e1fa5 100644 --- a/Documentation/Doxygen/3-DeveloperManual/Starting/Starting.dox +++ b/Documentation/Doxygen/3-DeveloperManual/Starting/Starting.dox @@ -1,38 +1,38 @@ /** \page StartingDevelopment Starting your MITK Development This introduction will acquaint you with the most important workflows to get you started with your MITK development. First, \ref Architecture will explain the differences between the application and the toolkit. -\ref SettingUpMITK will get you started with a working environment for MITK development. \ref GettingToKnowMITK will walk you trough the folder structure, the module system, and plugin system. This chapter also contains an extensive tutorial on how to work with MITK. +\ref SettingUpMITK will get you started with a working environment for MITK development. \ref GettingToKnowMITK will walk you through the folder structure, the module system, and plugin system. This chapter also contains an extensive tutorial on how to work with MITK. The \ref FirstSteps section will then show you how to extend MITK for your own project.
  • \subpage Architecture
  • \subpage SettingUpMITK
    • \ref SupportedPlatformsPage
    • \ref BuildInstructionsPage
    • \ref thirdpartylibs
    • \ref HowToNewProject
  • \subpage GettingToKnowMITK
    • \ref DirectoryStructurePage
    • \ref TutorialPage
    • \ref CMAKE_FAQ
    • \ref StyleGuideAndNotesPage
    • \ref DocumentationGuide
    • \ref CodingPage
    • \ref KnownProblemsPage
  • \subpage FirstSteps
    • \ref NewViewPage
    • \ref NewModulePage
    • \ref CMAKE_FAQ
  • \subpage AboutTestingPage
*/ diff --git a/Examples/DocumentationExample.h b/Examples/DocumentationExample.h index 1253cc69f0..af38be5fb6 100644 --- a/Examples/DocumentationExample.h +++ b/Examples/DocumentationExample.h @@ -1,67 +1,67 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ /** * \brief This is a class for showing how to document your code using doxygen. * * The more detailed description is needed for some more elaborate description. Here you can describe * anything anyone might ever want to know about your class. Of especial interest might be to mention -* what it can be used for or what its main purpose is. If you want you can even use pictures (you migth want +* what it can be used for or what its main purpose is. If you want you can even use pictures (you might want * take a look at the doxygen documentation for that). */ class DocumentationExample { public: /** * \brief A constructor. * A more elaborate description of the constructor. */ DocumentationExample(); /** * \brief A destructor. * A more elaborate description of the destructor. */ ~DocumentationExample(); /** * \brief Casts the char at position number to an int and returns it * * This function will take an int number and a char pointer name, then will try to access the int a position number * and cast it to an int and return it. No verification is done within the function to ensure, that it is a valid * position. * * @param number The position of the char to be cast. This should never be below 0 and not above the length of the char * array. * @param name A constant character pointer. - * @return The char value of the charakter at position number casted to int + * @return The char value of the character at position number casted to int */ int ExampleCastCharToInt(int number, const char *name); /** * \brief Tests whether an integer is within the bounds of a string * * This will perform a check whether an int is bigger than an arbitrary zero and smaller than the length of the string. * @param itsAString The string to be checked as reference * @param theInt The position to be checked for * @exception std::out_of_range parameter is out of range. * @see zero() * @return True if character is in range, should never return false but throw an exception instead. */ bool TestWithin(std::string itsAString, int theInt) throw(std::out_of_range); /** * \brief The public definition of zero * * This variable is used to determine what will be regarded as zero. */ int zero; }; diff --git a/Examples/Dump/mitkdump.cpp b/Examples/Dump/mitkdump.cpp index 33ef0ad2cf..8726c8cc98 100644 --- a/Examples/Dump/mitkdump.cpp +++ b/Examples/Dump/mitkdump.cpp @@ -1,283 +1,283 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ /** \file mitkdump.cpp \brief Commandline application to see what DICOMFileReaderSelector would produce from a set of files. Usage: \verbatim mitkdump [-v] file1 [... fileN] -v output more details on commandline fileN DICOM file \endverbatim The application will ask a DICOMFileReaderSelector to analyze the files given as file1 .. fileN. Once the reader with the least number of files is selected, this result is printed to commandline. If the "-v" flag is used (as a first parameter), the output will contain details about filenames, which can make the output considerably harder to read. Output is also written to a log file of name "%gt;datetime-stamp%lt;_dir_>directory-name<.mitkdump */ #include "mitkDICOMFileReaderSelector.h" #include "mitkDICOMImageFrameInfo.h" #include "mitkDICOMReaderConfigurator.h" using mitk::DICOMTag; std::string buildDateString() { std::time_t rawtime; std::tm *timeinfo; char buffer[80]; std::time(&rawtime); timeinfo = std::localtime(&rawtime); std::strftime(buffer, 80, "%Y%m%d-%H%M%S", timeinfo); return std::string(buffer); } void gen_random(char *s, const int len) { static const char alphanum[] = "0123456789" "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; for (int i = 0; i < len; ++i) { s[i] = alphanum[rand() % (sizeof(alphanum) - 1)]; } s[len] = 0; } std::string gen_random(const int len) { if (len > 0 && len < 100) { char retval[100]; gen_random(retval, len); return std::string(retval); } else { return std::string(""); } } std::string removeUnsafeChars(const std::string &str) { std::string retval; for (std::string::const_iterator it = str.begin(); it != str.end(); ++it) { const char &c = *it; if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c == '.') || (c == '-') || (c == '_')) { retval += c; } } return retval; } std::string extractDirString(const std::string &dirString) { std::string wholeprefix = dirString.substr(0, dirString.find_last_of("/\\")); std::string lastDirectoryPart = wholeprefix.substr(wholeprefix.find_last_of("/\\") + 1); std::string cleanLastDirectoryPart = removeUnsafeChars(lastDirectoryPart); if (!cleanLastDirectoryPart.empty()) { return cleanLastDirectoryPart; } else { std::stringstream emptydirname; emptydirname << "noname_" << gen_random(6); return emptydirname.str(); } } int main(int argc, char *argv[]) { bool fileDetails(false); bool loadimage(false); int firstFileIndex = 1; // see if we got the '-v' flag to output file details if (argc > 1 && std::string(argv[firstFileIndex]) == "-v") { fileDetails = true; ++firstFileIndex; } // see if we got the '-l' flag if (argc > 1 && std::string(argv[firstFileIndex]) == "-l") { loadimage = true; ++firstFileIndex; } // analyze files from argv mitk::StringList inputFiles; for (int a = firstFileIndex; a < argc; ++a) { inputFiles.push_back(std::string(argv[a])); } if (inputFiles.empty()) { MITK_INFO << "0 input files given, exiting..."; return EXIT_SUCCESS; } mitk::DICOMFileReaderSelector::Pointer configSelector = mitk::DICOMFileReaderSelector::New(); - configSelector->LoadBuiltIn3DConfigs(); // a set of compiled in ressources with standard configurations that work well + configSelector->LoadBuiltIn3DConfigs(); // a set of compiled in resources with standard configurations that work well configSelector->SetInputFiles(inputFiles); mitk::DICOMFileReader::Pointer reader = configSelector->GetFirstReaderWithMinimumNumberOfOutputImages(); if (reader.IsNull()) { MITK_ERROR << "Could not configure any DICOM reader.. Exiting..."; return EXIT_FAILURE; } // output best reader result MITK_INFO << "---- Best reader configuration '" << reader->GetConfigurationLabel() << "' with " << reader->GetNumberOfOutputs() << " outputs"; if (fileDetails) { reader->PrintOutputs(std::cout, fileDetails); } // construct the name of a log file std::string datestring = buildDateString(); ; std::string dirString = extractDirString(argv[firstFileIndex]); std::string logfilename = datestring + "_dir_" + dirString + ".mitkdump"; MITK_INFO << "Logfile " << logfilename; // write output to file for later analysis std::ofstream fs; fs.open(logfilename.c_str()); fs << "---- " << dirString << ": Best reader configuration '" << reader->GetConfigurationLabel() << "' with " << reader->GetNumberOfOutputs() << " outputs" << std::endl; reader->PrintOutputs(fs, true); // always verbose in log file fs.close(); // serialize, deserialize, analyze again, verify result! mitk::DICOMReaderConfigurator::Pointer serializer = mitk::DICOMReaderConfigurator::New(); std::string readerSerialization = serializer->CreateConfigStringFromReader(reader.GetPointer()); bool outputError(false); for (unsigned int outputIndex = 0; outputIndex < reader->GetNumberOfOutputs(); ++outputIndex) { const mitk::DICOMImageBlockDescriptor &outputDescriptor = reader->GetOutput(outputIndex); mitk::StringList filenamesOfThisGroup; const mitk::DICOMImageFrameList &frames = outputDescriptor.GetImageFrameList(); for (auto fIter = frames.begin(); fIter != frames.end(); ++fIter) { filenamesOfThisGroup.push_back((*fIter)->Filename); } mitk::DICOMReaderConfigurator::Pointer readerConfigurator = mitk::DICOMReaderConfigurator::New(); mitk::DICOMFileReader::Pointer dicomReader = readerConfigurator->CreateFromUTF8ConfigString(readerSerialization); dicomReader->SetInputFiles(filenamesOfThisGroup); dicomReader->AnalyzeInputFiles(); if (dicomReader->GetNumberOfOutputs() != 1) { MITK_ERROR << "****** Re-analyzing files of output group " << outputIndex << " yields " << dicomReader->GetNumberOfOutputs() << " groups"; outputError = true; for (auto fIter = frames.begin(); fIter != frames.end(); ++fIter) { MITK_INFO << "filename group " << outputIndex << ": " << (*fIter)->Filename; } } else { MITK_INFO << "Re-analyzing files of output group " << outputIndex << " yields " << dicomReader->GetNumberOfOutputs() << " groups"; } } if (outputError) { std::stringstream es; es << "Original reader configuration: " << std::endl; reader->PrintConfiguration(es); es << std::endl; mitk::DICOMReaderConfigurator::Pointer readerConfigurator = mitk::DICOMReaderConfigurator::New(); mitk::DICOMFileReader::Pointer dicomReader = readerConfigurator->CreateFromUTF8ConfigString(readerSerialization); es << "New reader configuration: " << std::endl; dicomReader->PrintConfiguration(es); es << std::endl; es << "Original XML: \n" << readerSerialization << std::endl; std::string newSerialization = serializer->CreateConfigStringFromReader(dicomReader.GetPointer()); es << "New XML: \n" << newSerialization << std::endl; MITK_ERROR << es.str(); } if (loadimage) { MITK_INFO << "Loading..."; reader->LoadImages(); mitk::Image::Pointer image = reader->GetOutput(0).GetMitkImage(); MITK_INFO << "---- Output image:"; mitk::BaseGeometry::Pointer geo3D = image->GetGeometry(); if (geo3D.IsNotNull()) { mitk::SlicedGeometry3D::Pointer sg = dynamic_cast(geo3D.GetPointer()); if (sg.IsNotNull()) { unsigned int nos = sg->GetSlices(); mitk::PlaneGeometry::ConstPointer first = sg->GetPlaneGeometry(0); mitk::PlaneGeometry::ConstPointer last = sg->GetPlaneGeometry(nos - 1); mitk::Point3D firstOrigin = first->GetOrigin(); mitk::Point3D lastOrigin = last->GetOrigin(); MITK_INFO << "Geometry says: First slice at " << firstOrigin << ", last slice at " << lastOrigin; mitk::StringLookupTableProperty::Pointer sliceLocations = dynamic_cast(image->GetProperty("dicom.image.0020.1041").GetPointer()); if (sliceLocations.IsNotNull()) { std::string firstSliceLocation = sliceLocations->GetValue().GetTableValue(0); std::string lastSliceLocation = sliceLocations->GetValue().GetTableValue(nos - 1); MITK_INFO << "Image properties says: first slice location at " << firstSliceLocation << ", last slice location at " << lastSliceLocation; } mitk::StringLookupTableProperty::Pointer instanceNumbers = dynamic_cast(image->GetProperty("dicom.image.0020.0013").GetPointer()); if (instanceNumbers.IsNotNull()) { std::string firstInstanceNumber = instanceNumbers->GetValue().GetTableValue(0); std::string lastInstanceNumber = instanceNumbers->GetValue().GetTableValue(nos - 1); MITK_INFO << "Image properties says: first instance number at " << firstInstanceNumber << ", last instance number at " << lastInstanceNumber; } } } MITK_INFO << "---- End of output"; } // if we got so far, everything is fine return EXIT_SUCCESS; } diff --git a/Examples/FirstSteps/NewModule/autoload/IO/mitkExampleDataStructureWriterService.h b/Examples/FirstSteps/NewModule/autoload/IO/mitkExampleDataStructureWriterService.h index b73b3059e9..544b12e3e3 100644 --- a/Examples/FirstSteps/NewModule/autoload/IO/mitkExampleDataStructureWriterService.h +++ b/Examples/FirstSteps/NewModule/autoload/IO/mitkExampleDataStructureWriterService.h @@ -1,43 +1,43 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef mitkExampleDataStructureWriterService_h #define mitkExampleDataStructureWriterService_h #include #include namespace mitk { /** - * Writes example data strucutres to a file + * Writes example data structures to a file * @ingroup Process */ class ExampleDataStructureWriterService : public mitk::AbstractFileWriter { public: typedef mitk::ExampleDataStructure InputType; ExampleDataStructureWriterService(); ~ExampleDataStructureWriterService() override; using AbstractFileWriter::Write; void Write() override; protected: ExampleDataStructureWriterService(const ExampleDataStructureWriterService &other); mitk::ExampleDataStructureWriterService *Clone() const override; }; } // end of namespace mitk #endif diff --git a/Examples/FirstSteps/NewModule/test/mitkExampleDataStructureReaderWriterTest.cpp b/Examples/FirstSteps/NewModule/test/mitkExampleDataStructureReaderWriterTest.cpp index f765ccc53b..387adb5b39 100644 --- a/Examples/FirstSteps/NewModule/test/mitkExampleDataStructureReaderWriterTest.cpp +++ b/Examples/FirstSteps/NewModule/test/mitkExampleDataStructureReaderWriterTest.cpp @@ -1,71 +1,71 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ // Testing #include "mitkTestFixture.h" #include "mitkTestingMacros.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 + * @brief Setup Always call this method before each Test-case to ensure correct and new initialization 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::Save(m_Data, path); mitk::ExampleDataStructure::Pointer loadedData = mitk::IOUtil::Load(path); 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 index 135771e8b4..b82767d23c 100644 --- a/Examples/FirstSteps/NewModule/test/mitkExampleDataStructureTest.cpp +++ b/Examples/FirstSteps/NewModule/test/mitkExampleDataStructureTest.cpp @@ -1,66 +1,66 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ // Testing #include "mitkTestFixture.h" #include "mitkTestingMacros.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 + * @brief Setup Always call this method before each Test-case to ensure correct and new initialization 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/Examples/Plugins/org.mitk.example.gui.customviewer.views/src/internal/DicomView.h b/Examples/Plugins/org.mitk.example.gui.customviewer.views/src/internal/DicomView.h index 1a7a3d8326..733dce681e 100644 --- a/Examples/Plugins/org.mitk.example.gui.customviewer.views/src/internal/DicomView.h +++ b/Examples/Plugins/org.mitk.example.gui.customviewer.views/src/internal/DicomView.h @@ -1,74 +1,74 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef DICOMVIEW_H_ #define DICOMVIEW_H_ #include #include #include "ui_QmitkDicomViewControls.h" /** * \brief A view class suited for the DicomPerspective within the custom viewer plug-in. * * This view class contributes dicom import functionality to the DicomPerspective. * The view controls are provided within CreatePartControl() by the QmitkDicomExternalDataWidget * class. A DicomView instance is part of the DicomPerspective for Dicom import functionality. */ // //! [DicomViewDecl] class DicomView : public QmitkAbstractView // //! [DicomViewDecl] { Q_OBJECT public: /** * String based view identifier. */ static const std::string VIEW_ID; /** * Standard constructor. */ DicomView(); /** * Standard destructor. */ ~DicomView() override; /** * Creates the view control widgets provided by the QmitkDicomExternalDataWidget class. * Widgets associated with unused functionality are being removed and DICOM import and data - * storage transfer funcionality being connected to the appropriate slots. + * storage transfer functionality being connected to the appropriate slots. */ void CreateQtPartControl(QWidget *parent) override; protected Q_SLOTS: /** * Loads the DICOM series specified by the given string parameter and adds the resulting data * node to the data storage. Subsequently switches to the ViewerPerspective for further * data examination. */ void AddDataNodeFromDICOM(QHash eventProperties); protected: void SetFocus() override; Ui::QmitkDicomViewControls m_Controls; QWidget *m_Parent; }; #endif /*DICOMVIEW_H_*/ diff --git a/Examples/Plugins/org.mitk.example.gui.customviewer/documentation/doxygen/CustomViewerExample.dox b/Examples/Plugins/org.mitk.example.gui.customviewer/documentation/doxygen/CustomViewerExample.dox index fea77b2bb2..60dc547219 100644 --- a/Examples/Plugins/org.mitk.example.gui.customviewer/documentation/doxygen/CustomViewerExample.dox +++ b/Examples/Plugins/org.mitk.example.gui.customviewer/documentation/doxygen/CustomViewerExample.dox @@ -1,312 +1,312 @@ /** \page BlueBerryExampleCustomViewer A highly customized viewer This documentation is structured as follows: -# \subpage Introduction -# \subpage ViewerPluginCreation -# \subpage MainWindowLayout -# \subpage AddFunctionality -# \subpage GUICustomization \page Introduction Introduction The Custom Viewer Example is a BlueBerry example plugin, developed to demonstrate customization capabilities provided by the BlueBerry application framework. The example plugin implements a GUI customized viewer application. The developed viewer incorporates simple viewer functionality embedded in a customized graphical user interface. Spoken in BlueBerry terms, the following features are provided:
  • Hidden Menu-, Tool- and Statusbars
  • Hidden Editor Area
  • Fixed perspectives
  • Customized main window contents
  • Customized perspectives bar based on QTabBar
  • GUI Customization using Qt-Stylesheets
The custom viewer itself consists of two perspectives, i.e. a viewer perspective and a DICOM perspective. As part of the viewer perspective, an instance of QmitkDataManagerView allows for data selection. Visualization of the selected data is then provided by a simple render window view. According data can either be directly loaded from file or be imported as DICOM data. DICOM import functionality is accessible from the DICOM perspective incorporating the QmitkDicomExternalDataWidget. The GUI-appearance is customized using Qt-Stylesheets in order to give the application a non-native look and feel. This is further emphasized by a Tab-Widget-like presentation of the perspectives using a modified perspective bar based on a QTabBar. In addition to an absence of menu-, tool- and status-bars, simplicity is accentuated by a proper view and perspective design, i.e. the editor area being invisible and views being fixated. The following images depict the viewer- and DICOM-perspectives of the custom viewer. \imageMacro{ViewerPerspective.png,"Viewer perspective of the Custom Viewer.",16.00} \imageMacro{DicomPerspective.png,"Dicom perspective of the Custom Viewer.",16.00} Go to the previous page \ref BlueBerryExampleCustomViewer. Or proceed to the next page \ref ViewerPluginCreation. \page ViewerPluginCreation Creating the CustomViewer plugin As we want to develop our Custom Viewer as part of BlueBerry to demonstrate the customization capabilities of the application framework, we have to integrate the components of our example application as BlueBerry plugins in order to make the application framework's functionalities available to our application. For example plugin startup with BlueBerry, a convenience application called BlueBerryExampleLauncher is already provided in the Examples/BlueBerryExampleLauncher directory of MITK. This application acts as a BlueBerry loading mechanism for any example application provided. To make an example application startable by the BlueBerry example launcher, we have to provide a folder inside Examples/Plugins containing all files making up the application plugin itself. Additionally, we have to add entries to the PluginList.cmake in the Examples/Plugins directory for plugin registration and create a new file in the Examples/BlueBerryExampleLauncher/Configurations folder containing the plugin itself as well as any further required plugin. The resulting plugin folder for our custom viewer application consists of several subfolders and files: \imageMacro{CustomViewerDirectory.png,"The directory structure for the CustomViewer plugin.",16.00} We can see a documentation and resources folder, a source folder containing all source files, some cmake-related files and a plugin.xml file for the BlueBerry related plugin extension and extension-point declarations. Next, we add some source code to our plugin folder. First, we need a class for our CustomViewer application itself which we derive from the berry:IApplication application class: \dontinclude CustomViewer.h \skip class CustomViewer : public QObject \until void Stop(); In short, this class acts as an entry point for the BlueBerry application runtime. It defines what the application does when it is started (Start()-Method) and before it is ended (Stop()-Method). Our Start()-Method creates a BlueBerry display for GUI-rendering and a WorkbenchAdvisor for workbench control. Then the BlueBerry workbench is created and run given the created display and WorkbenchAdvisor: \snippet MinimalApplicationSnippet.cpp MinimalApplicationClass_StartMethod The Stop()-method does not need to be further defined. In addition, a default perspective identifier is given to define an initial perspective to be shown by the WorkbenchWindow. Later, we well need a proper WorkbenchAdvisor class derived from berry::WorkbenchAdvisor. In it, we will create a WorkbenchWindowAdvisor, which on his part is used to control the WorkbenchWindow's GUI creation. For now, we simply use the berry::WorkbenchWindowAdvisor, which creates the GUI in a default way. As the development of our custom viewer advances, we will want to take part in the GUI creation ourselves, so we will need to customize our own WorkbenchWindowAdvisor accordingly. Now we create a class named ViewerPerspective which is derived from berry::IPerspectiveFactory: \snippet ViewerPerspective.h ViewerPerspectiveClassDeclaration It acts as an initial perspective to be displayed as part of our bulk application plugin. For now, the perspective will remain empty and can, alongside the definition of further perspectives, be provided with views later by overwriting the CreateInitialLayout()-Method. Finally, we need a ctkPluginActivator derived class which is used to customize the starting and stopping of our plugin: \snippet org_mitk_example_gui_customviewer_Activator.h PluginActivatorHeader During plugin-start, we register our plugin classes given the ctkPluginContext by overwriting the start()-Method. \dontinclude org_mitk_example_gui_customviewer_Activator.cpp \skip ::start \until ViewerPerspective \skip PluginContext \until } In order to connect our application and its perspectives to the BlueBerry components we have to declare the according extension-point-contributions inside the plugin.xml file. As can be seen in the image below, our application and its perspectives contribute to the org.blueberry.osgi.applications and org.blueberry.ui.perspectives extension points respectively. \imageMacro{plugin_xml_0.png,"Extension point contributions of our initial custom viewer application",16.00} When we start the BlueBerryExampleLauncher (either directly or via the provided batch files), we can now choose our bulk application (among others present), and a window is presented showing our empty initial perspective. \imageMacro{MinimalApplicationWindow.png,"Our first application window showing one single perspective",16.00} Go to the previous page \ref Introduction. Or proceed to the next page \ref MainWindowLayout. \page MainWindowLayout Main Window Layout: ViewerPerspective and DicomPerspective Now that we have created a bulk plugin for our custom viewer, we intend to customize the way how the main window of our Blueberry application is laid out. We want:
  • No visible menu-, tool- and status-bars
  • Two perspectives: a viewer perspective and a DICOM perspective
  • A tab-bar like perspective bar that allows for perspective switching
  • An open file button for perspective-independent file opening
Customizing the main window contents requires creating a custom WorkbenchWindowAdvisor derived from berry::WorkbenchWindowAdvisor hence this class controls the WorkbenchWindow layout: \snippet CustomViewerWorkbenchWindowAdvisor.h CustomViewerWorkbenchWindowAdvisorClassDeclaration As we mentioned in \ref ViewerPluginCreation, it is the WorkbenchAdvisor class that creates the WorkbenchWindowAdvisor. Hence, we now create our own version of a WorkbenchAdvisor: \snippet CustomViewerWorkbenchAdvisor.h WorkbenchAdvisorDecl Here, we overwrite the CreateWorkbenchWindowAdvisor()-method: \snippet CustomViewerWorkbenchAdvisor.cpp WorkbenchAdvisorCreateWindowAdvisor First, to prevent the WorkbenchWindow from rendering any menu-, tool- and status-bars, we overwrite the PreWindowOpen()-Method of the WorkbenchWindowAdvisor and access the WorkbenchWindowConfigurer helper class instance. Additionally, we set an appropriate title for our application window: \snippet CustomViewerWorkbenchWindowAdvisor.cpp CustomViewerWorkbenchWindowAdvisorPreWindowOpen Then we forge bulk versions of our viewer and dicom perspectives. We already created a bulk version of the viewer perspective earlier (see \ref ViewerPluginCreation). Accordingly, we create our DicomPerspective by defining the perspective class, contributing to the perspectives-extension point, registering the perspective in the plugin activator and adding to the cmake files. For the tab-bar like perspective bar we define a QtPerspectiveSwitcherTabBar derived from QTabBar: \snippet QtPerspectiveSwitcherTabBar.h PerspectiveSwitcherDeclaration The perspective switching functionality is implemented by a SwitchPerspective function, a signal-slot-connection that reacts on tab changes and a perspective listener that on perspective activation consistently switches to the according tab. Within the SwitchPerspective function, we show the perspective according to the current index indicating the currently active tab: \snippet QtPerspectiveSwitcherTabBar.cpp PerspectiveSwitcherSwitchPerspective Here, we have to ignore the first tab change event that can be fired during tab bar configuration. At that time, the perspective layout generally is not yet finished, which subsequently leads to an error. The SwitchPerspective slot is being connected to the tab-change-event during construction. The perspective listener is implemented as a helper friend struct derived from berry::IPerspectiveListener: \snippet QtPerspectiveSwitcherTabBar.cpp SwitchPerspectiveListener In the PerspectiveActivated-Method, we activate the tab according to the activated perspective's ID: \snippet QtPerspectiveSwitcherTabBar.cpp SwitchPerspectiveListenerPerspectiveActivated Now, our tab-bar like perspective bar is ready for use in the customized window layout. The open file functionality will later be implemented as an OpenFile-slot to the WorkbenchWindowAdvisor. Refer to \ref AddFunctionality for details. As such, it can be connected to a perspective-independent push button that will be part of to the application's window contents, together with an instance of the QtPerspectiveSwitcherTabBar. The customization of the window contents takes place within the CreateWindowContents method. That means, we can overwrite the superclass' CreateWindowContents method and lay out every widget of the main window individually. Given the method's berry::Shell parameter, we can extract the application's main window as QMainWindow using the berry::Shell::GetControl()-method: \snippet CustomViewerWorkbenchWindowAdvisor.cpp WorkbenchWindowAdvisorCreateWindowContentsHead Usually, as in the superclass' CreateWindowContents method, the shell ist given to the WindowConfigurer where the Page Composite, i.e. the part holding the perspective's view contents, is added as a single QControlWidget to an HBoxLayout. For our purposes, we want to place the QtPerspectiveSwitcherTabBar and the View-Button alongside the Page Composite. We can achieve that by creating the Page Composite within the CreateWindowContents method, lay it out in the MainWindow together with the other widgets, and give it to the WorkbenchWindowConfigurer for the view control layout process, which will then take place wrapped within our own PageComposite widget: \dontinclude CustomViewerWorkbenchWindowAdvisor.cpp \skip mainWindow->setCentralWidget(CentralWidget); \until PerspectivesLayer->addWidget(OpenFileButton); \skip for correct initial layout \until this->GetWindowConfigurer -The OpenFile-Button and the QtPerspectiveSwitcherTabBar will be laid out together in a HBoxLayout called PerspectivesLayer. The PerspectivesLayer will be vertically arranged with the PageComposite widget in a VBoxLayout called CentralWidgetLayout. This CentralWidgetLayout will be assigned a QWidget being set the CentralWidget of our MainWindow. Caveat: we need to call the activate- and update-Methods of our CentralWidgetLayout; otherweise the widgets will not be laid out properly. See Bug-1654 for further details. See our bulk custom viewer application depicted below. +The OpenFile-Button and the QtPerspectiveSwitcherTabBar will be laid out together in a HBoxLayout called PerspectivesLayer. The PerspectivesLayer will be vertically arranged with the PageComposite widget in a VBoxLayout called CentralWidgetLayout. This CentralWidgetLayout will be assigned a QWidget being set the CentralWidget of our MainWindow. Caveat: we need to call the activate- and update-Methods of our CentralWidgetLayout; otherwise the widgets will not be laid out properly. See Bug-1654 for further details. See our bulk custom viewer application depicted below. \imageMacro{BulkApplicationWindow.png,"Our bulk application showing two empty perspectives managed with a tab-bar based perspectives bar",16.00} Go to the previous page \ref ViewerPluginCreation. Or proceed to the next page \ref AddFunctionality. \page AddFunctionality Adding functionality: Data Manager, Render Window, File Opening and DICOM Import Up to now, we have developed a bulk custom viewer application, i.e. a runnable BlueBerry application showing an open file button and two perspectives switched by a custom tab-bar like perspectives bar. Now, we will add the desired functionality for our custom viewer. We want to integrate:
  • A Data Manager, managing Data Nodes related to loaded or DICOM-imported images
  • A Render Window to visualize the Data Nodes
  • File Opening functionality connected to the Open-File-Button
  • DICOM Import functionality
Except for the File Opening functionality, which is already GUI-represented, we need to integrate proper views to our perspectives in order to make the according functionality accessible. Concerning the design of our example application, two options appear straight-forward:
  1. Integrate the view-class source-code to the main-application-plugin (the custom viewer plugin)
  2. Create a proper plugin for the views
Taking into account the plugin dependencies, it can be revealed that the first solution is not an option. Without going into detail, that solution would result in a cyclic dependency scenario, so an adequate plugin activation order would not be achievable at runtime. So we will create a proper plugin for the views we intend to use. This is straightforward as shown in \ref ViewerPluginCreation. For Data Manager functionality we will make use of the QmitkDataManagerView which - being a berry::IVewPart - can externally be integrated to our viewer perspective. The only thing we have to do is add the QMitkDataManagerView to the viewer perspective's CreateInitialLayout()-method: \dontinclude ViewerPerspective.cpp \skip ::CreateInitialLayout \until org.mitk.views.datamanager For the rendering functionality we have to create a proper view class. We derive that view class called SimpleRenderWindowView from QmitkAbstractView (for direct DataStorage access) and from mitk::IRenderWindowPart: \snippet SimpleRenderWindowView.h SimpleRenderWindowViewDeclaration Concrete implementations can for example be adapted from QmitkAbstractRenderEditor. The AbstractRenderWindowViewPrivate helper class is modified with regard to the views-Plugin-Activator: \snippet SimpleRenderWindowView.cpp SimpleRenderWindowViewHelper In CreateQtPartControl() we can now lay out the view controls. For that, we create a QmitkRenderWindow whose Renderer is subsequently be given the DataStorage: \snippet SimpleRenderWindowView.cpp SimpleRenderWindowViewCreatePartControl Finally we add the SimpleRenderWindowView in ViewerPerspective::CreateInitialLayout(): \snippet ViewerPerspective.cpp AddView1 For the DICOM import functionality we derive the DicomView class from QmitkAbstractView: \snippet DicomView.h DicomViewDecl In CreateQtPartControl(), we add a QmitkDicomExternalDataWidget to our view controls (this time e.g. via ui-File): \snippet DicomView.cpp DicomViewCreatePartControl The QmitkDicomExternalDataWidget yields a tree view for DICOM data, as well as a signal for Dicom transfer to the data manager and a slot for DICOM import to the tree view. With the Dicom transfer signal a string containing information about the DICOM series currently selected in the tree view is piggybacked. We use this information in an AddDataNodeFromDICOM slot defined in the DicomView class following the example of the DicomEventHandler class: \snippet DicomView.cpp DicomViewCreateAddDataNodeInformation The file path and seriesUID information are used to load the selected DICOM series into a mitk::DataNode: \snippet DicomView.cpp DicomViewCreateAddDataNodeLoadSeries which can then be added to the DataStorage: \snippet DicomView.cpp DicomViewCreateAddDataNode After that, we activate the viewer perspective to examine the data in the rendering window view. \snippet DicomView.cpp DicomViewCreateAddDataNodeActivatePersp Having a look back to the QmitkDicomExternalDataWidget, while there is already a view button present that triggers Dicom transfer signal emission, we still have to bind DICOM import functionality to a proper import button. We will do this once again in the CreateQtPartControl method (refer to the above snippet). After setting up the view controls containing the QmitkDicomExternalDataWidget and an import button, we render the unused widgets invisible. After connecting the Dicom transfer signal to the AddDataNodeFromDICOM slot and our import button to the DICOM import slot of the QmitkDicomExternalDataWidget, the DicomView is ready for use. Finally, the DicomView is added inside the DicomPerspective::CreateInitialLayout() method: \snippet DicomPerspective.cpp DicomPerspCreateLayout The following images show the Dicom import functionality. \imageMacro{dicomImportFiles.png,"The DICOM file import dialog",16.00} \imageMacro{dataTree.png,"Imported DICOM data shown in the tree view",16.00} \imageMacro{DICOMimported.png,"Imported DICOM data presented in the render window view",16.00} Now we implement the file open slot already defined earlier (see \ref MainWindowLayout). While it appears that we could simply assign a QmitkFileOpenAction to a QToolButton, this is not possible due to the fact, that by default, the WorkbenchUtil::LoadFiles() method invoked by the QmitkFileOpenAction awaits an editor to be present in the current application. To prevent the method from throwing an exception, we made a workaround by giving the LoadFiles() method an additional parameter that determines whether an editor is to be opened or not: \snippet mitkWorkbenchUtil.cpp UtilLoadFiles Hence, we have to invoke that method manually, e.g. inside an OpenFile-slot implemented inside the WorkbenchWindowAdvisor: \snippet CustomViewerWorkbenchWindowAdvisor.cpp WorkbenchWindowAdvisorOpenFile In it, a dialog is opened that asks for the user for a number of files to open. If any files are given, these are being loaded by the WorkbenchUtil::LoadFiles method. Finally, the viewer perspective is activated: \snippet CustomViewerWorkbenchWindowAdvisor.cpp WorkbenchWindowAdvisorOpenFilePerspActive -Before we can examine the loaded data, we have to manually invoke a reinit on it. The render window concept in mitk is actually undergoing some work, where this inconvenience will also be adressed. The images below show the resulting file opening functionality. +Before we can examine the loaded data, we have to manually invoke a reinit on it. The render window concept in mitk is actually undergoing some work, where this inconvenience will also be addressed. The images below show the resulting file opening functionality. \imageMacro{OpenFileDialog.png,"The open file dialog",16.00} \imageMacro{FileOpened.png,"Opened file shown in the render window view",16.00} Go to the previous page \ref MainWindowLayout. Or proceed to the next page \ref GUICustomization. \page GUICustomization Customizing the Main Window using Qt-Stylesheets In a final step, we want to further customize the appearance of our mainWindow to give it an distinct non-native look and feel. We want to achieve this by pursuing the following aims:
  • Change the background and widget colors
  • Change the tab-widget and ToolButton style, also with respect to mouse-over-button (hovering) effects
  • Completing the non-native tab-widget like impression of the perspectives by gluing tab-bar and perspective's PageComposite together
  • DICOM Import functionality
-For GUI customization, we will modify the Qt-Stylesheets files already used by blueberry applications. Within the Qt-Stylesheet-Files, all widgets can globally and locally be adressed inside the main window for style changes. We have to adress the berry::IQtStyleManager to tell the BlueBerry workbench to use a specific Qt-Stylesheet. This is done inside the WorkbenchAdvisor in the CustomViewerWorkbenchAdvisor::Initialize() method: +For GUI customization, we will modify the Qt-Stylesheets files already used by blueberry applications. Within the Qt-Stylesheet-Files, all widgets can globally and locally be addressed inside the main window for style changes. We have to address the berry::IQtStyleManager to tell the BlueBerry workbench to use a specific Qt-Stylesheet. This is done inside the WorkbenchAdvisor in the CustomViewerWorkbenchAdvisor::Initialize() method: \snippet CustomViewerWorkbenchAdvisor.cpp WorkbenchAdvisorInit The style manager is taken from the application's plugin context via service reference. Invoking the berry::IQtStyleManager::AddStyle() and berry::IQtStyleManager::SetStyle() methods, the workbench will now use the announced qss-File to style our Workbench Window. In a production system, the stylesheets are usually compiled into the plug-in or application using the Qt resource system. -However, during developement of the stylesheets it is often more convenient to reference them using a hard-coded path to the local file system (see live update functionality below). +However, during development of the stylesheets it is often more convenient to reference them using a hard-coded path to the local file system (see live update functionality below). Before we start customization we will first provide some customization convenience. We add an UpdateStyle()-slot to our CustomViewerWorkbenchWindowAdvisor where we explicitly reset the css-File to the style manager: \snippet CustomViewerWorkbenchWindowAdvisor.cpp WorkbenchWindowAdvisorUpdateStyle By integrating an update style button to the Application's main window and connecting this button with the previously defined slot, we can now button-push-update the style on runtime. This will of course only work for stylesheets which are referenced from the local file system. \imageMacro{StyledMainWindow0.png,"The unstyled Main Window",16.00} First we might want to change the background color style by setting the background color of the QWidget#CentralWidget to a linear gradient from light to dark blue: \snippet customstyleSnippet.qss CentralWidgetColor Then, we give the page composite control widget a slight grey border (except for the upper border where no border should be visible) and the same background color as the activated tab widget: \snippet customstyleSnippet.qss PageComposite The image below depicts the style changes. \imageMacro{StyledMainWindow2.png,"Background-color changed Central and Composite Control Widgets",16.00} Concerning the tab-widget style, four states have to be customized: QtPerspectiveSwitcherTabBar::tab (the tab in general), QtPerspectiveSwitcherTabBar::tab:selected (when tab is selected), QtPerspectiveSwitcherTabBar::tab:selected:hover (when tab is selected and the mouse is hovering above), QtPerspectiveSwitcherTabBar::tab:!selected:hover (respectively). All tabs are given round corners using border-top-left- and border-top-right-radius definitions. Additionally, all tabs is provided a gap to its neighbor defining a positive margin right. Selected tabs appear bigger by defining a negative upper margin, and they have no lower frame in the unselected state so a tab-widget appearance is provided. Finally, they have a brighter background color also used by the QWidget#ClientComposite. Hovering tabs are colored yellow and have a visible lower border: \snippet customstyleSnippet.qss Tabs Finally, we customize the Push- and Tool-Buttons in a similar way: \snippet customstyleSnippet.qss Buttons The resulting style-customized main window is shown below (the style update button removed). \imageMacro{StyledMainWindow_final1.png,"The final version of our Custom Viewer (viewer perspective).",16.00} \imageMacro{StyledMainWindow_final2.png,"The final version of our Custom Viewer (DICOM perspective).",16.00} Proceed to the previous page \ref AddFunctionality. */ The custom viewer plugin implements simple viewer functionality presented in a customized look and feel. It was developed to demonstrate extensibility and customizability of the blueberry application framework. As an example for the GUI customization capabilities provided by the BlueBerry application framework, the custom viewer plugin was developed. It features simple viewer functionality presented in a customized look and feel. The custom viewer consists of two perspectives, i.e. a viewer perspective and a DICOM perspective. As part of the viewer perspective, an instance of QmitkDataManagerView allows for data selection. Visualization of the selected data is then performed by a simple render window view. According data can either be directly loaded from file or be imported as DICOM data. DICOM import functionality is accessible from the DICOM perspective incorporating the QmitkDicomExternalDataWidget. The customization of Qt Stylesheets is used to give the application a non-native look and feel. This is further emphasized by a Tab-Widget-like unification of the perspectives with the according perspective bar. In addition to an absence of menu-, tool- and status-bars, simplicity is accentuated by a proper view and perspective design, i.e. the editor area being invisible and views being fixated. diff --git a/Examples/Plugins/org.mitk.example.gui.customviewer/src/internal/QtPerspectiveSwitcherTabBar.cpp b/Examples/Plugins/org.mitk.example.gui.customviewer/src/internal/QtPerspectiveSwitcherTabBar.cpp index a581c7a9f2..11fe749e66 100644 --- a/Examples/Plugins/org.mitk.example.gui.customviewer/src/internal/QtPerspectiveSwitcherTabBar.cpp +++ b/Examples/Plugins/org.mitk.example.gui.customviewer/src/internal/QtPerspectiveSwitcherTabBar.cpp @@ -1,93 +1,93 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QtPerspectiveSwitcherTabBar.h" #include #include #include /** - * \brief A Listener class for perspective changes. Neccessary for consistent tab activation + * \brief A Listener class for perspective changes. Necessary for consistent tab activation * in QtPerspectiveSwitcherTabBar instances. */ // //! [SwitchPerspectiveListener] struct QtPerspectiveSwitcherTabBarListener : public berry::IPerspectiveListener // //! [SwitchPerspectiveListener] { /** * Constructor. */ QtPerspectiveSwitcherTabBarListener(QtPerspectiveSwitcherTabBar *switcher) : switcher(switcher) {} /** * Only listens to perspective activation events. */ Events::Types GetPerspectiveEventTypes() const override { return Events::ACTIVATED; } /** * Sets the corresponding perspective index within the associated QtPerspectiveSwitcherTabBar instance. */ // //! [SwitchPerspectiveListenerPerspectiveActivated] void PerspectiveActivated(const berry::IWorkbenchPage::Pointer & /*page*/, const berry::IPerspectiveDescriptor::Pointer &perspective) override { int index = perspective->GetId() == "org.mitk.example.viewerperspective" ? 0 : 1; switcher->setCurrentIndex(index); } // //! [SwitchPerspectiveListenerPerspectiveActivated] private: /** * The associated QtPerspectiveSwitcherTabBar instance. */ QtPerspectiveSwitcherTabBar *switcher; }; QtPerspectiveSwitcherTabBar::QtPerspectiveSwitcherTabBar(berry::IWorkbenchWindow::Pointer window) : window(window), perspListener(new QtPerspectiveSwitcherTabBarListener(this)) { this->tabChanged = false; QWidget *parent = static_cast(window->GetShell()->GetControl()); this->setParent(parent); QObject::connect(this, SIGNAL(currentChanged(int)), this, SLOT(SwitchPerspective(void))); window->AddPerspectiveListener(this->perspListener.data()); } QtPerspectiveSwitcherTabBar::~QtPerspectiveSwitcherTabBar() { window->RemovePerspectiveListener(this->perspListener.data()); } // //! [PerspectiveSwitcherSwitchPerspective] void QtPerspectiveSwitcherTabBar::SwitchPerspective() { if (!this->tabChanged) { this->tabChanged = true; return; } int index = this->currentIndex(); if (index == 0) { QString perspectiveId = "org.mitk.example.viewerperspective"; this->window->GetWorkbench()->ShowPerspective(perspectiveId, berry::IWorkbenchWindow::Pointer(window)); } else if (index == 1) { QString perspectiveId = "org.mitk.example.dicomperspective"; this->window->GetWorkbench()->ShowPerspective(perspectiveId, berry::IWorkbenchWindow::Pointer(window)); } } // //! [PerspectiveSwitcherSwitchPerspective] diff --git a/Examples/Plugins/org.mitk.example.gui.customviewer/src/internal/QtPerspectiveSwitcherTabBar.h b/Examples/Plugins/org.mitk.example.gui.customviewer/src/internal/QtPerspectiveSwitcherTabBar.h index 1aaff429ea..4f2544f9c3 100644 --- a/Examples/Plugins/org.mitk.example.gui.customviewer/src/internal/QtPerspectiveSwitcherTabBar.h +++ b/Examples/Plugins/org.mitk.example.gui.customviewer/src/internal/QtPerspectiveSwitcherTabBar.h @@ -1,70 +1,70 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef QTPERSPECTIVESWITCHERTABBAR_H_ #define QTPERSPECTIVESWITCHERTABBAR_H_ #include #include #include #include /** * \brief A QTabBar providing perspective bar functionality in BlueBerry applications. * * This subclass of QTabBar acts as a perspective bar in BlueBerry applications. Providing * perspective switching functionality in a tab-bar like outfit, this class serves as an * alternative to the ToolBar based berry::QtPerspectiveSwitcher class. */ // //! [PerspectiveSwitcherDeclaration] class QtPerspectiveSwitcherTabBar : public QTabBar // //! [PerspectiveSwitcherDeclaration] { Q_OBJECT public: /** * Constructor. */ QtPerspectiveSwitcherTabBar(berry::IWorkbenchWindow::Pointer window); /** * Standard destructor. */ ~QtPerspectiveSwitcherTabBar() override; private Q_SLOTS: /** * Implements perspective switching. */ void SwitchPerspective(); private: berry::IWorkbenchWindow::Pointer window; QScopedPointer perspListener; QHash perspIdToActionMap; /** - * Neccessary to prevent initial tab switching. + * Necessary to prevent initial tab switching. */ bool tabChanged; /** - * Listener for perspective changes. Neccessary for consistent tab activation. + * Listener for perspective changes. Necessary for consistent tab activation. */ friend struct QtPerspectiveSwitcherTabBarListener; }; #endif /* QTPERSPECTIVESWITCHERTABBAR_H_ */ diff --git a/Examples/Plugins/org.mitk.example.gui.extensionpointdefinition/documentation/doxygen/ExampleExtensionPoint.dox b/Examples/Plugins/org.mitk.example.gui.extensionpointdefinition/documentation/doxygen/ExampleExtensionPoint.dox index f701c5ba4a..f767e72096 100644 --- a/Examples/Plugins/org.mitk.example.gui.extensionpointdefinition/documentation/doxygen/ExampleExtensionPoint.dox +++ b/Examples/Plugins/org.mitk.example.gui.extensionpointdefinition/documentation/doxygen/ExampleExtensionPoint.dox @@ -1,55 +1,55 @@ /** \page ExtensionPointDefinition Extension Point Definition This example plugin defines an extension point and collects extensions. \image html ExtensionPointWithoutExtension.png In this example the extension point concept is used to define an extension point and extend the plugin functionality with it. The GUI consists of a view with two group boxes. In the right box the user can insert text in an input text field. The left box is the area where extensions for this plugin are displayed. In the plugin.xml file of this application the extension point is defined as follows: \image html DefinitionXML.png The according schema (\github{Examples/Plugins/org.mitk.example.gui.extensionpointdefinition/schema/changetext.exsd,changetext.exsd}) defines attributes for extensions of this extension point like a description. In the view of this plugin the registry is used to find all available extensions. For each found descriptor a push button is created that holds the functionality extension. \snippet MinimalView.cpp Collect extensions through registry These created push buttons are connected to the ChangeText method that alters the input text (in the way the extension defines) and outputs the changed text in another text field. \snippet MinimalView.cpp Use extended functionality to alter input text - The plugin can now be extened by extensions provided by other plugins. + The plugin can now be extended by extensions provided by other plugins. View complete source files: \li \github{Examples/Plugins/org.mitk.example.gui.extensionpointdefinition/src/internal/ChangeTextDescriptor.cpp,ChangeTextDescriptor.cpp} \li \github{Examples/Plugins/org.mitk.example.gui.extensionpointdefinition/src/internal/ChangeTextDescriptor.h,ChangeTextDescriptor.h} \li \github{Examples/Plugins/org.mitk.example.gui.extensionpointdefinition/src/internal/ChangeTextRegistry.cpp,ChangeTextRegistry.cpp} \li \github{Examples/Plugins/org.mitk.example.gui.extensionpointdefinition/src/internal/ChangeTextRegistry.h,ChangeTextRegistry.h} \li \github{Examples/Plugins/org.mitk.example.gui.extensionpointdefinition/src/internal/ExtensionPointDefinition.cpp,ExtensionPointDefinition.cpp} \li \github{Examples/Plugins/org.mitk.example.gui.extensionpointdefinition/src/internal/ExtensionPointDefinition.h,ExtensionPointDefinition.h} \li \github{Examples/Plugins/org.mitk.example.gui.extensionpointdefinition/src/internal/ExtensionPointDefinitionConstants.cpp,ExtensionPointDefinitionConstants.cpp} \li \github{Examples/Plugins/org.mitk.example.gui.extensionpointdefinition/src/internal/ExtensionPointDefinitionConstants.h,ExtensionPointDefinitionConstants.h} [\ref BlueBerryExampleExtensionPoint] [Next: \ref ExtensionContribution] [\ref BlueBerryExamples] \page ExtensionContribution Extension Contribution This example plugin holds no GUI. It just extends the Extension Point Definition plugin. \image html ExtensionPointWithExtension.png Through the extension of the Extension Point Definition plugin two functionalities are added by implementing the minimal IChangeText interface of the plugin. In the plugin.xml file of the plugin the extensions are defined as follows: \image html ContributionXML.png The extension point ID in the plugin.xml matches the ID of the extension point and is therefore found by the registry as a valid extension. A description is given for each extension although the schema for the extension point allows an extension without a description (\c \). The GUI now holds two buttons that change the input text of the user to upper or lower case. This is done by two simple methods. View complete source files: \li \github{Examples/Plugins/org.mitk.example.gui.extensionpointcontribution/src/internal/ChangeTextToLowerCase.cpp,ChangeTextToLowerCase.cpp} \li \github{Examples/Plugins/org.mitk.example.gui.extensionpointcontribution/src/internal/ChangeTextToLowerCase.h,ChangeTextToLowerCase.h} \li \github{Examples/Plugins/org.mitk.example.gui.extensionpointcontribution/src/internal/ChangeTextToUpperCase.cpp,ChangeTextToUpperCase.cpp} \li \github{Examples/Plugins/org.mitk.example.gui.extensionpointcontribution/src/internal/ChangeTextToUpperCase.h,ChangeTextToUpperCase.h} [\ref BlueBerryExampleExtensionPoint] [Previous: \ref ExtensionPointDefinition] [\ref BlueBerryExamples] */ diff --git a/Examples/Plugins/org.mitk.example.gui.extensionpointdefinition/documentation/doxygen/modules.dox b/Examples/Plugins/org.mitk.example.gui.extensionpointdefinition/documentation/doxygen/modules.dox index 5162e5ff62..348be1325f 100644 --- a/Examples/Plugins/org.mitk.example.gui.extensionpointdefinition/documentation/doxygen/modules.dox +++ b/Examples/Plugins/org.mitk.example.gui.extensionpointdefinition/documentation/doxygen/modules.dox @@ -1,15 +1,15 @@ /** \defgroup org_mitk_example_gui_extensionpointdefinition org.mitk.example.gui.extensionpointdefinition \ingroup MITKExamplePlugins - \brief A minimal applictaion that definines an extension point and collects extensions. + \brief A minimal applictaion that defines an extension point and collects extensions. */ /** \defgroup org_mitk_example_gui_extensionpointdefinition_internal Internal \ingroup org_mitk_example_gui_extensionpointdefinition \brief This subcategory includes the internal classes of the org.mitk.example.gui.extensionpointdefinition plugin. Other plugins must not rely on these classes. They contain implementation details and their interface may change at any time. We mean it. */ diff --git a/Examples/Plugins/org.mitk.example.gui.imaging/documentation/UserManual/QmitkIsoSurfaceUserManual.dox b/Examples/Plugins/org.mitk.example.gui.imaging/documentation/UserManual/QmitkIsoSurfaceUserManual.dox index 5eeb7cc302..905c0e1936 100644 --- a/Examples/Plugins/org.mitk.example.gui.imaging/documentation/UserManual/QmitkIsoSurfaceUserManual.dox +++ b/Examples/Plugins/org.mitk.example.gui.imaging/documentation/UserManual/QmitkIsoSurfaceUserManual.dox @@ -1,37 +1,37 @@ /** \page org_mitk_views_isosurface The Iso Surface Module \imageMacro{IsoSurfaceIcon.png,"Icon of the Iso Surface Module",2.00} Available sections: - \ref QmitkIsoSurfaceUserManualOverview - \ref QmitkIsoSurfaceUserManualFeatures - \ref QmitkIsoSurfaceUserManualUsage - \ref QmitkIsoSurfaceUserManualTroubleshooting \section QmitkIsoSurfaceUserManualOverview Overview -IsoSurface is a program module for creating surfaces (e.g. polygon structures) out of images. The user defines a threshold that seperates object and background inside the image. Pixels that belong to the object need grey values below the threshold. Pixles with grey values above the threshold will be ignored. The result is a polygon object +IsoSurface is a program module for creating surfaces (e.g. polygon structures) out of images. The user defines a threshold that separates object and background inside the image. Pixels that belong to the object need grey values below the threshold. Pixles with grey values above the threshold will be ignored. The result is a polygon object that can be saved as an *.stl-object. \section QmitkIsoSurfaceUserManualFeatures Features - Creates a surface by thresholding an image \section QmitkIsoSurfaceUserManualUsage Usage How to create a surface: - Load an image into the program, for example by drag & drop - Look for a meaningful threshold. All pixel grey values of the image that are lower than the threshold will be used to create the surface. All grey values that are higher than the surface will be ignored. You can find the best threshold by using the Volumetry-Functionality or by reading the grey value while clicking on a pixel (see picture 2). - Insert the threshold into the GUI - Press the Button "Create Surface" \imageMacro{IsoSurfaceGUI.png,"Graphical User Interface of Iso Surface",16.00} \section QmitkIsoSurfaceUserManualTroubleshooting Troubleshooting */ diff --git a/Examples/Plugins/org.mitk.example.gui.imaging/documentation/UserManual/QmitkSimpleExampleUserManual.dox b/Examples/Plugins/org.mitk.example.gui.imaging/documentation/UserManual/QmitkSimpleExampleUserManual.dox index e3493b2926..48e1d0f5d1 100644 --- a/Examples/Plugins/org.mitk.example.gui.imaging/documentation/UserManual/QmitkSimpleExampleUserManual.dox +++ b/Examples/Plugins/org.mitk.example.gui.imaging/documentation/UserManual/QmitkSimpleExampleUserManual.dox @@ -1,20 +1,20 @@ /** \page org_mitk_views_simpleexample The Simple Example module \imageMacro{SimpleExample-dox.png,"Icon of the Simple Example Module",2.00} \section QmitkSimpleExampleViewUserManualSummary Summary This module is namely a simple example for simple image interaction. It offers:
  • Sliders to navigate through the different slice stacks (Axial, Sagittal, Coronal) and the time steps
  • A "player" for image time series. It automatically steps through all time series with a speed defined by the slider on the right.
  • A button "Re-Initialize Navigators" to reinitialize those sliders to their initial position (Slice and time position 0)
  • A button "Take Screenshot" to take a screenshot of the chosen window (Axial, Sagittal, Coronal, 3D)
  • A button "Take HighDef 3D Screenshot" to take an high resolution screenshot of the 3D scene -
  • A button "Generate Movie" whichs takes a movie of the chosen window by scrolling either through all slices (Axial, Sagittal, Coronal). Please use the MovieMaker view for recording a movie of a rotating 3D scene. +
  • A button "Generate Movie" which takes a movie of the chosen window by scrolling either through all slices (Axial, Sagittal, Coronal). Please use the MovieMaker view for recording a movie of a rotating 3D scene.
  • A selection box where the 3D view can be transformed into either a red-blue stereo or a D4D stereo view
*/ diff --git a/Examples/Plugins/org.mitk.example.gui.imaging/src/ExamplesDll.h b/Examples/Plugins/org.mitk.example.gui.imaging/src/ExamplesDll.h index fc3ad053ed..e5d0c9bee3 100644 --- a/Examples/Plugins/org.mitk.example.gui.imaging/src/ExamplesDll.h +++ b/Examples/Plugins/org.mitk.example.gui.imaging/src/ExamplesDll.h @@ -1,36 +1,36 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef _EXAMPLES_EXPORT_DLL_H_ #define _EXAMPLES_EXPORT_DLL_H_ // // The following block is the standard way of creating macros which make exporting // from a DLL simpler. All files within this DLL are compiled with the org_mitk_gui_qt_examples_EXPORTS // symbol defined on the command line. this symbol should not be defined on any project // that uses this DLL. This way any other project whose source files include this file see -// org_mitk_gui_qt_examples_EXPORTS functions as being imported from a DLL, wheras this DLL sees symbols +// org_mitk_gui_qt_examples_EXPORTS functions as being imported from a DLL, whereas this DLL sees symbols // defined with this macro as being exported. // #if defined(_WIN32) && !defined(MITK_STATIC) #if defined(org_mitk_gui_qt_examples_EXPORTS) #define EXAMPLES_EXPORT __declspec(dllexport) #else #define EXAMPLES_EXPORT __declspec(dllimport) #endif #endif #if !defined(EXAMPLES_EXPORT) #define EXAMPLES_EXPORT #endif #endif /*_EXAMPLES_EXPORT_DLL_H_*/ diff --git a/Examples/Plugins/org.mitk.example.gui.imaging/src/internal/surfaceutilities/mitkSurfaceToPointSetFilter.h b/Examples/Plugins/org.mitk.example.gui.imaging/src/internal/surfaceutilities/mitkSurfaceToPointSetFilter.h index 487c733273..8ef1838cef 100644 --- a/Examples/Plugins/org.mitk.example.gui.imaging/src/internal/surfaceutilities/mitkSurfaceToPointSetFilter.h +++ b/Examples/Plugins/org.mitk.example.gui.imaging/src/internal/surfaceutilities/mitkSurfaceToPointSetFilter.h @@ -1,53 +1,53 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef mitkSurfaceToPointSetFilter_h #define mitkSurfaceToPointSetFilter_h // mitk headers #include "mitkSurface.h" #include namespace mitk { /** Documentation * @brief This filter converts the input surface into a point set. The output point set contains every point exactly * one time - * (no dublicated points like in the stl-format). + * (no duplicated points like in the stl-format). */ class SurfaceToPointSetFilter : public mitk::PointSetSource { public: mitkClassMacro(SurfaceToPointSetFilter, mitk::PointSetSource); itkFactorylessNewMacro(Self); itkCloneMacro(Self); using Superclass::SetInput; void SetInput(mitk::Surface::Pointer m_InputSurface); std::string GetErrorMessage(); protected: SurfaceToPointSetFilter(); ~SurfaceToPointSetFilter() override; /** @brief method generating the output of this filter. Called in the updated process of the pipeline. */ void GenerateData() override; //############### members ######################## mitk::Surface::Pointer m_InputSurface; std::string m_ErrorMessage; }; } // namespace mitk #endif diff --git a/Examples/Plugins/org.mitk.example.gui.multipleperspectives/documentation/doxygen/MultiplePerspectives.dox b/Examples/Plugins/org.mitk.example.gui.multipleperspectives/documentation/doxygen/MultiplePerspectives.dox index 0fd2354c43..eade0be3ea 100644 --- a/Examples/Plugins/org.mitk.example.gui.multipleperspectives/documentation/doxygen/MultiplePerspectives.dox +++ b/Examples/Plugins/org.mitk.example.gui.multipleperspectives/documentation/doxygen/MultiplePerspectives.dox @@ -1,36 +1,36 @@ /** \page BlueBerryExampleMultiplePerspectives Multiple perspectives and views \image html MultiplePerspectives.png The second example creates two perspectives. The first perspective has a visible editor area and uses a folder to bring the first view to the left side. The second perspective sets the editor area to invisible. The two views are filled with an empty QListWidget. The perspective bar is enabled. The visibility of the editor area is set in the corresponding perspective cpp file by: \snippet MinimumPerspective.cpp Visibility of editor area The visibility of the perspective bar is set in the applications MultiplePerspectives.cpp: \snippet MultiplePerspectives.cpp Visibility of perspective bar Here we can also set/change some other general preferences (e.g. the initial size): \snippet MultiplePerspectives.cpp initial window size The perspective bar: \image html Perspectivebar.png - The icons of the perspectives are definded by the extension declaration in the plugin.xml + The icons of the perspectives are defined by the extension declaration in the plugin.xml of the example and lie in the resources directory of the plug-in: \include org.mitk.example.gui.multipleperspectives/plugin.xml View complete source files: \li \github{Examples/Plugins/org.mitk.example.gui.multipleperspectives/src/internal/MultiplePerspectives.cpp,MultiplePerspectives.cpp} \li \github{Examples/Plugins/org.mitk.example.gui.multipleperspectives/src/internal/MultiplePerspectives.h,MultiplePerspectives.h} \li \github{Examples/Plugins/org.mitk.example.gui.multipleperspectives/src/internal/MinimumPerspective.cpp,MinimumPerspective.cpp} \li \github{Examples/Plugins/org.mitk.example.gui.multipleperspectives/src/internal/MinimumPerspective.h,MinimumPerspective.h} \li \github{Examples/Plugins/org.mitk.example.gui.multipleperspectives/src/internal/ExtendedPerspective.cpp,ExtendedPerspective.cpp} \li \github{Examples/Plugins/org.mitk.example.gui.multipleperspectives/src/internal/ExtendedPerspective.h,ExtendedPerspective.h} [\ref BlueBerryIntro] [Previous: \ref BlueBerryExampleMinimalApplication] [Next: \ref BlueBerrySelectionServiceIntro] */ diff --git a/Examples/Plugins/org.mitk.example.gui.selectionservicemitk.views/manifest_headers.cmake b/Examples/Plugins/org.mitk.example.gui.selectionservicemitk.views/manifest_headers.cmake index b59bf17ec9..d89cacf219 100644 --- a/Examples/Plugins/org.mitk.example.gui.selectionservicemitk.views/manifest_headers.cmake +++ b/Examples/Plugins/org.mitk.example.gui.selectionservicemitk.views/manifest_headers.cmake @@ -1,7 +1,7 @@ -set(Plugin-Name "MITK Example: Seletion service MITK") +set(Plugin-Name "MITK Example: Selection service MITK") set(Plugin-Version "1.0.0") set(Plugin-Vendor "German Cancer Research Center (DKFZ)") set(Plugin-ContactAddress "https://www.mitk.org") set(Require-Plugin org.mitk.gui.qt.common ) diff --git a/Examples/Plugins/org.mitk.example.gui.selectionservicemitk.views/src/internal/ListenerViewMitk.h b/Examples/Plugins/org.mitk.example.gui.selectionservicemitk.views/src/internal/ListenerViewMitk.h index eaaa78049a..e3de314fef 100644 --- a/Examples/Plugins/org.mitk.example.gui.selectionservicemitk.views/src/internal/ListenerViewMitk.h +++ b/Examples/Plugins/org.mitk.example.gui.selectionservicemitk.views/src/internal/ListenerViewMitk.h @@ -1,79 +1,79 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef LISTENERVIEWMITK_H_ #define LISTENERVIEWMITK_H_ // QMitk includes #include // berry includes #include // Qt includes #include // ui includes #include "ui_ListenerViewMitkControls.h" /** * \ingroup org_mitk_example_gui_selectionservicemitk * * \brief This BlueBerry view is part of the BlueBerry example * "Selection service MITK". It creates two radio buttons that listen * for selection events of the Qlistwidget (SelectionProvider) and * change the radio button state accordingly. * * @see SelectionViewMitk * */ class ListenerViewMitk : public QmitkAbstractView { Q_OBJECT public: static const std::string VIEW_ID; ListenerViewMitk(); protected: void CreateQtPartControl(QWidget *parent) override; void SetFocus() override; //! [MITK Selection Listener method] - /** @brief Reimplemention of method from QmitkAbstractView that implements the selection listener functionality. + /** @brief Reimplementation of method from QmitkAbstractView that implements the selection listener functionality. * @param part The workbench part responsible for the selection change. * @param nodes A list of selected nodes. * * @see QmitkAbstractView * */ void OnSelectionChanged(berry::IWorkbenchPart::Pointer part, const QList &nodes) override; //! [MITK Selection Listener method] private Q_SLOTS: /** @brief Simple slot function that changes the selection of the radio buttons according to the passed string. - * @param selectStr QString that contains the name of the slected list element + * @param selectStr QString that contains the name of the selected list element * */ void ToggleRadioMethod(QString selectStr); private: Ui::ListenerViewMitkControls m_Controls; QWidget *m_Parent; }; #endif /*LISTENERVIEWMITK_H_*/ diff --git a/Examples/Plugins/org.mitk.example.gui.selectionservicemitk/documentation/doxygen/SelectionServiceMitk.dox b/Examples/Plugins/org.mitk.example.gui.selectionservicemitk/documentation/doxygen/SelectionServiceMitk.dox index 453fc73b90..e5cfe8ef6d 100644 --- a/Examples/Plugins/org.mitk.example.gui.selectionservicemitk/documentation/doxygen/SelectionServiceMitk.dox +++ b/Examples/Plugins/org.mitk.example.gui.selectionservicemitk/documentation/doxygen/SelectionServiceMitk.dox @@ -1,42 +1,42 @@ /** \page BlueBerryExampleSelectionServiceMitk MITK DataNode Selections \brief An example application plug-in with a minimal selection service based on mitk::DataNode objects. \image html SelectionServiceMITK.png This example is an alternative for the Qt selection service described in the previous example. The selection service is based on MITK data nodes. Again the selection service is used to connect the selection of QListWidgetItems of the SelectionView with the radio buttons from the ListenerView and the - funtionality is the same. + functionality is the same. This time the SelectionView does not inherit from berry::QtViewPart but from QmitkAbstractView. QmitkAbstractView provides a virtual method called GetDataNodeSelectionModel() with which the selection model of the QListWidget can be returned. No selection provider needs to be registered explicitly with the workbench. In the SelectionViewMitk.h the method from QmitkAbstractView ist declared: \snippet SelectionViewMitk.h MITK Selection Provider method First we need to create two data nodes and set some creative names in the SelectionViewMitk.cpp: \snippet SelectionViewMitk.cpp MITK Selection Provider data nodes These data nodes are used to create two QListWidgetItems that are added to the QListWidget: \snippet SelectionViewMitk.cpp MITK Selection Provider listwidgetitems Now if one of the data nodes is selected in the QListWidget or the selection changes this is registered by the ListenerView. In the ListenerViewMitk.h we reimplement the method from QmitkAbstractView that implements the selection listener functionality: \snippet ListenerViewMitk.h MITK Selection Listener method The simple implementation of this method looks as follows: \snippet ListenerViewMitk.cpp MITK Selection Listener method implementation View complete source files: \li \github{Examples/Plugins/org.mitk.example.gui.selectionservicemitk.views/src/internal/SelectionViewMitk.cpp,SelectionViewMitk.cpp} \li \github{Examples/Plugins/org.mitk.example.gui.selectionservicemitk.views/src/internal/SelectionViewMitk.h,SelectionViewMitk.h} \li \github{Examples/Plugins/org.mitk.example.gui.selectionservicemitk.views/src/internal/ListenerViewMitk.cpp,ListenerViewMitk.cpp} \li \github{Examples/Plugins/org.mitk.example.gui.selectionservicemitk.views/src/internal/ListenerViewMitk.h,ListenerViewMitk.h} [\ref BlueBerrySelectionServiceIntro] [Previous: \ref BlueBerryExampleSelectionServiceQt] [\ref BlueBerryExamples] */ diff --git a/Examples/Plugins/org.mitk.example.gui.selectionservicemitk/manifest_headers.cmake b/Examples/Plugins/org.mitk.example.gui.selectionservicemitk/manifest_headers.cmake index ddeb405483..0433c5dd12 100644 --- a/Examples/Plugins/org.mitk.example.gui.selectionservicemitk/manifest_headers.cmake +++ b/Examples/Plugins/org.mitk.example.gui.selectionservicemitk/manifest_headers.cmake @@ -1,7 +1,7 @@ -set(Plugin-Name "MITK Example: Seletion service MITK") +set(Plugin-Name "MITK Example: Selection service MITK") set(Plugin-Version "1.0.0") set(Plugin-Vendor "German Cancer Research Center (DKFZ)") set(Plugin-ContactAddress "https://www.mitk.org") set(Require-Plugin org.blueberry.ui.qt ) diff --git a/Examples/Plugins/org.mitk.example.gui.selectionserviceqt/documentation/doxygen/SelectionServiceQt.dox b/Examples/Plugins/org.mitk.example.gui.selectionserviceqt/documentation/doxygen/SelectionServiceQt.dox index 01c0933553..2dc994f491 100644 --- a/Examples/Plugins/org.mitk.example.gui.selectionserviceqt/documentation/doxygen/SelectionServiceQt.dox +++ b/Examples/Plugins/org.mitk.example.gui.selectionserviceqt/documentation/doxygen/SelectionServiceQt.dox @@ -1,47 +1,47 @@ /** \page BlueBerryExampleSelectionServiceQt Qt Model/View selections \brief An example application plug-in with a minimal selection service based on Qt selection models. \image html SelectionServiceQT.png In this example the selection service is used to connect the selection of the radio buttons from one view with the selection of the list of the other view. The SelectionView holds a QListWidget that provides the user selection (qt selection provider) for the selection listener (ListenerView). The radio buttons of the listener view are changed according to the selection in the QListWidget. Vice versa the radio buttons (the selection listener) does not provide any selection events. If the user changes the radio button state the QListWidget is not altered. - For additional informations on the selection service concept see https://www.mitk.org/wiki/Article_Using_the_Selection_Service + For additional information on the selection service concept see https://www.mitk.org/wiki/Article_Using_the_Selection_Service The berry::QtSelectionProvider class implements the interface berry::ISelectionProvider. Due to the model/view concept in Qt, the workbench provides the berry::QtSelectionProvider class for Qt viewers which must be provided with a QItemSelectionModel. In the SelectionView.h we declare a pointer that holds the selection provider... \snippet SelectionView.h Qt Selection Provider ...and in the SelectionView.cpp we set the selection model to the model of the QListWidget (m_SelectionList) \snippet SelectionView.cpp Qt Selection Provider registration Now that the QListWidget of the SelectionView sends out selection events we need a listener implementation. In the ListenerView.h we need to include the ISelectionListener which is a simple class with just one method. The method that implements the selection listener functionality of ISelectionListener and the pointer that holds the selection listener is declared... \snippet ListenerView.h Qt Selection Listener method and pointer ...and implemented in the cpp-file: \snippet ListenerView.cpp Qt Selection Listener method implementation Now the name of the selected list element is passed to a method that toggles the radio buttons of the ListenerView accordingly. View complete source files: \li \github{Examples/Plugins/org.mitk.example.gui.selectionserviceqt/src/internal/SelectionView.cpp,SelectionView.cpp} \li \github{Examples/Plugins/org.mitk.example.gui.selectionserviceqt/src/internal/SelectionView.h,SelectionView.h} \li \github{Examples/Plugins/org.mitk.example.gui.selectionserviceqt/src/internal/ListenerView.cpp,ListenerView.cpp} \li \github{Examples/Plugins/org.mitk.example.gui.selectionserviceqt/src/internal/ListenerView.h,ListenerView.h} [\ref BlueBerrySelectionServiceIntro] [Next: \ref BlueBerryExampleSelectionServiceMitk] [\ref BlueBerryExamples] */ diff --git a/Examples/Plugins/org.mitk.example.gui.selectionserviceqt/manifest_headers.cmake b/Examples/Plugins/org.mitk.example.gui.selectionserviceqt/manifest_headers.cmake index 7b788636a4..a6bfba48ff 100644 --- a/Examples/Plugins/org.mitk.example.gui.selectionserviceqt/manifest_headers.cmake +++ b/Examples/Plugins/org.mitk.example.gui.selectionserviceqt/manifest_headers.cmake @@ -1,7 +1,7 @@ -set(Plugin-Name "MITK Example: Seletion service QT") +set(Plugin-Name "MITK Example: Selection service QT") set(Plugin-Version "1.0.0") set(Plugin-Vendor "German Cancer Research Center (DKFZ)") set(Plugin-ContactAddress "https://www.mitk.org") set(Require-Plugin org.blueberry.ui.qt ) diff --git a/Examples/Plugins/org.mitk.example.gui.selectionserviceqt/src/internal/ListenerView.h b/Examples/Plugins/org.mitk.example.gui.selectionserviceqt/src/internal/ListenerView.h index a7fcf07224..d13916f867 100644 --- a/Examples/Plugins/org.mitk.example.gui.selectionserviceqt/src/internal/ListenerView.h +++ b/Examples/Plugins/org.mitk.example.gui.selectionserviceqt/src/internal/ListenerView.h @@ -1,81 +1,81 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef LISTENERVIEW_H_ #define LISTENERVIEW_H_ // berry includes #include #include #include // ui includes #include "ui_ListenerViewControls.h" /** * \ingroup org_mitk_example_gui_selectionserviceqt * * \brief This BlueBerry view is part of the BlueBerry example * "Selection service QT". It creates two radio buttons that listen * for selection events of the Qlistwidget (SelectionProvider) and * change the radio button state accordingly. * * @see SelectionView */ class ListenerView : public berry::QtViewPart { Q_OBJECT public: static const std::string VIEW_ID; ListenerView(); ~ListenerView() override; protected: void CreateQtPartControl(QWidget *parent) override; void SetFocus() override; private Q_SLOTS: /** * @brief Simple slot function that changes the selection of the radio buttons according to the passed string. - * @param selectStr QString that contains the name of the slected list element + * @param selectStr QString that contains the name of the selected list element */ void ToggleRadioMethod(QString selectStr); private: //! [Qt Selection Listener method and pointer] /** * @brief Method of berry::ISelectionListener that implements the selection listener functionality. * @param sourcepart The workbench part responsible for the selection change. * @param selection This parameter holds the current selection. * * @see ISelectionListener */ void SelectionChanged(const berry::IWorkbenchPart::Pointer &sourcepart, const berry::ISelection::ConstPointer &selection); /** @brief this pointer holds the selection listener */ QScopedPointer m_SelectionListener; //! [Qt Selection Listener method and pointer] friend struct berry::SelectionChangedAdapter; Ui::ListenerViewControls m_Controls; QWidget *m_Parent; }; #endif /*LISTENERVIEW_H_*/ diff --git a/Examples/Tutorial/Step5/Step5.cpp b/Examples/Tutorial/Step5/Step5.cpp index e4b149aef6..85d72b1e78 100644 --- a/Examples/Tutorial/Step5/Step5.cpp +++ b/Examples/Tutorial/Step5/Step5.cpp @@ -1,203 +1,203 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkRegisterClasses.h" #include "QmitkRenderWindow.h" #include "QmitkSliceWidget.h" #include "mitkNodePredicateDataType.h" #include "mitkProperties.h" #include "mitkRenderingManager.h" #include "mitkStandaloneDataStorage.h" #include "mitkPointSet.h" // NEW INCLUDE #include "mitkPointSetDataInteractor.h" #include #include #include #include //##Documentation //## @brief Interactively add points //## //## As in Step4, load one or more data sets (many image, //## surface and other formats) and create 3 views on the data. //## Additionally, we want to interactively add points. A node containing //## a PointSet as data is added to the data tree and a PointSetDataInteractor //## is associated with the node, which handles the interaction. The //## @em interaction @em pattern is defined in a state-machine, stored in an //## external XML file. Thus, we need to load a state-machine //## The interaction patterns defines the @em events, //## on which the interactor reacts (e.g., which mouse buttons are used to //## set a point), the @em transition to the next state (e.g., the initial //## may be "empty point set") and associated @a actions (e.g., add a point -//## at the position where the mouse-click occured). +//## at the position where the mouse-click occurred). int main(int argc, char *argv[]) { QApplication qtapplication(argc, argv); if (argc < 2) { fprintf( stderr, "Usage: %s [filename1] [filename2] ...\n\n", itksys::SystemTools::GetFilenameName(argv[0]).c_str()); return 1; } // Register Qmitk-dependent global instances QmitkRegisterClasses(); //************************************************************************* // Part I: Basic initialization //************************************************************************* // Create a DataStorage mitk::StandaloneDataStorage::Pointer ds = mitk::StandaloneDataStorage::New(); //************************************************************************* // Part II: Create some data by reading files //************************************************************************* int i; for (i = 1; i < argc; ++i) { // For testing if (strcmp(argv[i], "-testing") == 0) continue; // Load datanode (eg. many image formats, surface formats, etc.) mitk::StandaloneDataStorage::SetOfObjects::Pointer dataNodes = mitk::IOUtil::Load(argv[i], *ds); //********************************************************************* // Part III: Put the data into the datastorage //********************************************************************* // Add the node to the DataStorage if (dataNodes->empty()) { fprintf(stderr, "Could not open file %s \n\n", argv[i]); exit(2); } } //************************************************************************* // Part V: Create windows and pass the tree to it //************************************************************************* // Create toplevel widget with horizontal layout QWidget toplevelWidget; QHBoxLayout layout; layout.setSpacing(2); layout.setContentsMargins({}); toplevelWidget.setLayout(&layout); //************************************************************************* // Part Va: 3D view //************************************************************************* // Create a renderwindow QmitkRenderWindow renderWindow(&toplevelWidget); layout.addWidget(&renderWindow); // Tell the renderwindow which (part of) the tree to render renderWindow.GetRenderer()->SetDataStorage(ds); // Use it as a 3D view renderWindow.GetRenderer()->SetMapperID(mitk::BaseRenderer::Standard3D); // Reposition the camera to include all visible actors renderWindow.GetRenderer()->GetVtkRenderer()->ResetCamera(); //************************************************************************* // Part Vb: 2D view for slicing axially //************************************************************************* // Create QmitkSliceWidget, which is based on the class // QmitkRenderWindow, but additionally provides sliders QmitkSliceWidget view2(&toplevelWidget); layout.addWidget(&view2); // Tell the QmitkSliceWidget which (part of) the tree to render. // By default, it slices the data axially view2.SetDataStorage(ds); mitk::DataStorage::SetOfObjects::ConstPointer rs = ds->GetSubset(mitk::TNodePredicateDataType::New()); view2.SetData(rs->Begin(), mitk::AnatomicalPlane::Axial); // We want to see the position of the slice in 2D and the // slice itself in 3D: add it to the tree! ds->Add(view2.GetRenderer()->GetCurrentWorldPlaneGeometryNode()); //************************************************************************* // Part Vc: 2D view for slicing sagittally //************************************************************************* // Create QmitkSliceWidget, which is based on the class // QmitkRenderWindow, but additionally provides sliders QmitkSliceWidget view3(&toplevelWidget); layout.addWidget(&view3); // Tell the QmitkSliceWidget which (part of) the tree to render // and to slice sagittally view3.SetDataStorage(ds); view3.SetData(rs->Begin(), mitk::AnatomicalPlane::Sagittal); // We want to see the position of the slice in 2D and the // slice itself in 3D: add it to the tree! ds->Add(view3.GetRenderer()->GetCurrentWorldPlaneGeometryNode()); // ******************************************************* // ****************** START OF NEW PART ****************** // ******************************************************* //************************************************************************* // Part VI: For allowing to interactively add points ... //************************************************************************* // ATTENTION: It is very important that the renderer already know their DataStorage, // because registerig DataInteractors with the render windows is done automatically // and only works if the BaseRenderer and the DataStorage know each other. // Create PointSet and a node for it mitk::PointSet::Pointer pointSet = mitk::PointSet::New(); mitk::DataNode::Pointer pointSetNode = mitk::DataNode::New(); // Store the point set in the DataNode pointSetNode->SetData(pointSet); // Add the node to the tree ds->Add(pointSetNode); // Create PointSetDataInteractor mitk::PointSetDataInteractor::Pointer interactor = mitk::PointSetDataInteractor::New(); // Set the StateMachine pattern that describes the flow of the interactions interactor->LoadStateMachine("PointSet.xml"); // Set the configuration file, which describes the user interactions that trigger actions // in this file SHIFT + LeftClick triggers add Point, but by modifying this file, // it could as well be changes to any other user interaction. interactor->SetEventConfig("PointSetConfig.xml"); // Assign the pointSetNode to the interactor, // alternatively one could also add the DataInteractor to the pointSetNode using the SetDataInteractor() method. interactor->SetDataNode(pointSetNode); // ******************************************************* // ******************* END OF NEW PART ******************* // ******************************************************* //************************************************************************* // Part VII: Qt-specific initialization //************************************************************************* toplevelWidget.show(); return qtapplication.exec(); } /** \example Step5.cpp */ diff --git a/MITKCPackOptions.cmake.in b/MITKCPackOptions.cmake.in index 5728c46f84..bd50c04e08 100644 --- a/MITKCPackOptions.cmake.in +++ b/MITKCPackOptions.cmake.in @@ -1,31 +1,31 @@ if(CPACK_GENERATOR MATCHES "NSIS") # set the package header icon for MUI set(CPACK_PACKAGE_ICON "@MITK_SOURCE_DIR@\\mitk.bmp") - # set the install/unistall icon used for the installer itself + # set the install/uninstall icon used for the installer itself # There is a bug in NSIS that does not handle full unix paths properly. set(CPACK_NSIS_MUI_ICON "@MITK_SOURCE_DIR@\\mitk.ico") set(CPACK_NSIS_MUI_UNIICON "@MITK_SOURCE_DIR@\\mitk.ico") set(CPACK_NSIS_DISPLAY_NAME "MITK - Medical Imaging and Interaction Toolkit") # tell cpack to create links to the doc files set(CPACK_NSIS_MENU_LINKS "https://www.mitk.org" "MITK Web Site" ) # tell cpack the executables you want in the start menu as links set(CPACK_PACKAGE_EXECUTABLES "@MITK_CPACK_PACKAGE_EXECUTABLES@") # tell cpack to create a desktop link to MainApp set(CPACK_CREATE_DESKTOP_LINKS "@CPACK_CREATE_DESKTOP_LINKS@") set(CPACK_NSIS_INSTALLED_ICON_NAME "bin\\\\mitk.ico") set(CPACK_NSIS_HELP_LINK "http:\\\\www.mitk.org") set(CPACK_NSIS_URL_INFO_ABOUT "http:\\\\www.mitk.org") set(CPACK_NSIS_CONTACT mitk@mitk.org) set(CPACK_NSIS_MODIFY_PATH ON) endif(CPACK_GENERATOR MATCHES "NSIS") diff --git a/MITKConfig.cmake.in b/MITKConfig.cmake.in index f7b840ceb2..b63d5b6dbe 100644 --- a/MITKConfig.cmake.in +++ b/MITKConfig.cmake.in @@ -1,252 +1,252 @@ if(CMAKE_VERSION VERSION_LESS @MITK_CMAKE_MINIMUM_REQUIRED_VERSION@) message(FATAL_ERROR "MITK requires at least CMake Version @MITK_CMAKE_MINIMUM_REQUIRED_VERSION@") endif() # The MITK version number set(MITK_VERSION_MAJOR "@MITK_VERSION_MAJOR@") set(MITK_VERSION_MINOR "@MITK_VERSION_MINOR@") set(MITK_VERSION_PATCH "@MITK_VERSION_PATCH@") set(MITK_VERSION_STRING "@MITK_VERSION_STRING@") #----------------------------------------------------------------------------- # C++ language standard #----------------------------------------------------------------------------- set(MITK_CXX_STANDARD @MITK_CXX_STANDARD@) #----------------------------------------------------------------------------- # Include required CMake scripts #----------------------------------------------------------------------------- # Update the CMake module path set(MITK_CMAKE_MODULE_PATH "@MITK_SOURCE_DIR@/CMake") list(APPEND CMAKE_MODULE_PATH ${MITK_CMAKE_MODULE_PATH}) # Standard CMake macros include(CMakeParseArguments) include(FeatureSummary) include(FindPackageHandleStandardArgs) include(GenerateExportHeader) # Include MITK macros include(MacroParseArguments) include(mitkFunctionAddManifest) include(mitkFunctionAddCustomModuleTest) include(mitkFunctionCheckMitkCompatibility) include(mitkFunctionCheckModuleDependencies) include(mitkFunctionConfigureVisualStudioUserProjectFile) include(mitkFunctionCreateBlueBerryApplication) include(mitkFunctionCreateCommandLineApp) include(mitkFunctionCreateModule) include(mitkFunctionCreatePlugin) include(mitkFunctionCreateProvisioningFile) include(mitkFunctionCreateWindowsBatchScript) include(mitkFunctionGetLibrarySearchPaths) include(mitkFunctionInstallAutoLoadModules) include(mitkFunctionInstallCTKPlugin) include(mitkFunctionInstallProvisioningFiles) include(mitkFunctionInstallThirdPartyCTKPlugins) include(mitkFunctionOrganizeSources) include(mitkFunctionUseModules) include(mitkMacroCreateExecutable) include(mitkMacroCreateModuleTests) include(mitkMacroFindDependency) include(mitkMacroGenerateToolsLibrary) include(mitkMacroGetPMDPlatformString) include(mitkMacroInstall) include(mitkMacroInstallHelperApp) include(mitkMacroInstallTargets) include(mitkMacroMultiplexPicType) #----------------------------------------------------------------------------- # MITK flags and directories #----------------------------------------------------------------------------- # MITK compiler flags set(MITK_C_FLAGS "@MITK_C_FLAGS@") set(MTTK_C_FLAGS_DEBUG "@MITK_C_FLAGS_DEBUG@") set(MITK_C_FLAGS_RELEASE "@MITK_C_FLAGS_RELEASE@") set(MITK_CXX_FLAGS "@MITK_CXX_FLAGS@") set(MTTK_CXX_FLAGS_DEBUG "@MITK_CXX_FLAGS_DEBUG@") set(MITK_CXX_FLAGS_RELEASE "@MITK_CXX_FLAGS_RELEASE@") # MITK linker flags set(MITK_EXE_LINKER_FLAGS "@MITK_EXE_LINKER_FLAGS@") set(MITK_SHARED_LINKER_FLAGS "@MITK_SHARED_LINKER_FLAGS@") set(MITK_MODULE_LINKER_FLAGS "@MITK_MODULE_LINKER_FLAGS@") # MITK specific directories set(MITK_SOURCE_DIR "@MITK_SOURCE_DIR@") set(MITK_BINARY_DIR "@MITK_BINARY_DIR@") set(MITK_CMAKE_DIR "@MITK_CMAKE_DIR@") # MITK output directories set(MITK_CMAKE_RUNTIME_OUTPUT_DIRECTORY "@MITK_CMAKE_RUNTIME_OUTPUT_DIRECTORY@") set(MITK_CMAKE_ARCHIVE_OUTPUT_DIRECTORY "@MITK_CMAKE_ARCHIVE_OUTPUT_DIRECTORY@") set(MITK_CMAKE_LIBRARY_OUTPUT_DIRECTORY "@MITK_CMAKE_LIBRARY_OUTPUT_DIRECTORY@") #----------------------------------------------------------------------------- # Miscellaneous variables #----------------------------------------------------------------------------- # Internal version numbers, used for approximate compatibility checks # of a MITK development version (non-release). set(MITK_VERSION_PLUGIN_SYSTEM 2) # dropped legacy BlueBerry plug-in CMake support set(MITK_DATA_DIR "@MITK_DATA_DIR@") set(UTILITIES_DIR "@UTILITIES_DIR@") set(REGISTER_QFUNCTIONALITY_CPP_IN "@REGISTER_QFUNCTIONALITY_CPP_IN@") set(MITK_DOXYGEN_TAGFILE_NAME "@MITK_DOXYGEN_TAGFILE_NAME@") set(MITK_LEGACY_EXPORT_MACRO_NAME 1) set(DCMTK_CMAKE_DEBUG_POSTFIX @DCMTK_CMAKE_DEBUG_POSTFIX@) # Get the canonical name of the directory to avoid potential case mismatch, # e.g. in the drive letter on Windows. get_filename_component(CMAKE_CURRENT_LIST_DIR_REALPATH ${CMAKE_CURRENT_LIST_DIR} REALPATH) if(CMAKE_CURRENT_LIST_DIR_REALPATH STREQUAL MITK_BINARY_DIR) set(MITK_EXTERNAL_PROJECT_PREFIX @MITK_EXTERNAL_PROJECT_PREFIX@) endif() set(MITK_MODULES_PACKAGE_DEPENDS_DIR "@MITK_MODULES_PACKAGE_DEPENDS_DIR@") if(MODULES_PACKAGE_DEPENDS_DIRS) list(APPEND MODULES_PACKAGE_DEPENDS_DIRS ${MITK_MODULES_PACKAGE_DEPENDS_DIR}) list(REMOVE_DUPLICATES MODULES_PACKAGE_DEPENDS_DIRS) else() set(MODULES_PACKAGE_DEPENDS_DIRS ${MITK_MODULES_PACKAGE_DEPENDS_DIR}) endif() #----------------------------------------------------------------------------- # External dependencies #----------------------------------------------------------------------------- list(APPEND CMAKE_PREFIX_PATH "@MITK_EXTERNAL_PROJECT_PREFIX@") # ----------------------------------------- # Qt variables and dependencies set(MITK_USE_Qt6 @MITK_USE_Qt6@) if(MITK_USE_Qt6) set(MITK_QT6_MINIMUM_VERSION @MITK_QT6_MINIMUM_VERSION@) set(MITK_QT6_COMPONENTS @MITK_QT6_COMPONENTS@) mitkMacroFindDependency(Qt6 ${MITK_QT6_MINIMUM_VERSION} COMPONENTS ${MITK_QT6_COMPONENTS}) endif() # ----------------------------------------- # Special Python variables set(MITK_USE_Python3 @MITK_USE_Python3@) if(MITK_USE_Python3) set(PYTHON_EXECUTABLE "@PYTHON_EXECUTABLE@" CACHE FILEPATH "") set(PYTHON_INCLUDE_DIR "@PYTHON_INCLUDE_DIR@" CACHE PATH "") set(PYTHON_LIBRARY "@PYTHON_LIBRARY@" CACHE FILEPATH "") set(PYTHON_INCLUDE_DIR2 "@PYTHON_INCLUDE_DIR2@" CACHE PATH "") mitkMacroFindDependency(Python3 COMPONENTS Interpreter Development NumPy) endif() # ----------------------------------------- # Special Boost variables set(MITK_USE_Boost_LIBRARIES @MITK_USE_Boost_LIBRARIES@) set(MITK_USE_SYSTEM_Boost @MITK_USE_SYSTEM_Boost@) set(Boost_ROOT "@Boost_ROOT@" CACHE PATH "") set(BOOST_LIBRARYDIR "@BOOST_LIBRARYDIR@" CACHE PATH "") set(Boost_ADDITIONAL_VERSIONS 1.74 1.74.0) # We need this later for a DCMTK workaround set(_dcmtk_dir_orig "@DCMTK_DIR@") # ----------------------------------------- # External dependencies from the superbuild # or injected from somewhere else via # _DIR variables. @MITK_CONFIG_EXTERNAL_PROJECTS@ # Ensure the MITK module path comes first set(CMAKE_MODULE_PATH ${MITK_CMAKE_MODULE_PATH} ${CMAKE_MODULE_PATH}) # ----------------------------------------- # Special handling for DCMTK if(MITK_USE_DCMTK) # Due to the preferred CONFIG mode in find_package calls above, # the DCMTKConfig.cmake file is read, which does not provide useful - # package information. We explictly need MODULE mode to find DCMTK. + # package information. We explicitly need MODULE mode to find DCMTK. if(${_dcmtk_dir_orig} MATCHES "${MITK_EXTERNAL_PROJECT_PREFIX}.*") # Help our FindDCMTK.cmake script find our super-build DCMTK set(DCMTK_DIR ${MITK_EXTERNAL_PROJECT_PREFIX}) else() # Use the original value set(DCMTK_DIR ${_dcmtk_dir_orig}) endif() find_package(DCMTK REQUIRED MODULE) endif() # ----------------------------------------- # Special handling for DCMQI if(MITK_USE_DCMQI) # Due to the preferred CONFIG mode in find_package calls above, # the DCMQIConfig.cmake file is read, which does not provide useful - # package information. We explictly need MODULE mode to find DCMQI. + # package information. We explicitly need MODULE mode to find DCMQI. # Help our FindDCMQI.cmake script find our super-build DCMQI set(DCMQI_DIR ${MITK_EXTERNAL_PROJECT_PREFIX}) find_package(DCMQI REQUIRED) endif() # ----------------------------------------- # Special handling for Boost if(MITK_USE_Boost) link_directories(${Boost_LIBRARY_DIRS}) endif() # ----------------------------------------- # Internal project dependencies set(CppMicroServices_DIR "@CppMicroServices_DIR@") mitkMacroFindDependency(CppMicroServices) set(MITK_USE_BLUEBERRY @MITK_USE_BLUEBERRY@) if(MITK_USE_BLUEBERRY) set(MITK_PLUGIN_USE_FILE "@MITK_PLUGIN_USE_FILE@") if(MITK_PLUGIN_USE_FILE) if(EXISTS "${MITK_PLUGIN_USE_FILE}") include("${MITK_PLUGIN_USE_FILE}") endif() endif() set(MITK_PLUGIN_PROVISIONING_FILE "@MITK_EXTAPP_PROVISIONING_FILE@") set(MITK_PROVISIONING_FILES "${BLUEBERRY_PLUGIN_PROVISIONING_FILE}" "${MITK_PLUGIN_PROVISIONING_FILE}") endif() set(BLUEBERRY_USE_QT_HELP @BLUEBERRY_USE_QT_HELP@) if(BLUEBERRY_USE_QT_HELP AND DOXYGEN_VERSION VERSION_LESS "1.8.7") message("Setting BLUEBERRY_USE_QT_HELP to OFF because Doxygen version 1.8.7 or newer not found.") set(BLUEBERRY_USE_QT_HELP OFF) endif() set(BLUEBERRY_QTPLUGIN_PATH "@BLUEBERRY_QTPLUGIN_PATH@") set(QT_HELPGENERATOR_EXECUTABLE "@QT_HELPGENERATOR_EXECUTABLE@") set(QT_COLLECTIONGENERATOR_EXECUTABLE "@QT_COLLECTIONGENERATOR_EXECUTABLE@") #----------------------------------------------------------------------------- # Import MITK targets and set custom properties #----------------------------------------------------------------------------- if(NOT MITK_EXPORTS_FILE_INCLUDED) if(EXISTS "@MITK_EXPORTS_FILE@") set(MITK_EXPORTS_FILE_INCLUDED 1) include("@MITK_EXPORTS_FILE@") endif() endif() # Set properties on exported targets @MITK_EXPORTED_TARGET_PROPERTIES@ diff --git a/Plugins/org.blueberry.core.commands/src/common/berryNamedHandleObject.h b/Plugins/org.blueberry.core.commands/src/common/berryNamedHandleObject.h index 549e017b07..3e0c48e9c7 100644 --- a/Plugins/org.blueberry.core.commands/src/common/berryNamedHandleObject.h +++ b/Plugins/org.blueberry.core.commands/src/common/berryNamedHandleObject.h @@ -1,80 +1,80 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef BERRYNAMEDHANDLEOBJECT_H_ #define BERRYNAMEDHANDLEOBJECT_H_ #include "berryHandleObject.h" namespace berry { /** * A handle object that carries with it a name and a description. This type of * handle object is quite common across the commands code base. For example, * Command, Context and Scheme. * * @since 3.1 */ class BERRY_COMMANDS NamedHandleObject : public HandleObject { public: berryObjectMacro(NamedHandleObject); protected: /** * The description for this handle. This value may be null if * the handle is undefined or has no description. */ QString description; /** - * The name of this handle. This valud should not be null + * The name of this handle. This value should not be null * unless the handle is undefined. */ QString name; /** * Constructs a new instance of NamedHandleObject. * * @param id * The identifier for this handle; must not be null. */ NamedHandleObject(const QString& id); public: /** * Returns the description for this handle. * * @return The description; may be null if there is no * description. * @throws NotDefinedException * If the handle is not currently defined. */ virtual QString GetDescription() const; /** * Returns the name for this handle. * * @return The name for this handle; never null. * @throws NotDefinedException * If the handle is not currently defined. */ virtual QString GetName() const; }; } #endif /*BERRYNAMEDHANDLEOBJECT_H_*/ diff --git a/Plugins/org.blueberry.core.runtime/src/internal/berryExtensionRegistry.cpp b/Plugins/org.blueberry.core.runtime/src/internal/berryExtensionRegistry.cpp index 360ee8568c..6e6cf25925 100644 --- a/Plugins/org.blueberry.core.runtime/src/internal/berryExtensionRegistry.cpp +++ b/Plugins/org.blueberry.core.runtime/src/internal/berryExtensionRegistry.cpp @@ -1,1340 +1,1340 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "berryExtensionRegistry.h" #include "berryCombinedEventDelta.h" #include "berryConfigurationElement.h" #include "berryConfigurationElementAttribute.h" #include "berryConfigurationElementDescription.h" #include "berryExtension.h" #include "berryExtensionHandle.h" #include "berryExtensionPoint.h" #include "berryExtensionPointHandle.h" #include "berryExtensionsParser.h" #include "berryIConfigurationElement.h" #include "berryIExtension.h" #include "berryIExtensionPoint.h" #include "berrySimpleExtensionPointFilter.h" #include "berryMultiStatus.h" #include "berryRegistryConstants.h" #include "berryRegistryContribution.h" #include "berryRegistryContributor.h" #include "berryRegistryMessages.h" #include "berryRegistryObjectFactory.h" #include "berryRegistryObjectManager.h" #include "berryRegistryProperties.h" #include "berryRegistryStrategy.h" #include "berryStatus.h" #include #include namespace berry { struct ExtensionRegistry::ListenerInfo { IExtensionPointFilter filter; IRegistryEventListener* listener; ListenerInfo(IRegistryEventListener* listener, const IExtensionPointFilter& filter) : filter(filter), listener(listener) { } /** * Used by ListenerList to ensure uniqueness. */ bool operator==(const ListenerInfo& another) const { return another.listener == this->listener; } }; void ExtensionRegistry::Add(const SmartPointer &element) { QWriteLocker l(&access); eventDelta = CombinedEventDelta::RecordAddition(); BasicAdd(element, true); FireRegistryChangeEvent(); eventDelta.Reset(); } QString ExtensionRegistry::AddExtension(int extension) { Extension::Pointer addedExtension = registryObjects->GetObject(extension, RegistryObjectManager::EXTENSION).Cast(); QString extensionPointToAddTo = addedExtension->GetExtensionPointIdentifier(); ExtensionPoint::Pointer extPoint = registryObjects->GetExtensionPointObject(extensionPointToAddTo); //orphan extension if (extPoint.IsNull()) { registryObjects->AddOrphan(extensionPointToAddTo, extension); return QString(); } // otherwise, link them QList newExtensions = extPoint->GetRawChildren(); newExtensions.push_back(extension); Link(extPoint, newExtensions); if (!eventDelta.IsNull()) eventDelta.RememberExtension(extPoint, extension); return extPoint->GetNamespace(); //return RecordChange(extPoint, extension, ExtensionDelta::ADDED); } QString ExtensionRegistry::AddExtensionPoint(int extPoint) { ExtensionPoint::Pointer extensionPoint = registryObjects->GetObject(extPoint, RegistryObjectManager::EXTENSION_POINT).Cast(); if (!eventDelta.IsNull()) eventDelta.RememberExtensionPoint(extensionPoint); QList orphans = registryObjects->RemoveOrphans(extensionPoint->GetUniqueIdentifier()); if (orphans.empty()) return QString(); Link(extensionPoint, orphans); if (!eventDelta.IsNull()) eventDelta.RememberExtensions(extensionPoint, orphans); return extensionPoint->GetNamespace(); //return RecordChange(extensionPoint, orphans, ExtensionDelta::ADDED); } QSet ExtensionRegistry::AddExtensionsAndExtensionPoints(const SmartPointer& element) { // now add and resolve extensions and extension points QSet affectedNamespaces; QList extPoints = element->GetExtensionPoints(); for (int i = 0; i < extPoints.size(); i++) { QString namespaze = this->AddExtensionPoint(extPoints[i]); if (!namespaze.isEmpty()) affectedNamespaces.insert(namespaze); } QList extensions = element->GetExtensions(); for (int i = 0; i < extensions.size(); i++) { QString namespaze = this->AddExtension(extensions[i]); if (!namespaze.isEmpty()) affectedNamespaces.insert(namespaze); } return affectedNamespaces; } void ExtensionRegistry::AddListenerInternal(IRegistryEventListener* listener, const IExtensionPointFilter& filter) { listeners.Add(ListenerInfo(listener, filter)); } void ExtensionRegistry::BasicAdd(const SmartPointer& element, bool link) { registryObjects->AddContribution(element); if (!link) return; AddExtensionsAndExtensionPoints(element); SetObjectManagers(registryObjects->CreateDelegatingObjectManager( registryObjects->GetAssociatedObjects(element->GetContributorId()))); } void ExtensionRegistry::SetObjectManagers(const SmartPointer& manager) { if (!eventDelta.IsNull()) eventDelta.SetObjectManager(manager); } void ExtensionRegistry::BasicRemove(const QString& contributorId) { // ignore anonymous namespaces RemoveExtensionsAndExtensionPoints(contributorId); QHash associatedObjects = registryObjects->GetAssociatedObjects(contributorId); registryObjects->RemoveObjects(associatedObjects); registryObjects->AddNavigableObjects(associatedObjects); // put the complete set of navigable objects SetObjectManagers(registryObjects->CreateDelegatingObjectManager(associatedObjects)); registryObjects->RemoveContribution(contributorId); registryObjects->RemoveContributor(contributorId); } void ExtensionRegistry::FireRegistryChangeEvent() { // if there is nothing to say, just bail out if (listeners.IsEmpty()) { return; } // for thread safety, create tmp collections QList tmpListeners = listeners.GetListeners(); // do the notification asynchronously //strategy->ScheduleChangeEvent(tmpListeners, tmpDeltas, this); this->ScheduleChangeEvent(tmpListeners, eventDelta); } //RegistryDelta ExtensionRegistry::GetDelta(const QString& namespaze) const //{ // // is there a delta for the plug-in? // RegistryDelta existingDelta = deltas.value(namespaze); // if (existingDelta != null) // return existingDelta; // //if not, create one // RegistryDelta delta = new RegistryDelta(); // deltas.put(namespace, delta); // return delta; //} void ExtensionRegistry::Link(const SmartPointer& extPoint, const QList& extensions) { extPoint->SetRawChildren(extensions); registryObjects->Add(extPoint, true); } //QString ExtensionRegistry::RecordChange(const SmartPointer& extPoint, int extension, int kind) //{ // // avoid computing deltas when there are no listeners // if (listeners.isEmpty()) // return QString(); // ExtensionDelta extensionDelta = new ExtensionDelta(); // extensionDelta.setExtension(extension); // extensionDelta.setExtensionPoint(extPoint.getObjectId()); // extensionDelta.setKind(kind); // getDelta(extPoint.getNamespace()).addExtensionDelta(extensionDelta); // return extPoint.getNamespace(); //} //QString ExtensionRegistry::RecordChange(const SmartPointer& extPoint, const QList& extensions, int kind) //{ // if (listeners.isEmpty()) // return null; // QString namespace = extPoint.getNamespace(); // if (extensions == null || extensions.length == 0) // return namespace; // RegistryDelta pluginDelta = getDelta(extPoint.getNamespace()); // for (int i = 0; i < extensions.length; i++) { // ExtensionDelta extensionDelta = new ExtensionDelta(); // extensionDelta.setExtension(extensions[i]); // extensionDelta.setExtensionPoint(extPoint.getObjectId()); // extensionDelta.setKind(kind); // pluginDelta.addExtensionDelta(extensionDelta); // } // return namespace; //} QString ExtensionRegistry::RemoveExtension(int extensionId) { Extension::Pointer extension = registryObjects->GetObject(extensionId, RegistryObjectManager::EXTENSION).Cast(); registryObjects->RemoveExtensionFromNamespaceIndex(extensionId, extension->GetNamespaceIdentifier()); QString xptName = extension->GetExtensionPointIdentifier(); ExtensionPoint::Pointer extPoint = registryObjects->GetExtensionPointObject(xptName); if (extPoint.IsNull()) { registryObjects->RemoveOrphan(xptName, extensionId); return QString(); } // otherwise, unlink the extension from the extension point QList existingExtensions = extPoint->GetRawChildren(); QList newExtensions; if (existingExtensions.size() > 1) { for (int i = 0; i < existingExtensions.size(); ++i) if (existingExtensions[i] != extension->GetObjectId()) newExtensions.push_back(existingExtensions[i]); } Link(extPoint, newExtensions); if (!eventDelta.IsNull()) eventDelta.RememberExtension(extPoint, extensionId); return extPoint->GetNamespace(); //return recordChange(extPoint, extension.getObjectId(), IExtensionDelta.REMOVED); } QString ExtensionRegistry::RemoveExtensionPoint(int extPoint) { ExtensionPoint::Pointer extensionPoint = registryObjects->GetObject( extPoint, RegistryObjectManager::EXTENSION_POINT).Cast(); registryObjects->RemoveExtensionPointFromNamespaceIndex(extPoint, extensionPoint->GetNamespace()); QList existingExtensions = extensionPoint->GetRawChildren(); if (!existingExtensions.empty()) { registryObjects->AddOrphans(extensionPoint->GetUniqueIdentifier(), existingExtensions); Link(extensionPoint, QList()); } if (!eventDelta.IsNull()) { eventDelta.RememberExtensionPoint(extensionPoint); eventDelta.RememberExtensions(extensionPoint, existingExtensions); } return extensionPoint->GetNamespace(); //return recordChange(extensionPoint, existingExtensions, IExtensionDelta.REMOVED); } QSet ExtensionRegistry::RemoveExtensionsAndExtensionPoints(const QString& contributorId) { QSet affectedNamespaces; QList extensions = registryObjects->GetExtensionsFrom(contributorId); for (int i = 0; i < extensions.size(); i++) { QString namespaze = this->RemoveExtension(extensions[i]); if (!namespaze.isEmpty()) affectedNamespaces.insert(namespaze); } // remove extension points QList extPoints = registryObjects->GetExtensionPointsFrom(contributorId); for (int i = 0; i < extPoints.size(); i++) { QString namespaze = this->RemoveExtensionPoint(extPoints[i]); if (!namespaze.isEmpty()) affectedNamespaces.insert(namespaze); } return affectedNamespaces; } struct ExtensionRegistry::QueueElement { QList listenerInfos; CombinedEventDelta scheduledDelta; QueueElement() { } QueueElement(const QList& infos, const CombinedEventDelta& delta) : listenerInfos(infos), scheduledDelta(delta) { } }; class ExtensionRegistry::RegistryEventThread : public QThread { private: QAtomicInt stop; ExtensionRegistry* registry; Queue& queue; public: RegistryEventThread(ExtensionRegistry* registry, Queue& queue) : stop(0), registry(registry), queue(queue) { this->setObjectName("Extension Registry Event Dispatcher"); } void interrupt() { stop.fetchAndStoreOrdered(1); } void run() override { while (!stop.fetchAndAddOrdered(0)) { QueueElement element; { Queue::Locker l(&queue); while (queue.empty()) queue.wait(); element = queue.takeFirst(); } registry->ProcessChangeEvent(element.listenerInfos, element.scheduledDelta); } } }; bool ExtensionRegistry::CheckReadWriteAccess(QObject* key, bool persist) const { if (masterToken == key) return true; if (userToken == key && !persist) return true; return false; } void ExtensionRegistry::LogError(const QString& owner, const QString& contributionName, const ctkException& e) { QString message = QString("Could not parse XML contribution for \"%1\". Any contributed extensions " "and extension points will be ignored.").arg(QString(owner) + "/" + contributionName); IStatus::Pointer status(new Status(IStatus::ERROR_TYPE, RegistryMessages::OWNER_NAME, 0, message, e, BERRY_STATUS_LOC)); Log(status); } void ExtensionRegistry::CreateExtensionData(const QString& contributorId, const ConfigurationElementDescription& description, const SmartPointer& parent, bool persist) { ConfigurationElement::Pointer currentConfigurationElement = GetElementFactory()->CreateConfigurationElement(persist); currentConfigurationElement->SetContributorId(contributorId); currentConfigurationElement->SetName(description.GetName()); QList descriptionProperties = description.GetAttributes(); QList properties; if (!descriptionProperties.empty()) { for (int i = 0; i < descriptionProperties.size(); i++) { properties.push_back(descriptionProperties[i].GetName()); properties.push_back(Translate(descriptionProperties[i].GetValue(), nullptr)); } } currentConfigurationElement->SetProperties(properties); QString value = description.GetValue(); if (!value.isEmpty()) currentConfigurationElement->SetValue(value); GetObjectManager()->Add(currentConfigurationElement, true); // process children QList children = description.GetChildren(); if (!children.empty()) { for (int i = 0; i < children.size(); i++) { CreateExtensionData(contributorId, children[i], currentConfigurationElement, persist); } } QList newValues = parent->GetRawChildren(); newValues.push_back(currentConfigurationElement->GetObjectId()); parent->SetRawChildren(newValues); currentConfigurationElement->SetParentId(parent->GetObjectId()); currentConfigurationElement->SetParentType(parent.Cast() ? RegistryObjectManager::CONFIGURATION_ELEMENT : RegistryObjectManager::EXTENSION); } bool ExtensionRegistry::RemoveObject(const SmartPointer& registryObject, bool isExtensionPoint, QObject* token) { if (!CheckReadWriteAccess(token, registryObject->ShouldPersist())) throw ctkInvalidArgumentException("Unauthorized access to the ExtensionRegistry.removeExtension() method. Check if proper access token is supplied."); int id = registryObject->GetObjectId(); QWriteLocker l(&access); eventDelta = CombinedEventDelta::RecordRemoval(); if (isExtensionPoint) { RemoveExtensionPoint(id); } else { RemoveExtension(id); } QHash removed; removed.insert(id, registryObject); // There is some asymmetry between extension and extension point removal. Removing extension point makes // extensions "orphans" but does not remove them. As a result, only extensions needs to be processed. if (!isExtensionPoint) { registryObjects->AddAssociatedObjects(removed, registryObject); } registryObjects->RemoveObjects(removed); registryObjects->AddNavigableObjects(removed); IObjectManager::Pointer manager = registryObjects->CreateDelegatingObjectManager(removed); //GetDelta(namespaze)->SetObjectManager(manager); //eventDelta->SetObjectManager(manager); registryObjects->UnlinkChildFromContributions(id); FireRegistryChangeEvent(); eventDelta.Reset(); return true; } void ExtensionRegistry::SetFileManager(const QString& /*cacheBase*/, bool /*isCacheReadOnly*/) { // if (cacheStorageManager != nullptr) // cacheStorageManager->Close(); // close existing file manager first // if (cacheBase != null) { // cacheStorageManager = new StorageManager(cacheBase, isCacheReadOnly ? "none" : null, isCacheReadOnly); //$NON-NLS-1$ // try { // cacheStorageManager.open(!isCacheReadOnly); // } catch (IOException e) { // // Ignore the exception. The registry will be rebuilt from source. // } // } } void ExtensionRegistry::EnterRead() { access.lockForRead(); } void ExtensionRegistry::ExitRead() { access.unlock(); } void ExtensionRegistry::SetElementFactory() { if (isMultiLanguage) { throw ctkRuntimeException("Multi-language registry not supported yet."); //theRegistryObjectFactory = new RegistryObjectFactoryMulti(this); } else { theRegistryObjectFactory.reset(new RegistryObjectFactory(this)); } } //TableReader ExtensionRegistry::getTableReader() const //{ // return theTableReader; //} bool ExtensionRegistry::CheckCache() { // for (int index = 0; index < strategy.getLocationsLength(); index++) { // File possibleCacheLocation = strategy.getStorage(index); // if (possibleCacheLocation == null) // break; // bail out on the first null // setFileManager(possibleCacheLocation, strategy.isCacheReadOnly(index)); // if (cacheStorageManager != null) { // // check this new location: // File cacheFile = null; // try { // cacheFile = cacheStorageManager.lookup(TableReader.getTestFileName(), false); // } catch (IOException e) { // //Ignore the exception. The registry will be rebuilt from the xml files. // } // if (cacheFile != null && cacheFile.isFile()) // return true; // found the appropriate location // } // } return false; } void ExtensionRegistry::StopChangeEventScheduler() { if (!eventThread.isNull()) { Queue::Locker l(&queue); eventThread->interrupt(); eventThread->wait(); eventThread.reset(); } } SmartPointer ExtensionRegistry::GetObjectManager() const { return registryObjects; } void ExtensionRegistry::AddListener(IRegistryEventListener* listener, const QString& extensionPointId) { AddListenerInternal(listener, extensionPointId.isEmpty() ? IExtensionPointFilter(nullptr) : IExtensionPointFilter(new SimpleExtensionPointFilter(extensionPointId))); } void ExtensionRegistry::AddListener(IRegistryEventListener* listener, const IExtensionPointFilter& filter) { this->AddListenerInternal(listener, filter); } QList > ExtensionRegistry::GetConfigurationElementsFor(const QString& extensionPointId) const { // this is just a convenience API - no need to do any sync'ing here int lastdot = extensionPointId.lastIndexOf('.'); if (lastdot == -1) { QList(); } return GetConfigurationElementsFor(extensionPointId.left(lastdot), extensionPointId.mid(lastdot + 1)); } QList > ExtensionRegistry::GetConfigurationElementsFor(const QString& pluginId, const QString& extensionPointSimpleId) const { // this is just a convenience API - no need to do any sync'ing here IExtensionPoint::Pointer extPoint = this->GetExtensionPoint(pluginId, extensionPointSimpleId); if (extPoint.IsNull()) return QList(); return extPoint->GetConfigurationElements(); } QList > ExtensionRegistry::GetConfigurationElementsFor(const QString& pluginId, const QString& extensionPointName, const QString& extensionId) const { // this is just a convenience API - no need to do any sync'ing here IExtension::Pointer extension = this->GetExtension(pluginId, extensionPointName, extensionId); if (extension.IsNull()) return QList(); return extension->GetConfigurationElements(); } SmartPointer ExtensionRegistry::GetExtension(const QString& extensionId) const { if (extensionId.isEmpty()) return IExtension::Pointer(); int lastdot = extensionId.lastIndexOf('.'); if (lastdot == -1) return IExtension::Pointer(); QString namespaze = extensionId.left(lastdot); QList extensions; { QReadLocker l(&access); extensions = registryObjects->GetExtensionsFromNamespace(namespaze); } for (int i = 0; i < extensions.size(); i++) { ExtensionHandle::Pointer suspect = extensions[i]; if (extensionId == suspect->GetUniqueIdentifier()) return suspect; } return IExtension::Pointer(); } SmartPointer ExtensionRegistry::GetExtension(const QString& extensionPointId, const QString& extensionId) const { // this is just a convenience API - no need to do any sync'ing here int lastdot = extensionPointId.lastIndexOf('.'); if (lastdot == -1) return IExtension::Pointer(); return GetExtension(extensionPointId.left(lastdot), extensionPointId.mid(lastdot + 1), extensionId); } SmartPointer ExtensionRegistry::GetExtension(const QString& pluginId, const QString& extensionPointName, const QString& extensionId) const { // this is just a convenience API - no need to do any sync'ing here IExtensionPoint::Pointer extPoint = GetExtensionPoint(pluginId, extensionPointName); if (extPoint.IsNotNull()) return extPoint->GetExtension(extensionId); return IExtension::Pointer(); } SmartPointer ExtensionRegistry::GetExtensionPoint(const QString& xptUniqueId) const { QReadLocker l(&access); return registryObjects->GetExtensionPointHandle(xptUniqueId); } SmartPointer ExtensionRegistry::GetExtensionPoint(const QString& elementName, const QString& xpt) const { QReadLocker l(&access); return registryObjects->GetExtensionPointHandle(elementName + '.' + xpt); } QList > ExtensionRegistry::GetExtensionPoints() const { QList handles; { QReadLocker l(&access); handles = registryObjects->GetExtensionPointsHandles(); } QList result; foreach(ExtensionPointHandle::Pointer handle, handles) { result.push_back(handle); } return result; } QList > ExtensionRegistry::GetExtensionPoints(const QString& namespaceName) const { QList handles; { QReadLocker l(&access); handles = registryObjects->GetExtensionPointsFromNamespace(namespaceName); } QList result; foreach(ExtensionPointHandle::Pointer handle, handles) { result.push_back(handle); } return result; } QList > ExtensionRegistry::GetExtensions(const QString& namespaceName) const { QList handles; { QReadLocker l(&access); handles = registryObjects->GetExtensionsFromNamespace(namespaceName); } QList result; foreach (ExtensionHandle::Pointer handle, handles) { result.push_back(handle); } return result; } QList > ExtensionRegistry::GetExtensions(const SmartPointer& contributor) const { RegistryContributor::Pointer regContributor = contributor.Cast(); if (regContributor.IsNull()) throw ctkInvalidArgumentException("Contributor must be a RegistryContributor."); // should never happen QString contributorId = regContributor->GetActualId(); QList handles; { QReadLocker l(&access); handles = registryObjects->GetExtensionsFromContributor(contributorId); } QList result; foreach (ExtensionHandle::Pointer handle, handles) { result.push_back(handle); } return result; } QList > ExtensionRegistry::GetExtensionPoints(const SmartPointer& contributor) const { RegistryContributor::Pointer regContributor = contributor.Cast(); if (regContributor.IsNull()) throw ctkInvalidArgumentException("Contributor must be a RegistryContributor."); // should never happen QString contributorId = regContributor->GetActualId(); QList handles; { QReadLocker l(&access); handles = registryObjects->GetExtensionPointsFromContributor(contributorId); } QList result; foreach (ExtensionPointHandle::Pointer handle, handles) { result.push_back(handle); } return result; } QList ExtensionRegistry::GetNamespaces() const { QReadLocker l(&access); QList namespaceElements = registryObjects->GetNamespacesIndex().Elements(); QList namespaceNames; for (int i = 0; i < namespaceElements.size(); i++) { namespaceNames.push_back(namespaceElements[i]->GetKey()); } return namespaceNames; } bool ExtensionRegistry::HasContributor(const SmartPointer& contributor) const { RegistryContributor::Pointer regContributor = contributor.Cast(); if (regContributor.IsNull()) throw ctkInvalidArgumentException("Contributor must be a RegistryContributor."); // should never happen QString contributorId = regContributor->GetActualId(); return HasContributor(contributorId); } bool ExtensionRegistry::HasContributor(const QString& contributorId) const { QReadLocker l(&access); return registryObjects->HasContribution(contributorId); } void ExtensionRegistry::Remove(const QString& removedContributorId, long timestamp) { Remove(removedContributorId); if (timestamp != 0) aggregatedTimestamp.Remove(timestamp); } void ExtensionRegistry::RemoveContributor(const SmartPointer& contributor, QObject* key) { RegistryContributor::Pointer regContributor = contributor.Cast(); if (regContributor.IsNull()) throw ctkInvalidArgumentException("Contributor must be a RegistryContributor."); // should never happen if (!CheckReadWriteAccess(key, true)) throw ctkInvalidArgumentException("Unauthorized access to the ExtensionRegistry.removeContributor() method. Check if proper access token is supplied."); QString contributorId = regContributor->GetActualId(); Remove(contributorId); } void ExtensionRegistry::Remove(const QString& removedContributorId) { QWriteLocker l(&access); eventDelta = CombinedEventDelta::RecordRemoval(); BasicRemove(removedContributorId); FireRegistryChangeEvent(); eventDelta.Reset(); } void ExtensionRegistry::RemoveListener(IRegistryEventListener* listener) { listeners.Remove(ListenerInfo(listener, IExtensionPointFilter(nullptr))); } ExtensionRegistry::ExtensionRegistry(RegistryStrategy* registryStrategy, QObject* masterToken, QObject* userToken) : registryObjects(nullptr), isMultiLanguage(false), mlErrorLogged(false), eventThread(nullptr) { isMultiLanguage = RegistryProperties::GetProperty(RegistryConstants::PROP_REGISTRY_MULTI_LANGUAGE) == "true"; if (registryStrategy != nullptr) strategy.reset(registryStrategy); else strategy.reset(new RegistryStrategy(QList(), QList(), nullptr)); this->masterToken = masterToken; this->userToken = userToken; registryObjects = new RegistryObjectManager(this); bool isRegistryFilledFromCache = false; // indicates if registry was able to use cache to populate it's content if (strategy->CacheUse()) { // Try to read the registry from the cache first. If that fails, create a new registry QElapsedTimer timer; if (Debug()) timer.start(); //The cache is made of several files, find the real names of these other files. If all files are found, try to initialize the objectManager if (CheckCache()) { // TODO Registry Cache // try { // theTableReader.setTableFile(cacheStorageManager.lookup(TableReader.TABLE, false)); // theTableReader.setExtraDataFile(cacheStorageManager.lookup(TableReader.EXTRA, false)); // theTableReader.setMainDataFile(cacheStorageManager.lookup(TableReader.MAIN, false)); // theTableReader.setContributionsFile(cacheStorageManager.lookup(TableReader.CONTRIBUTIONS, false)); // theTableReader.setContributorsFile(cacheStorageManager.lookup(TableReader.CONTRIBUTORS, false)); // theTableReader.setNamespacesFile(cacheStorageManager.lookup(TableReader.NAMESPACES, false)); // theTableReader.setOrphansFile(cacheStorageManager.lookup(TableReader.ORPHANS, false)); // long timestamp = strategy.getContributionsTimestamp(); // isRegistryFilledFromCache = registryObjects.init(timestamp); // if (isRegistryFilledFromCache) // aggregatedTimestamp.set(timestamp); // } catch (IOException e) { // // The registry will be rebuilt from the xml files. Make sure to clear anything filled // // from cache so that we won't have partially filled items. // isRegistryFilledFromCache = false; // clearRegistryCache(); // log(new Status(IStatus.ERROR, RegistryMessages.OWNER_NAME, 0, RegistryMessages.registry_bad_cache, e)); // } } // if (!isRegistryFilledFromCache) // { // // set cache storage manager to a first writable location // for (int index = 0; index < strategy.getLocationsLength(); index++) { // if (!strategy.isCacheReadOnly(index)) { // setFileManager(strategy.getStorage(index), false); // break; // } // } // } if (Debug() && isRegistryFilledFromCache) BERRY_INFO << "Reading registry cache: " << timer.elapsed() << "ms"; if (Debug()) { if (!isRegistryFilledFromCache) BERRY_INFO << "Reloading registry from manifest files..."; else BERRY_INFO << "Using registry cache..."; } } if (DebugEvents()) { struct DebugRegistryListener : public IRegistryEventListener { void Added(const QList& extensions) override { BERRY_INFO << "Registry extensions ADDED:"; foreach(IExtension::Pointer extension, extensions) { BERRY_INFO << "\t" << extension->GetExtensionPointUniqueIdentifier() << " - " << extension->GetNamespaceIdentifier() << "." << extension->GetSimpleIdentifier(); } } void Removed(const QList& extensions) override { BERRY_INFO << "Registry extensions REMOVED:"; foreach(IExtension::Pointer extension, extensions) { BERRY_INFO << "\t" << extension->GetExtensionPointUniqueIdentifier() << " - " << extension->GetNamespaceIdentifier() << "." << extension->GetSimpleIdentifier(); } } void Added(const QList& extensionPoints) override { BERRY_INFO << "Registry extension-points ADDED:"; foreach(IExtensionPoint::Pointer extensionPoint, extensionPoints) { BERRY_INFO << "\t" << extensionPoint->GetUniqueIdentifier(); } } void Removed(const QList& extensionPoints) override { BERRY_INFO << "Registry extension-points REMOVED:"; foreach(IExtensionPoint::Pointer extensionPoint, extensionPoints) { BERRY_INFO << "\t" << extensionPoint->GetUniqueIdentifier(); } } }; debugRegistryListener.reset(new DebugRegistryListener()); AddListener(debugRegistryListener.data()); } // Do extra start processing if specified in the registry strategy strategy->OnStart(this, isRegistryFilledFromCache); } ExtensionRegistry::~ExtensionRegistry() { } void ExtensionRegistry::Stop(QObject* /*key*/) { - // If the registry creator specified a key token, check that the key mathches it + // If the registry creator specified a key token, check that the key matches it // (it is assumed that registry owner keeps the key to prevent unautorized access). if (masterToken != nullptr && masterToken != nullptr) { throw ctkInvalidArgumentException("Unauthorized access to the ExtensionRegistry.stop() method. Check if proper access token is supplied."); //$NON-NLS-1$ } // Do extra stop processing if specified in the registry strategy strategy->OnStop(this); StopChangeEventScheduler(); // if (cacheStorageManager == nullptr) // return; // if (!registryObjects.isDirty() || cacheStorageManager.isReadOnly()) { // cacheStorageManager.close(); // theTableReader.close(); // return; // } // File tableFile = null; // File mainFile = null; // File extraFile = null; // File contributionsFile = null; // File contributorsFile = null; // File namespacesFile = null; // File orphansFile = null; // TableWriter theTableWriter = new TableWriter(this); // try { // cacheStorageManager.lookup(TableReader.TABLE, true); // cacheStorageManager.lookup(TableReader.MAIN, true); // cacheStorageManager.lookup(TableReader.EXTRA, true); // cacheStorageManager.lookup(TableReader.CONTRIBUTIONS, true); // cacheStorageManager.lookup(TableReader.CONTRIBUTORS, true); // cacheStorageManager.lookup(TableReader.NAMESPACES, true); // cacheStorageManager.lookup(TableReader.ORPHANS, true); // tableFile = File.createTempFile(TableReader.TABLE, ".new", cacheStorageManager.getBase()); //$NON-NLS-1$ // mainFile = File.createTempFile(TableReader.MAIN, ".new", cacheStorageManager.getBase()); //$NON-NLS-1$ // extraFile = File.createTempFile(TableReader.EXTRA, ".new", cacheStorageManager.getBase()); //$NON-NLS-1$ // contributionsFile = File.createTempFile(TableReader.CONTRIBUTIONS, ".new", cacheStorageManager.getBase()); //$NON-NLS-1$ // contributorsFile = File.createTempFile(TableReader.CONTRIBUTORS, ".new", cacheStorageManager.getBase()); //$NON-NLS-1$ // namespacesFile = File.createTempFile(TableReader.NAMESPACES, ".new", cacheStorageManager.getBase()); //$NON-NLS-1$ // orphansFile = File.createTempFile(TableReader.ORPHANS, ".new", cacheStorageManager.getBase()); //$NON-NLS-1$ // theTableWriter.setTableFile(tableFile); // theTableWriter.setExtraDataFile(extraFile); // theTableWriter.setMainDataFile(mainFile); // theTableWriter.setContributionsFile(contributionsFile); // theTableWriter.setContributorsFile(contributorsFile); // theTableWriter.setNamespacesFile(namespacesFile); // theTableWriter.setOrphansFile(orphansFile); // } catch (IOException e) { // cacheStorageManager.close(); // return; //Ignore the exception since we can recompute the cache // } // try { // long timestamp; // // A bit of backward compatibility: if registry was modified, but timestamp was not, // // it means that the new timestamp tracking mechanism was not used. In this case // // explicitly obtain timestamps for all contributions. Note that this logic // // maintains a problem described in the bug 104267 for contributions that // // don't use the timestamp tracking mechanism. // if (aggregatedTimestamp.isModifed()) // timestamp = aggregatedTimestamp.getContentsTimestamp(); // use timestamp tracking // else // timestamp = strategy.getContributionsTimestamp(); // use legacy approach // if (theTableWriter.saveCache(registryObjects, timestamp)) // cacheStorageManager.update(new QString[] {TableReader.TABLE, TableReader.MAIN, TableReader.EXTRA, TableReader.CONTRIBUTIONS, TableReader.CONTRIBUTORS, TableReader.NAMESPACES, TableReader.ORPHANS}, new QString[] {tableFile.getName(), mainFile.getName(), extraFile.getName(), contributionsFile.getName(), contributorsFile.getName(), namespacesFile.getName(), orphansFile.getName()}); // } catch (IOException e) { // //Ignore the exception since we can recompute the cache // } // theTableReader.close(); // cacheStorageManager.close(); } void ExtensionRegistry::ClearRegistryCache() { // QString[] keys = new QString[] {TableReader.TABLE, TableReader.MAIN, TableReader.EXTRA, TableReader.CONTRIBUTIONS, TableReader.ORPHANS}; // for (int i = 0; i < keys.length; i++) // try { // cacheStorageManager.remove(keys[i]); // } catch (IOException e) { // log(new Status(IStatus.ERROR, RegistryMessages.OWNER_NAME, IStatus.ERROR, RegistryMessages.meta_registryCacheReadProblems, e)); // } aggregatedTimestamp.Reset(); } RegistryObjectFactory* ExtensionRegistry::GetElementFactory() { if (theRegistryObjectFactory.isNull()) SetElementFactory(); return theRegistryObjectFactory.data(); } void ExtensionRegistry::Log(const SmartPointer& status) const { strategy->Log(status); } QString ExtensionRegistry::Translate(const QString& key, QTranslator* resources) const { if (isMultiLanguage) return key; return strategy->Translate(key, resources); } bool ExtensionRegistry::Debug() const { return strategy->Debug(); } bool ExtensionRegistry::DebugEvents() const { return strategy->DebugRegistryEvents(); } bool ExtensionRegistry::UseLazyCacheLoading() const { return strategy->CacheLazyLoading(); } long ExtensionRegistry::ComputeState() const { return strategy->GetContainerTimestamp(); } QObject* ExtensionRegistry::CreateExecutableExtension(const SmartPointer& defaultContributor, const QString& className, const QString& requestedContributorName) { return strategy->CreateExecutableExtension(defaultContributor, className, requestedContributorName); } void ExtensionRegistry::ProcessChangeEvent( const QList& listenerInfos, const CombinedEventDelta& scheduledDelta) { for (int i = 0; i < listenerInfos.size(); i++) { const ListenerInfo& listenerInfo = listenerInfos[i]; IRegistryEventListener* extensionListener = listenerInfo.listener; QList extensions = scheduledDelta.GetExtensions(listenerInfo.filter); QList extensionPoints = scheduledDelta.GetExtensionPoints(listenerInfo.filter); // notification order - on addition: extension points; then extensions if (scheduledDelta.IsAddition()) { if (!extensionPoints.empty()) extensionListener->Added(extensionPoints); if (!extensions.empty()) extensionListener->Added(extensions); } else { // on removal: extensions; then extension points if (!extensions.empty()) extensionListener->Removed(extensions); if (!extensionPoints.empty()) extensionListener->Removed(extensionPoints); } } IObjectManager::Pointer manager = scheduledDelta.GetObjectManager(); if (manager.IsNotNull()) manager->Close(); } void ExtensionRegistry::ScheduleChangeEvent(const QList& listenerInfos, const CombinedEventDelta& scheduledDelta) { QueueElement newElement(listenerInfos, scheduledDelta); if (eventThread.isNull()) { eventThread.reset(new RegistryEventThread(this, queue)); eventThread->start(); } { Queue::Locker l(&queue); queue.push_back(newElement); queue.notify(); } } bool ExtensionRegistry::AddContribution(QIODevice* is, const SmartPointer& contributor, bool persist, const QString& contributionName, QTranslator* translationBundle, QObject* key, long timestamp) { bool result = AddContribution(is, contributor, persist, contributionName, translationBundle, key); if (timestamp != 0) aggregatedTimestamp.Add(timestamp); return result; } bool ExtensionRegistry::AddContribution(QIODevice* is, const SmartPointer& contributor, bool persist, const QString& contributionName, QTranslator* translationBundle, QObject* key) { if (!CheckReadWriteAccess(key, persist)) throw ctkInvalidArgumentException("Unauthorized access to the ExtensionRegistry::AddContribution() method. Check if proper access token is supplied."); RegistryContributor::Pointer internalContributor = contributor.Cast(); registryObjects->AddContributor(internalContributor); // only adds a contributor if it is not already present QString ownerName = internalContributor->GetActualName(); QString message = QString("Problems parsing plug-in manifest for: \"%1\".").arg(ownerName); MultiStatus::Pointer problems(new MultiStatus(RegistryMessages::OWNER_NAME, ExtensionsParser::PARSE_PROBLEM, message, BERRY_STATUS_LOC)); ExtensionsParser parser(problems, this); RegistryContribution::Pointer contribution = GetElementFactory()->CreateContribution(internalContributor->GetActualId(), persist); try { QXmlInputSource xmlInput(is); bool success = parser.parseManifest(strategy->GetXMLParser(), &xmlInput, contributionName, GetObjectManager().GetPointer(), contribution, translationBundle); int status = problems->GetSeverity(); if (status != IStatus::OK_TYPE || !success) { Log(problems); if (status == IStatus::ERROR_TYPE || status == IStatus::CANCEL_TYPE || !success) return false; } } catch (const ctkException& e) { LogError(ownerName, contributionName, e); return false; } Add(contribution); // the add() method does synchronization return true; } bool ExtensionRegistry::AddExtensionPoint(const QString& identifier, const SmartPointer& contributor, bool persist, const QString& label, const QString& schemaReference, QObject* token) { if (!CheckReadWriteAccess(token, persist)) throw ctkInvalidArgumentException("Unauthorized access to the ExtensionRegistry::AddExtensionPoint() method. Check if proper access token is supplied."); RegistryContributor::Pointer internalContributor = contributor.Cast(); registryObjects->AddContributor(internalContributor); // only adds a contributor if it is not already present QString contributorId = internalContributor->GetActualId(); // Extension point Id might not be null if (identifier.isEmpty()) { QString message = QString("Missing ID for the extension point \"%1\". Element ignored.").arg(label); IStatus::Pointer status(new Status(IStatus::ERROR_TYPE, RegistryMessages::OWNER_NAME, 0, message, BERRY_STATUS_LOC)); Log(status); } // addition wraps in a contribution RegistryContribution::Pointer contribution = GetElementFactory()->CreateContribution(contributorId, persist); ExtensionPoint::Pointer currentExtPoint = GetElementFactory()->CreateExtensionPoint(persist); QString uniqueId; QString namespaceName; int simpleIdStart = identifier.lastIndexOf('.'); if (simpleIdStart == -1) { namespaceName = contribution->GetDefaultNamespace(); uniqueId = namespaceName + '.' + identifier; } else { namespaceName = identifier.left(simpleIdStart); uniqueId = identifier; } currentExtPoint->SetUniqueIdentifier(uniqueId); currentExtPoint->SetNamespace(namespaceName); QString labelNLS = Translate(label, nullptr); currentExtPoint->SetLabel(labelNLS); currentExtPoint->SetSchema(schemaReference); if (!GetObjectManager()->AddExtensionPoint(currentExtPoint, true)) { if (Debug()) { QString msg = QString("Ignored duplicate extension point \"%1\" supplied by \"%2\".").arg(uniqueId).arg(contribution->GetDefaultNamespace()); IStatus::Pointer status(new Status(IStatus::ERROR_TYPE, RegistryMessages::OWNER_NAME, 0, msg, BERRY_STATUS_LOC)); Log(status); } return false; } currentExtPoint->SetContributorId(contributorId); // array format: {Number of extension points, Number of extensions, Extension Id} QList contributionChildren; // Put the extension points into this namespace contributionChildren.push_back(1); contributionChildren.push_back(0); contributionChildren.push_back(currentExtPoint->GetObjectId()); contribution->SetRawChildren(contributionChildren); Add(contribution); return true; } bool ExtensionRegistry::AddExtension(const QString& identifier, const SmartPointer& contributor, bool persist, const QString& label, const QString& extensionPointId, const ConfigurationElementDescription& configurationElements, QObject* token) { if (!CheckReadWriteAccess(token, persist)) throw ctkInvalidArgumentException("Unauthorized access to the ExtensionRegistry::AddExtensionPoint() method. Check if proper access token is supplied."); // prepare namespace information RegistryContributor::Pointer internalContributor = contributor.Cast(); registryObjects->AddContributor(internalContributor); // only adds a contributor if it is not already present QString contributorId = internalContributor->GetActualId(); // addition wraps in a contribution RegistryContribution::Pointer contribution = GetElementFactory()->CreateContribution(contributorId, persist); Extension::Pointer currentExtension = GetElementFactory()->CreateExtension(persist); QString simpleId; QString namespaceName; int simpleIdStart = identifier.lastIndexOf('.'); if (simpleIdStart != -1) { simpleId = identifier.mid(simpleIdStart + 1); namespaceName = identifier.left(simpleIdStart); } else { simpleId = identifier; namespaceName = contribution->GetDefaultNamespace(); } currentExtension->SetSimpleIdentifier(simpleId); currentExtension->SetNamespaceIdentifier(namespaceName); QString extensionLabelNLS = Translate(label, nullptr); currentExtension->SetLabel(extensionLabelNLS); QString targetExtensionPointId; if (extensionPointId.indexOf('.') == -1) // No dots -> namespace name added at the start targetExtensionPointId = contribution->GetDefaultNamespace() + '.' + extensionPointId; else targetExtensionPointId = extensionPointId; currentExtension->SetExtensionPointIdentifier(targetExtensionPointId); // if we have an Id specified, check for duplicates. Only issue warning if duplicate found // as it might still work fine - depending on the access pattern. if (!simpleId.isNull() && Debug()) { QString uniqueId = namespaceName + '.' + simpleId; IExtension::Pointer existingExtension = GetExtension(uniqueId); if (existingExtension.IsNotNull()) { QString currentSupplier = contribution->GetDefaultNamespace(); QString existingSupplier = existingExtension->GetContributor()->GetName(); QString msg = QString("Extensions supplied by \"%1\" and \"%2\" have the same Id: \"%3\".") .arg(currentSupplier).arg(existingSupplier).arg(uniqueId); IStatus::Pointer status(new Status(IStatus::WARNING_TYPE, RegistryMessages::OWNER_NAME, 0, msg, BERRY_STATUS_LOC)); Log(status); return false; } } GetObjectManager()->Add(currentExtension, true); CreateExtensionData(contributorId, configurationElements, currentExtension, persist); currentExtension->SetContributorId(contributorId); QList contributionChildren; contributionChildren.push_back(0); contributionChildren.push_back(1); contributionChildren.push_back(currentExtension->GetObjectId()); contribution->SetRawChildren(contributionChildren); Add(contribution); return true; } bool ExtensionRegistry::RemoveExtension(const SmartPointer& extension, QObject* token) { ExtensionHandle::Pointer handle = extension.Cast(); if (handle.IsNull()) return false; return RemoveObject(handle->GetObject(), false, token); } bool ExtensionRegistry::RemoveExtensionPoint(const SmartPointer& extensionPoint, QObject* token) { ExtensionPointHandle::Pointer handle = extensionPoint.Cast(); if (handle.IsNull()) return false; return RemoveObject(handle->GetObject(), true, token); } QList > ExtensionRegistry::GetAllContributors() const { QList result; QReadLocker l(&access); foreach(RegistryContributor::Pointer contributor, registryObjects->GetContributors().values()) { result.push_back(contributor); } return result; } bool ExtensionRegistry::IsMultiLanguage() const { return isMultiLanguage; } QList ExtensionRegistry::Translate(const QList& nonTranslated, const SmartPointer& contributor, const QLocale& locale) const { return strategy->Translate(nonTranslated, contributor, locale); } QLocale ExtensionRegistry::GetLocale() const { return strategy->GetLocale(); } void ExtensionRegistry::LogMultiLangError() const { if (mlErrorLogged) // only log this error ones return; IStatus::Pointer status(new Status(IStatus::ERROR_TYPE, RegistryMessages::OWNER_NAME, 0, QString("The requested multi-language operation is not enabled. See runtime option \"") + RegistryConstants::PROP_REGISTRY_MULTI_LANGUAGE + "\".", ctkInvalidArgumentException(""), BERRY_STATUS_LOC)); Log(status); mlErrorLogged = true; } } diff --git a/Plugins/org.blueberry.ui.qt.help/src/internal/berryQHelpEngineWrapper.cpp b/Plugins/org.blueberry.ui.qt.help/src/internal/berryQHelpEngineWrapper.cpp index 45ca5566a3..8b0ee076a7 100644 --- a/Plugins/org.blueberry.ui.qt.help/src/internal/berryQHelpEngineWrapper.cpp +++ b/Plugins/org.blueberry.ui.qt.help/src/internal/berryQHelpEngineWrapper.cpp @@ -1,60 +1,60 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "berryQHelpEngineWrapper.h" #include namespace berry { QHelpEngineWrapper::QHelpEngineWrapper(const QString &collectionFile) : QHelpEngine(collectionFile) { /* * Otherwise we will waste time if several new docs are found, * because we will start to index them, only to be interrupted * by the next request. Also, there is a nasty SQLITE bug that will * cause the application to hang for minutes in that case. - * This call is reverted by initalDocSetupDone(), which must be + * This call is reverted by initialDocSetupDone(), which must be * called after the new docs have been installed. */ disconnect(this, SIGNAL(setupFinished()), searchEngine(), SLOT(indexDocumentation())); } QHelpEngineWrapper::~QHelpEngineWrapper() { } void QHelpEngineWrapper::initialDocSetupDone() { connect(this, SIGNAL(setupFinished()), searchEngine(), SLOT(indexDocumentation())); setupData(); } const QString QHelpEngineWrapper::homePage() const { return m_HomePage; } void QHelpEngineWrapper::setHomePage(const QString &page) { if (m_HomePage != page) { m_HomePage = page; emit homePageChanged(page); } } } // end namespace berry diff --git a/Plugins/org.blueberry.ui.qt/src/internal/berryParameterValueConverterProxy.h b/Plugins/org.blueberry.ui.qt/src/internal/berryParameterValueConverterProxy.h index 843c223234..c3e33981af 100644 --- a/Plugins/org.blueberry.ui.qt/src/internal/berryParameterValueConverterProxy.h +++ b/Plugins/org.blueberry.ui.qt/src/internal/berryParameterValueConverterProxy.h @@ -1,78 +1,78 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef BERRYPARAMETERVALUECONVERTERPROXY_H #define BERRYPARAMETERVALUECONVERTERPROXY_H #include "berryIParameterValueConverter.h" #include namespace berry { struct IConfigurationElement; /** - * A proxy for a parameter value converter that has been defined in the regisry. + * A proxy for a parameter value converter that has been defined in the registry. * This delays the class loading until the converter is really asked to do * string/object conversions. */ class ParameterValueConverterProxy : public IParameterValueConverter { private: /** * The configuration element providing the executable extension that will * extend AbstractParameterValueConverter. This value will * not be null. */ const SmartPointer converterConfigurationElement; /** * The real parameter value converter instance. This will be * null until one of the conversion methods are used. */ mutable QSharedPointer parameterValueConverter; /** * Returns the real parameter value converter for this proxy or throws an * exception indicating the converter could not be obtained. * * @return the real converter for this proxy; never null. * @throws ParameterValueConversionException * if the converter could not be obtained */ IParameterValueConverter* GetConverter() const; public: /** * Constructs a ParameterValueConverterProxy to represent the * real converter until it is needed. * * @param converterConfigurationElement * The configuration element from which the real converter can be * loaded. */ ParameterValueConverterProxy( const SmartPointer& converterConfigurationElement); Object::Pointer ConvertToObject(const QString& parameterValue) override; QString ConvertToString(const Object::Pointer& parameterValue) override; }; } #endif // BERRYPARAMETERVALUECONVERTERPROXY_H diff --git a/Plugins/org.blueberry.ui.qt/src/internal/berryRegistryReader.h b/Plugins/org.blueberry.ui.qt/src/internal/berryRegistryReader.h index 205579322d..991ffdb7e2 100644 --- a/Plugins/org.blueberry.ui.qt/src/internal/berryRegistryReader.h +++ b/Plugins/org.blueberry.ui.qt/src/internal/berryRegistryReader.h @@ -1,165 +1,165 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef BERRYREGISTRYREADER_H_ #define BERRYREGISTRYREADER_H_ #include #include namespace berry { struct IExtension; struct IExtensionRegistry; struct IConfigurationElement; /** * \ingroup org_blueberry_ui_internal * * Template implementation of a registry reader that creates objects * representing registry contents. Typically, an extension * contains one element, but this reader handles multiple * elements per extension. * * To start reading the extensions from the registry for an * extension point, call the method readRegistry. * * To read children of an IConfigurationElement, call the * method readElementChildren from your implementation * of the method readElement, as it will not be * done by default. */ class RegistryReader { // for dynamic UI - remove this cache to avoid inconsistency //protected static Hashtable extensionPoints = new Hashtable(); /** * The constructor. */ protected: RegistryReader(); virtual ~RegistryReader(); /** * Logs the error in the workbench log using the provided * text and the information in the configuration element. */ static void LogError(const SmartPointer& element, const QString& text); /** * Logs a very common registry error when a required attribute is missing. */ static void LogMissingAttribute(const SmartPointer& element, const QString& attributeName); /** * Logs a very common registry error when a required child is missing. */ static void LogMissingElement(const SmartPointer& element, const QString& elementName); /** * Logs a registry error when the configuration element is unknown. */ static void LogUnknownElement(const SmartPointer& element); public: /** * Apply a reproducible order to the list of extensions * provided, such that the order will not change as * extensions are added or removed. * @param extensions the extensions to order * @return ordered extensions */ static QList > OrderExtensions(const QList >& extensions); protected: /** * Implement this method to read element's attributes. * If children should also be read, then implementor * is responsible for calling readElementChildren. * Implementor is also responsible for logging missing * attributes. * * @return true if element was recognized, false if not. */ virtual bool ReadElement(const SmartPointer& element) = 0; /** * Read the element's children. This is called by * the subclass' readElement method when it wants * to read the children of the element. */ virtual void ReadElementChildren(const SmartPointer& element); /** * Read each element one at a time by calling the * subclass implementation of readElement. * * Logs an error if the element was not recognized. */ virtual void ReadElements(const QList >& elements); /** * Read one extension by looping through its * configuration elements. */ virtual void ReadExtension(const SmartPointer& extension); public: /** * Start the registry reading process using the * supplied plugin ID and extension point. * * @param registry the registry to read from - * @param pluginId the plugin id of the extenion point + * @param pluginId the plugin id of the extension point * @param extensionPoint the extension point id */ virtual void ReadRegistry(IExtensionRegistry* registry, const QString& pluginId, const QString& extensionPoint); /** * Utility for extracting the description child of an element. * * @param configElement the element * @return the description */ static QString GetDescription(const SmartPointer& configElement); /** * Utility for extracting the value of a class attribute or a nested class * element that follows the pattern set forth by * {@link org.blueberry.core.runtime.IExecutableExtension}. * * @param configElement * the element * @param classAttributeName * the name of the class attribute to check * @return the value of the attribute or nested class element */ static QString GetClassValue(const SmartPointer& configElement, const QString& classAttributeName); }; } #endif /*BERRYREGISTRYREADER_H_*/ diff --git a/Plugins/org.blueberry.ui.qt/src/internal/berryWorkbench.cpp b/Plugins/org.blueberry.ui.qt/src/internal/berryWorkbench.cpp index 2118c1b969..6429efefaa 100644 --- a/Plugins/org.blueberry.ui.qt/src/internal/berryWorkbench.cpp +++ b/Plugins/org.blueberry.ui.qt/src/internal/berryWorkbench.cpp @@ -1,1976 +1,1976 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "berryLog.h" #include "tweaklets/berryWorkbenchTweaklet.h" #include "berryWorkbench.h" #include #include "berryPolicy.h" #include "berrySaveablesList.h" #include "berryViewRegistry.h" #include "berryEditorRegistry.h" #include "berryEditorHistory.h" #include "berryEditorHistoryItem.h" #include "berryServiceLocatorCreator.h" #include "berryWorkbenchPage.h" #include "berryPerspective.h" #include "berryPreferenceConstants.h" #include "berryUIExtensionTracker.h" #include "berryWorkbenchWindow.h" #include "berryDisplay.h" #include "services/berryIServiceFactory.h" #include "util/berrySafeRunnable.h" #include "berryWorkbenchServiceRegistry.h" #include "berryWorkbenchPlugin.h" #include "berryWorkbenchConstants.h" #include "berryWorkbenchMenuService.h" #include "berryEvaluationService.h" #include "berryCommandService.h" #include "berryCommandManager.h" #include "berryMenuManager.h" #include "berryParameterType.h" #include "berryQActionProperties.h" #include "berrySourceProviderService.h" #include "berryWorkbenchLocationService.h" #include #include #include #include #include #include #include #include #include #include #include #include #include namespace berry { Workbench* Workbench::instance = nullptr; WorkbenchTestable::Pointer Workbench::testableObject; const unsigned int Workbench::VERSION_STRING_COUNT = 1; const QString Workbench::VERSION_STRING[Workbench::VERSION_STRING_COUNT] = { "1.0" }; const QString Workbench::DEFAULT_WORKBENCH_STATE_FILENAME = "workbench.xml"; class RestoreStateRunnable: public SafeRunnable { private: Workbench* workbench; Poco::File stateFile; bool& result; public: RestoreStateRunnable(Workbench* workbench, const QString& stateFile, bool& result) : SafeRunnable( "Unable to read workbench state. Workbench UI layout will be reset."), workbench(workbench), stateFile(stateFile.toStdString()), result(result) { } void Run() override { Poco::FileInputStream input(stateFile.path()); IMemento::Pointer memento = XMLMemento::CreateReadRoot(input); // Validate known version format QString version; memento->GetString(WorkbenchConstants::TAG_VERSION, version); bool valid = false; for (auto & elem : Workbench::VERSION_STRING) { if (elem == version) { valid = true; break; } } if (!valid) { input.close(); QString msg = "Invalid workbench state version. workbench.xml will be deleted"; QMessageBox::critical(nullptr, "Restoring Problems", msg); stateFile.remove(); // result[0] = new Status(IStatus.ERROR, // WorkbenchPlugin.PI_WORKBENCH, // IWorkbenchConfigurer.RESTORE_CODE_RESET, msg, null); result = false; return; } // // Validate compatible version format // // We no longer support the release 1.0 format // if (VERSION_STRING[0].equals(version)) // { // reader.close(); // QString msg = "The saved user interface layout is in an " // "obsolete format and cannot be preserved. Your projects and files " // "will not be affected. Press OK to convert to the new format. Press " // "Cancel to exit with no changes."; // QList dlgLabels; // dlgLabels.push_back("Ok"); // dlgLabels.push_back("Cancel"); // IDialog::Pointer dlg = MessageDialog::CreateDialog(Shell::Pointer(0), // "Cannot Preserve Layout", 0, msg, IDialog::WARNING, dlgLabels, 0); // IDialog::ReturnCode ignoreSavedState = dlg->Open(); // // OK is the default // if (ignoreSavedState == IDialog::OK) // { // stateFile.remove(); // // result[0] = new Status(IStatus.WARNING, // // WorkbenchPlugin.PI_WORKBENCH, // // IWorkbenchConfigurer.RESTORE_CODE_RESET, msg, // // null); // result = false; // } // else // { // // result[0] = new Status(IStatus.WARNING, // // WorkbenchPlugin.PI_WORKBENCH, // // IWorkbenchConfigurer.RESTORE_CODE_EXIT, msg, // // null); // result = false; // } // return; // } // Restore the saved state //final IStatus restoreResult = restoreState(memento); /*bool restoreResult =*/ workbench->RestoreState(memento); input.close(); // if (restoreResult.getSeverity() == IStatus.ERROR) { // StartupThreading // .runWithoutExceptions(new StartupRunnable() { // // public void runWithException() throws Throwable { // StatusManager.getManager().handle(restoreResult, StatusManager.LOG); // } // }); // // } } void HandleException(const ctkException& e) override { //StartupThreading.runWithoutExceptions(new StartupRunnable() { //public void runWithException() { Handle(e); // QString msg = e.getMessage() == null ? "" : e.getMessage(); //$NON-NLS-1$ // result[0] = new Status(IStatus.ERROR, // WorkbenchPlugin.PI_WORKBENCH, // IWorkbenchConfigurer.RESTORE_CODE_RESET, msg, e); result = false; stateFile.remove(); // }}); } private: void Handle(const ctkException& e) { SafeRunnable::HandleException(e); } }; int Workbench::CreateAndRunWorkbench(Display* display, WorkbenchAdvisor* advisor) { // create the workbench instance Workbench workbench(display, advisor); // run the workbench event loop int returnCode = workbench.RunUI(); return returnCode; } Display* Workbench::CreateDisplay() { // create the display Display* newDisplay = Tweaklets::Get(WorkbenchTweaklet::KEY)->CreateDisplay(); // workaround for 1GEZ9UR and 1GF07HN //newDisplay.setWarnings(false); // Set the priority higher than normal so as to be higher // than the JobManager. //Poco::Thread::current()->setPriority(Poco::Thread::PRIO_HIGH); //initializeImages(); return newDisplay; } Workbench::ServiceLocatorOwner::ServiceLocatorOwner(Workbench* wb) : workbench(wb) { } void Workbench::ServiceLocatorOwner::Dispose() { QMessageBox::information( nullptr, "Restart needed", "A required plug-in is no longer available and the Workbench needs " "to be restarted. You will be prompted to save if there is any unsaved work."); workbench->Close(PlatformUI::RETURN_RESTART, true); } Workbench::Workbench(Display* display, WorkbenchAdvisor* advisor) : commandManager(nullptr), progressCount(-1) , serviceLocatorOwner(new ServiceLocatorOwner(this)) , largeUpdates(0), introManager(nullptr), isStarting(true), isClosing(false) , activeWorkbenchWindow(nullptr) { poco_check_ptr(display) ; poco_check_ptr(advisor); // the reference count to the one and only workbench instance // is increased, so that temporary smart pointer to the workbench // do not delete it this->Register(); this->display = display; this->advisor = advisor; Workbench::instance = this; serviceLocatorCreator.reset(new ServiceLocatorCreator()); serviceLocatorCreator->Register(); this->serviceLocator = serviceLocatorCreator->CreateServiceLocator( nullptr, nullptr, IDisposable::WeakPtr(serviceLocatorOwner)).Cast(); serviceLocator->RegisterService(serviceLocatorCreator.data()); workbenchLocationService.reset( new WorkbenchLocationService(IServiceScopes::WORKBENCH_SCOPE, this, nullptr, nullptr, 0)); workbenchLocationService->Register(); serviceLocator->RegisterService(workbenchLocationService.data()); returnCode = PlatformUI::RETURN_UNSTARTABLE; } Display* Workbench::GetDisplay() const { return display; } Workbench* Workbench::GetInstance() { return instance; } WorkbenchTestable::Pointer Workbench::GetWorkbenchTestable() { if (!testableObject) { testableObject = new WorkbenchTestable(); } return testableObject; } Workbench::~Workbench() { this->instance = nullptr; this->UnRegister(false); } Object* Workbench::GetService(const QString& key) { return serviceLocator->GetService(key); } bool Workbench::HasService(const QString& key) const { return serviceLocator->HasService(key); } bool Workbench::Init() { // // setup debug mode if required. // if (WorkbenchPlugin.getDefault().isDebugging()) { // WorkbenchPlugin.DEBUG = true; // ModalContext.setDebugMode(true); // } bool bail = false; // create workbench window manager //windowManager = new WindowManager(); IIntroRegistry* introRegistry = WorkbenchPlugin::GetDefault()->GetIntroRegistry(); if (introRegistry->GetIntroCount() > 0) { IProduct::Pointer product = Platform::GetProduct(); if (product.IsNotNull()) { introDescriptor = introRegistry->GetIntroForProduct(product->GetId()).Cast(); } } // TODO Correctly order service initialization // there needs to be some serious consideration given to // the services, and hooking them up in the correct order evaluationService.reset(new EvaluationService()); evaluationService->Register(); // StartupThreading.runWithoutExceptions(new StartupRunnable() { // // public void runWithException() { serviceLocator->RegisterService(evaluationService.data()); // } // }); // Initialize the activity support. //workbenchActivitySupport = new WorkbenchActivitySupport(); //activityHelper = ActivityPersistanceHelper.getInstance(); this->InitializeDefaultServices(); // initializeFonts(); // initializeColors(); // initializeApplicationColors(); // now that the workbench is sufficiently initialized, let the advisor // have a turn. advisor->InternalBasicInitialize(this->GetWorkbenchConfigurer()); // StartupThreading.runWithoutExceptions(new StartupRunnable() { // public void runWithException() { StartSourceProviders(); // } // }); // StartupThreading.runWithoutExceptions(new StartupRunnable() { // public void runWithException() { // activateWorkbenchContext(); // } // }); // StartupThreading.runWithoutExceptions(new StartupRunnable() { // public void runWithException() { // createApplicationMenu(); // } // }); // attempt to restore a previous workbench state advisor->PreStartup(); if (!advisor->OpenWindows()) { bail = true; } if (bail) return false; //forceOpenPerspective(); return true; } bool Workbench::RestoreState() { //return false; if (!GetWorkbenchConfigurer()->GetSaveAndRestore()) { // QString msg = "This application does not save and restore previously saved state."; // return new Status(IStatus.WARNING, WorkbenchPlugin.PI_WORKBENCH, // IWorkbenchConfigurer.RESTORE_CODE_RESET, msg, null); return false; } // Read the workbench state file. QString stateFile = GetWorkbenchStateFile(); // If there is no state file cause one to open. if (stateFile.isEmpty() || !QFile::exists(stateFile)) { // QString msg = "No previously saved state to restore."; // return new Status(IStatus.WARNING, WorkbenchPlugin.PI_WORKBENCH, // IWorkbenchConfigurer.RESTORE_CODE_RESET, msg, null); return false; } // final IStatus result[] = { new Status(IStatus.OK, // WorkbenchPlugin.PI_WORKBENCH, IStatus.OK, "", null) }; //$NON-NLS-1$ bool result = true; ISafeRunnable::Pointer runnable(new RestoreStateRunnable(this, stateFile, result)); SafeRunner::Run(runnable); // ensure at least one window was opened //if (result[0].isOK() && windowManager.getWindows().length == 0) if (result && windowManager.GetWindowCount() == 0) { QString msg = "No windows restored."; // result[0] = new Status(IStatus.ERROR, WorkbenchPlugin.PI_WORKBENCH, // IWorkbenchConfigurer.RESTORE_CODE_RESET, msg, null); result &= false; } return result; } bool Workbench::RestoreState(IMemento::Pointer memento) { // final MultiStatus result = new MultiStatus(PlatformUI.PLUGIN_ID, // IStatus.OK, WorkbenchMessages.Workbench_problemsRestoring, null); bool result = true; const bool showProgress = false; //TODO restore state progress // final boolean showProgress = PrefUtil.getAPIPreferenceStore() // .getBoolean( // IWorkbenchPreferenceConstants.SHOW_PROGRESS_ON_STARTUP); try { /* * Restored windows will be set in the createdWindows field to be * used by the openWindowsAfterRestore() method */ if (!showProgress) { DoRestoreState(memento, result); } else { // Retrieve how many plug-ins were loaded while restoring the // workbench int lastProgressCount = -1; memento->GetInteger(WorkbenchConstants::TAG_PROGRESS_COUNT, lastProgressCount); // If we don't know how many plug-ins were loaded last time, // assume we are loading half of the installed plug-ins. /*const std::size_t expectedProgressCount = std::max(1, lastProgressCount == -1 ? WorkbenchPlugin::GetDefault()->GetBundleCount() / 2 : lastProgressCount);*/ //TODO restore state progress // RunStartupWithProgress(expectedProgressCount, new Runnable() { // public void Run() { // DoRestoreState(memento, result); // } // }); } } catch (...) { OpenWindowsAfterRestore(); throw; } OpenWindowsAfterRestore(); return result; } void Workbench::DoRestoreState(IMemento::Pointer memento, bool& status) // final MultiStatus status) { IMemento::Pointer childMem; try { // UIStats.start(UIStats.RESTORE_WORKBENCH, "MRUList"); //$NON-NLS-1$ IMemento::Pointer mruMemento = memento ->GetChild(WorkbenchConstants::TAG_MRU_LIST); if (mruMemento) { // TODO restore editor history //status.add(getEditorHistory().restoreState(mruMemento)); } //UIStats.end(UIStats.RESTORE_WORKBENCH, this, "MRUList"); //$NON-NLS-1$ } catch (...) { //UIStats.end(UIStats.RESTORE_WORKBENCH, this, "MRUList"); //$NON-NLS-1$ throw; } // Restore advisor state. IMemento::Pointer advisorState = memento ->GetChild(WorkbenchConstants::TAG_WORKBENCH_ADVISOR); if (advisorState) { //status.add(getAdvisor().restoreState(advisorState)); status &= GetAdvisor()->RestoreState(advisorState); } // Get the child windows. QList children = memento ->GetChildren(WorkbenchConstants::TAG_WINDOW); createdWindows.clear(); // Read the workbench windows. for (int i = 0; i < children.size(); i++) { childMem = children[i]; WorkbenchWindow::Pointer newWindow; //StartupThreading.runWithoutExceptions(new StartupRunnable() { // public void runWithException() { newWindow = NewWorkbenchWindow(); newWindow->Create(); // }}); createdWindows.push_back(newWindow); // allow the application to specify an initial perspective to open // @issue temporary workaround for ignoring initial perspective // String initialPerspectiveId = // getAdvisor().getInitialWindowPerspectiveId(); // if (initialPerspectiveId != null) { // IPerspectiveDescriptor desc = // getPerspectiveRegistry().findPerspectiveWithId(initialPerspectiveId); // result.merge(newWindow.restoreState(childMem, desc)); // } // add the window so that any work done in newWindow.restoreState // that relies on Workbench methods has windows to work with windowManager.Add(newWindow); // now that we've added it to the window manager we need to listen // for any exception that might hose us before we get a chance to // open it. If one occurs, remove the new window from the manager. // Assume that the new window is a phantom for now try { //status.merge(newWindow[0].restoreState(childMem, null)); status &= newWindow->RestoreState(childMem, IPerspectiveDescriptor::Pointer(nullptr)); try { newWindow->FireWindowRestored(); } catch (const WorkbenchException& /*e*/) { //status.add(e.getStatus()); status &= false; } // everything worked so far, don't close now } catch (...) { // null the window in newWindowHolder so that it won't be // opened later on createdWindows[i] = nullptr; //StartupThreading.runWithoutExceptions(new StartupRunnable() { // public void runWithException() throws Throwable { newWindow->Close(); // }}); } } } void Workbench::OpenWindowsAfterRestore() { if (createdWindows.empty()) { return; } // now open the windows (except the ones that were nulled because we // closed them above) for (int i = 0; i < createdWindows.size(); i++) { if (createdWindows[i]) { WorkbenchWindow::Pointer myWindow = createdWindows[i]; //StartupThreading.runWithoutExceptions(new StartupRunnable() { // public void runWithException() throws Throwable { try { myWindow->Open(); } catch (...) { myWindow->Close(); throw; } // }}); } } createdWindows.clear(); } void Workbench::InitializeDefaultServices() { // final IContributionService contributionService = new ContributionService( // getAdvisor()); // serviceLocator.registerService(IContributionService.class, // contributionService); // // // TODO Correctly order service initialization // // there needs to be some serious consideration given to // // the services, and hooking them up in the correct order // // // StartupThreading.runWithoutExceptions(new StartupRunnable() { // // public void runWithException() { saveablesList.reset(new SaveablesList()); saveablesList->Register(); serviceLocator->RegisterService(saveablesList.data()); // }}); // /* * Phase 1 of the initialization of commands. When this phase completes, * all the services and managers will exist, and be accessible via the * getService(Object) method. */ // StartupThreading.runWithoutExceptions(new StartupRunnable() { // // public void runWithException() { Command::DEBUG_COMMAND_EXECUTION = Policy::DEBUG_COMMANDS(); Command::DEBUG_HANDLERS = Policy::DEBUG_HANDLERS_VERBOSE(); Command::DEBUG_HANDLERS_COMMAND_ID = Policy::DEBUG_HANDLERS_VERBOSE_COMMAND_ID(); commandManager.reset(new CommandManager()); // }}); // // final CommandService [] commandService = new CommandService[1]; // StartupThreading.runWithoutExceptions(new StartupRunnable() { // // public void runWithException() { commandService.reset(new CommandService(commandManager.data())); commandService->Register(); commandService->ReadRegistry(); serviceLocator->RegisterService(commandService.data()); // }}); // // StartupThreading.runWithoutExceptions(new StartupRunnable() { // // public void runWithException() { // ContextManager.DEBUG = Policy.DEBUG_CONTEXTS; // contextManager = new ContextManager(); // }}); // // final IContextService contextService = new ContextService( // contextManager); // // StartupThreading.runWithoutExceptions(new StartupRunnable() { // // public void runWithException() { // contextService.readRegistry(); // }}); // // serviceLocator.registerService(IContextService.class, contextService); // // // final IBindingService [] bindingService = new BindingService[1]; // // StartupThreading.runWithoutExceptions(new StartupRunnable() { // // public void runWithException() { // BindingManager.DEBUG = Policy.DEBUG_KEY_BINDINGS; // bindingManager = new BindingManager(contextManager, commandManager); // bindingService[0] = new BindingService( // bindingManager, commandService[0], Workbench.this); // // }}); // // bindingService[0].readRegistryAndPreferences(commandService[0]); // serviceLocator.registerService(IBindingService.class, bindingService[0]); // // final CommandImageManager commandImageManager = new CommandImageManager(); // final CommandImageService commandImageService = new CommandImageService( // commandImageManager, commandService[0]); // commandImageService.readRegistry(); // serviceLocator.registerService(ICommandImageService.class, // commandImageService); auto wms = new WorkbenchMenuService(serviceLocator.GetPointer()); menuService.reset(wms); menuService->Register(); serviceLocator->RegisterService(menuService.data()); // the service must be registered before it is initialized - its // initialization uses the service locator to address a dependency on // the menu service //StartupThreading.runWithoutExceptions(new StartupRunnable() { // public void runWithException() { wms->ReadRegistry(); // }}); // the source providers are now initialized in phase 3, but source // priorities have to be set before handler initialization // StartupThreading.runWithoutExceptions(new StartupRunnable() { // public void runWithException() { InitializeSourcePriorities(); // } // }); /* * Phase 2 of the initialization of commands. This handles the creation * of wrappers for legacy APIs. By the time this phase completes, any * code trying to access commands through legacy APIs should work. */ //IHandlerService* handlerService = nullptr; // StartupThreading.runWithoutExceptions(new StartupRunnable() { // public void runWithException() { /*handlerService = */serviceLocator->GetService(); // } // }); // workbenchContextSupport = new WorkbenchContextSupport(this, // contextManager); // workbenchCommandSupport = new WorkbenchCommandSupport(bindingManager, // commandManager, contextManager, handlerService[0]); // initializeCommandResolver(); this->AddWindowListener(this); // bindingManager.addBindingManagerListener(bindingManagerListener); // serviceLocator.registerService(ISelectionConversionService.class, // new SelectionConversionService()); } int Workbench::RunUI() { // initialize workbench and restore or open one window bool initOK = this->Init(); // let the advisor run its start up code if (initOK) { advisor->PostStartup(); // may trigger a close/restart } //TODO start eager plug-ins //startPlugins(); //addStartupRegistryListener(); isStarting = false; BERRY_INFO << "BlueBerry Workbench ready"; this->GetWorkbenchTestable()->Init(Display::GetDefault(), this); // spin event loop return display->RunEventLoop(); } QString Workbench::GetDefaultPerspectiveId() const { return this->GetAdvisor()->GetInitialWindowPerspectiveId(); } IAdaptable* Workbench::GetDefaultPageInput() const { return this->GetAdvisor()->GetDefaultPageInput(); } QString Workbench::GetPresentationId() const { if (factoryID != "") { return factoryID; } //factoryID = PrefUtil.getAPIPreferenceStore().getString( // IWorkbenchPreferenceConstants.PRESENTATION_FACTORY_ID); // Workaround for bug 58975 - New preference mechanism does not properly // initialize defaults // Ensure that the UI plugin has started too. factoryID = WorkbenchConstants::DEFAULT_PRESENTATION_ID; return factoryID; } IElementFactory* Workbench::GetElementFactory(const QString& factoryId) const { return WorkbenchPlugin::GetDefault()->GetElementFactory(factoryId); } void Workbench::UpdateTheme() { WorkbenchPlugin::GetDefault()->GetPresentationFactory()->UpdateTheme(); } void Workbench::LargeUpdateStart() { if (largeUpdates++ == 0) { // TODO Consider whether these lines still need to be here. // workbenchCommandSupport.setProcessing(false); // workbenchContextSupport.setProcessing(false); QList windows = this->GetWorkbenchWindows(); for (int i = 0; i < windows.size(); i++) { IWorkbenchWindow::Pointer window = windows[i]; if (window.Cast() != 0) { window.Cast()->LargeUpdateStart(); } } } } void Workbench::LargeUpdateEnd() { if (--largeUpdates == 0) { // TODO Consider whether these lines still need to be here. // workbenchCommandSupport.setProcessing(true); // workbenchContextSupport.setProcessing(true); // Perform window-specific blocking. QList windows = this->GetWorkbenchWindows(); for (int i = 0; i < windows.size(); i++) { IWorkbenchWindow::Pointer window = windows[i]; if (window.Cast() != 0) { window.Cast()->LargeUpdateEnd(); } } } } IExtensionTracker*Workbench::GetExtensionTracker() const { if (tracker.isNull()) { tracker.reset(new UIExtensionTracker(this->GetDisplay())); } return tracker.data(); } void Workbench::OpenFirstTimeWindow() { try { IAdaptable* input; //StartupThreading.runWithoutExceptions(new StartupRunnable() { // public void runWithException() throws Throwable { input = this->GetDefaultPageInput(); // }}); this->BusyOpenWorkbenchWindow(this->GetPerspectiveRegistry()->GetDefaultPerspective(), input); } catch (WorkbenchException& e) { // Don't use the window's shell as the dialog parent, // as the window is not open yet (bug 76724). //StartupThreading.runWithoutExceptions(new StartupRunnable() { // public void runWithException() throws Throwable { // ErrorDialog.openError(null, // WorkbenchMessages.Problems_Opening_Page, e.getMessage(), e // .getStatus()); // }}); BERRY_ERROR << "Error: Problems opening page. " << e.what() << std::endl; } } WorkbenchConfigurer::Pointer Workbench::GetWorkbenchConfigurer() { if (workbenchConfigurer.IsNull()) { workbenchConfigurer = new WorkbenchConfigurer(); } return workbenchConfigurer; } WorkbenchAdvisor* Workbench::GetAdvisor() const { return advisor; } IViewRegistry* Workbench::GetViewRegistry() const { return WorkbenchPlugin::GetDefault()->GetViewRegistry(); } IEditorRegistry* Workbench::GetEditorRegistry() const { return WorkbenchPlugin::GetDefault()->GetEditorRegistry(); } EditorHistory* Workbench::GetEditorHistory() const { if (editorHistory.isNull()) { editorHistory.reset(new EditorHistory()); } return editorHistory.data(); } IPerspectiveRegistry* Workbench::GetPerspectiveRegistry() const { return WorkbenchPlugin::GetDefault()->GetPerspectiveRegistry(); } bool Workbench::Close() { return this->Close(PlatformUI::RETURN_OK, false); } bool Workbench::Close(int returnCode, bool force) { BERRY_INFO << "Closing workbench..."; this->returnCode = returnCode; bool ret; //BusyIndicator.showWhile(null, new Runnable() { // public void run() { ret = this->BusyClose(force); // } //}); return ret; } /** * Closes the workbench. Assumes that the busy cursor is active. * * @param force * true if the close is mandatory, and false if the close is * allowed to fail * @return true if the close succeeded, and false otherwise */ bool Workbench::BusyClose(bool force) { // notify the advisor of preShutdown and allow it to veto if not forced isClosing = advisor->PreShutdown(); if (!force && !isClosing) { return false; } // notify regular workbench clients of preShutdown and allow them to // veto if not forced isClosing = this->FirePreShutdown(force); if (!force && !isClosing) { return false; } // save any open editors if they are dirty isClosing = this->SaveAllEditors(!force); if (!force && !isClosing) { return false; } bool closeEditors = !force && false; // false is the default for the not yet implemented preference below // && PrefUtil.getAPIPreferenceStore().getBoolean( // IWorkbenchPreferenceConstants.CLOSE_EDITORS_ON_EXIT); if (closeEditors) { // SafeRunner.run(new SafeRunnable() { // public void run() { QList windows = this->GetWorkbenchWindows(); for (int i = 0; i < windows.size(); i++) { IWorkbenchPage::Pointer page = windows[i]->GetActivePage(); if (page) isClosing = isClosing && page->CloseAllEditors(false); } // } //}); if (!force && !isClosing) { return false; } } if (this->GetWorkbenchConfigurer()->GetSaveAndRestore()) { try { // SafeRunner.run(new SafeRunnable() { // public void run() { XMLMemento::Pointer mem = RecordWorkbenchState(); // Save the IMemento to a file. SaveMementoToFile(mem); // } } catch(const ctkException& e) { // public void handleException(Throwable e) { QString message; if (e.what() == nullptr) { message = "An error has occurred. See error log for more details. Do you want to exit?"; } else { message = QString("An error has occurred: ") + e.what() + ". See error log for more details. Do you want to exit?"; } if (QMessageBox::question(nullptr, "Error", message) != QMessageBox::Yes) { isClosing = false; } } // } // }); } if (!force && !isClosing) { return false; } //SafeRunner.run(new SafeRunnable(WorkbenchMessages.ErrorClosing) { // public void run() { if (isClosing || force) { isClosing = windowManager.Close(); } // } //}); if (!force && !isClosing) { return false; } this->Shutdown(); display->ExitEventLoop(0); return true; } QString Workbench::GetWorkbenchStateFile() const { QString path = WorkbenchPlugin::GetDefault()->GetDataLocation(); if (path.isNull()) { return QString(); } return QDir::cleanPath(path + "/" + DEFAULT_WORKBENCH_STATE_FILENAME); } /* * Save the workbench UI in a persistence file. */ bool Workbench::SaveMementoToFile(XMLMemento::Pointer memento) { // Save it to a file. // XXX: nobody currently checks the return value of this method. QString stateFile = GetWorkbenchStateFile(); if (stateFile.isNull()) { return false; } //BERRY_INFO << "Saving state to: " << stateFile.path() << std::endl; try { Poco::FileOutputStream stream(stateFile.toStdString()); memento->Save(stream); } catch (const Poco::IOException& /*e*/) { QFile::remove(stateFile); QMessageBox::critical(nullptr, "Saving Problems", "Unable to store workbench state."); return false; } // Success ! return true; } IWorkbenchWindow::Pointer Workbench::GetActiveWorkbenchWindow() const { // Look for the window that was last known being // the active one WorkbenchWindow::Pointer win = this->GetActivatedWindow(); return win; } std::size_t Workbench::GetWorkbenchWindowCount() const { return windowManager.GetWindowCount(); } QList Workbench::GetWorkbenchWindows() const { QList windows = windowManager.GetWindows(); QList result; for (QList::iterator iter = windows.begin(); iter != windows.end(); ++iter) { result.push_back(iter->Cast()); } return result; } IWorkbenchWindow::Pointer Workbench::OpenWorkbenchWindow( const QString& perspID, IAdaptable* input) { // Run op in busy cursor. //final Object[] result = new Object[1]; //BusyIndicator.showWhile(null, new Runnable() { // public void run() { // try { return this->BusyOpenWorkbenchWindow(perspID, input); // } catch (WorkbenchException e) { // result[0] = e; // } // } //}); } IWorkbenchWindow::Pointer Workbench::OpenWorkbenchWindow(IAdaptable* input) { return this->OpenWorkbenchWindow(this->GetPerspectiveRegistry() ->GetDefaultPerspective(), input); } IWorkbenchPage::Pointer Workbench::ShowPerspective( const QString& perspectiveId, IWorkbenchWindow::Pointer window) { // If the specified window has the requested perspective open, then the // window // is given focus and the perspective is shown. The page's input is // ignored. WorkbenchWindow::Pointer win = window.Cast (); if (win) { IWorkbenchPage::Pointer page = win->GetActivePage(); if (page) { QList perspectives(page ->GetOpenPerspectives()); for (int i = 0; i < perspectives.size(); i++) { IPerspectiveDescriptor::Pointer persp = perspectives[i]; if (perspectiveId == persp->GetId()) { win->MakeVisible(); page->SetPerspective(persp); return page; } } } } // If another window that has the workspace root as input and the // requested - // perpective open and active, then the window is given focus. + // perspective open and active, then the window is given focus. IAdaptable* input = GetDefaultPageInput(); QList windows(GetWorkbenchWindows()); for (int i = 0; i < windows.size(); i++) { win = windows[i].Cast(); if (window != win) { WorkbenchPage::Pointer page = win->GetActivePage().Cast(); if (page) { bool inputSame = false; if (input == nullptr) { inputSame = (page->GetInput() == nullptr); } else { inputSame = input == page->GetInput(); } if (inputSame) { Perspective::Pointer persp = page->GetActivePerspective(); if (persp) { IPerspectiveDescriptor::Pointer desc = persp->GetDesc(); if (desc) { if (perspectiveId == desc->GetId()) { Shell::Pointer shell = win->GetShell(); shell->Open(); if (shell->GetMinimized()) { shell->SetMinimized(false); } return page; } } } } } } } // Otherwise the requested perspective is opened and shown in the // specified // window or in a new window depending on the current user preference // for opening // perspectives, and that window is given focus. win = window.Cast(); if (win) { auto* prefs = WorkbenchPlugin::GetDefault()->GetPreferences(); int mode = prefs->GetInt(PreferenceConstants::OPEN_PERSP_MODE, PreferenceConstants::OPM_ACTIVE_PAGE); IWorkbenchPage::Pointer page = win->GetActivePage(); IPerspectiveDescriptor::Pointer persp; if (page) { persp = page->GetPerspective(); } // Only open a new window if user preference is set and the window // has an active perspective. if (PreferenceConstants::OPM_NEW_WINDOW == mode && persp) { IWorkbenchWindow::Pointer newWindow = OpenWorkbenchWindow(perspectiveId, input); return newWindow->GetActivePage(); } IPerspectiveDescriptor::Pointer desc = GetPerspectiveRegistry() ->FindPerspectiveWithId(perspectiveId); if (desc == 0) { throw WorkbenchException( "Unable to create perspective \"" + perspectiveId + "\". There is no corresponding perspective extension."); } win->GetShell()->Open(); if (page == 0) { page = win->OpenPage(perspectiveId, input); } else { page->SetPerspective(desc); } return page; } // Just throw an exception.... throw WorkbenchException("Problems opening perspective \"" + perspectiveId + "\""); } IWorkbenchPage::Pointer Workbench::ShowPerspective( const QString& /*perspectiveId*/, IWorkbenchWindow::Pointer /*window*/, IAdaptable* /*input*/) { return IWorkbenchPage::Pointer(nullptr); // // If the specified window has the requested perspective open and the // // same requested // // input, then the window is given focus and the perspective is shown. // bool inputSameAsWindow = false; // WorkbenchWindow::Pointer win = window.Cast(); // if (win.IsNotNull()) { // WorkbenchPage::Pointer page = win->GetActiveWorkbenchPage(); // if (page.IsNotNull()) { // bool inputSame = false; // if (input == 0) { // inputSame = (page->GetInput() == 0); // } else { // inputSame = input.equals(page.getInput()); // } // if (inputSame) { // inputSameAsWindow = true; // IPerspectiveDescriptor perspectives[] = page // .getOpenPerspectives(); // for (int i = 0; i < perspectives.length; i++) { // IPerspectiveDescriptor persp = perspectives[i]; // if (perspectiveId.equals(persp.getId())) { // win.makeVisible(); // page.setPerspective(persp); // return page; // } // } // } // } // } // // // If another window has the requested input and the requested - // // perpective open and active, then that window is given focus. + // // perspective open and active, then that window is given focus. // IWorkbenchWindow[] windows = getWorkbenchWindows(); // for (int i = 0; i < windows.length; i++) { // win = (WorkbenchWindow) windows[i]; // if (window != win) { // WorkbenchPage page = win.getActiveWorkbenchPage(); // if (page != null) { // boolean inputSame = false; // if (input == null) { // inputSame = (page.getInput() == null); // } else { // inputSame = input.equals(page.getInput()); // } // if (inputSame) { // Perspective persp = page.getActivePerspective(); // if (persp != null) { // IPerspectiveDescriptor desc = persp.getDesc(); // if (desc != null) { // if (perspectiveId.equals(desc.getId())) { // win.getShell().open(); // return page; // } // } // } // } // } // } // } // // // If the specified window has the same requested input but not the // // requested // // perspective, then the window is given focus and the perspective is // // opened and shown // // on condition that the user preference is not to open perspectives in // // a new window. // win = (WorkbenchWindow) window; // if (inputSameAsWindow && win != null) { // IPreferenceStore store = WorkbenchPlugin.getDefault() // .getPreferenceStore(); // int mode = store.getInt(IPreferenceConstants.OPEN_PERSP_MODE); // // if (IPreferenceConstants.OPM_NEW_WINDOW != mode) { // IWorkbenchPage page = win.getActiveWorkbenchPage(); // IPerspectiveDescriptor desc = getPerspectiveRegistry() // .findPerspectiveWithId(perspectiveId); // if (desc == null) { // throw new WorkbenchException( // NLS // .bind( // WorkbenchMessages.WorkbenchPage_ErrorCreatingPerspective, // perspectiveId)); // } // win.getShell().open(); // if (page == null) { // page = win.openPage(perspectiveId, input); // } else { // page.setPerspective(desc); // } // return page; // } // } // // // If the specified window has no active perspective, then open the // // requested perspective and show the specified window. // if (win != null) { // IWorkbenchPage page = win.getActiveWorkbenchPage(); // IPerspectiveDescriptor persp = null; // if (page != null) { // persp = page.getPerspective(); // } // if (persp == null) { // IPerspectiveDescriptor desc = getPerspectiveRegistry() // .findPerspectiveWithId(perspectiveId); // if (desc == null) { // throw new WorkbenchException( // NLS // .bind( // WorkbenchMessages.WorkbenchPage_ErrorCreatingPerspective, // perspectiveId)); // } // win.getShell().open(); // if (page == null) { // page = win.openPage(perspectiveId, input); // } else { // page.setPerspective(desc); // } // return page; // } // } // // // Otherwise the requested perspective is opened and shown in a new // // window, and the // // window is given focus. // IWorkbenchWindow newWindow = openWorkbenchWindow(perspectiveId, input); // return newWindow.getActivePage(); } bool Workbench::SaveAllEditors(bool /*confirm*/) { return true; } IIntroManager* Workbench::GetIntroManager() const { return GetWorkbenchIntroManager(); } WorkbenchIntroManager* Workbench::GetWorkbenchIntroManager() const { if (introManager.isNull()) { introManager.reset(new WorkbenchIntroManager(const_cast(this))); } return introManager.data(); } IntroDescriptor::Pointer Workbench::GetIntroDescriptor() const { return introDescriptor; } void Workbench::SetIntroDescriptor(IntroDescriptor::Pointer descriptor) { if (GetIntroManager()->GetIntro()) { GetIntroManager()->CloseIntro(GetIntroManager()->GetIntro()); } introDescriptor = descriptor; } bool Workbench::IsRunning() const { return Tweaklets::Get(WorkbenchTweaklet::KEY)->IsRunning(); } bool Workbench::IsStarting() const { return isStarting; } bool Workbench::IsClosing() const { return isClosing; } WorkbenchWindow::Pointer Workbench::GetActivatedWindow() const { return activatedWindow; } /* * Sets the workbench window which was last known being the active one, or * null . */ void Workbench::SetActivatedWindow(WorkbenchWindow::Pointer window) { activatedWindow = window; } WorkbenchWindow::Pointer Workbench::NewWorkbenchWindow() { WorkbenchWindow::Pointer wbw(new WorkbenchWindow(this->GetNewWindowNumber())); return wbw; } int Workbench::GetNewWindowNumber() { // Get window list. QList windows = windowManager.GetWindows(); int count = static_cast(windows.size()); // Create an array of booleans (size = window count). // Cross off every number found in the window list. auto checkArray = new bool[count]; for (int nX = 0; nX < count; ++nX) { if (windows[nX].Cast ().IsNotNull()) { WorkbenchWindow::Pointer ww = windows[nX].Cast (); int index = ww->GetNumber() - 1; if (index >= 0 && index < count) { checkArray[index] = true; } } } // Return first index which is not used. // If no empty index was found then every slot is full. // Return next index. for (int index = 0; index < count; index++) { if (!checkArray[index]) { delete[] checkArray; return index + 1; } } delete[] checkArray; return static_cast(count + 1); } IWorkbenchWindow::Pointer Workbench::BusyOpenWorkbenchWindow( const QString& perspID, IAdaptable* input) { // Create a workbench window (becomes active window) //final WorkbenchWindow newWindowArray[] = new WorkbenchWindow[1]; //StartupThreading.runWithWorkbenchExceptions(new StartupRunnable() { // public void runWithException() { // newWindowArray[0] = newWorkbenchWindow(); WorkbenchWindow::Pointer newWindow = this->NewWorkbenchWindow(); // } //}); //final WorkbenchWindow newWindow = newWindowArray[0]; //StartupThreading.runWithoutExceptions(new StartupRunnable() { // public void runWithException() { newWindow->Create(); // must be created before adding to window // manager // } //}); windowManager.Add(newWindow); //final WorkbenchException [] exceptions = new WorkbenchException[1]; // Create the initial page. if (perspID != "") { //StartupThreading.runWithWorkbenchExceptions(new StartupRunnable() { try { newWindow->BusyOpenPage(perspID, input); } catch (WorkbenchException& e) { windowManager.Remove(newWindow); throw e; } } // Open window after opening page, to avoid flicker. //StartupThreading.runWithWorkbenchExceptions(new StartupRunnable() { // public void runWithException() { newWindow->Open(); // } //}); return newWindow; } bool Workbench::SaveState(IMemento::Pointer memento) { // MultiStatus result = new MultiStatus(PlatformUI.PLUGIN_ID, IStatus.OK, // WorkbenchMessages.Workbench_problemsSaving, null); bool result = true; // Save the version number. memento->PutString(WorkbenchConstants::TAG_VERSION, VERSION_STRING[0]); // Save how many plug-ins were loaded while restoring the workbench if (progressCount != -1) { memento->PutInteger(WorkbenchConstants::TAG_PROGRESS_COUNT, progressCount); } // Save the advisor state. IMemento::Pointer advisorState = memento ->CreateChild(WorkbenchConstants::TAG_WORKBENCH_ADVISOR); //result.add(getAdvisor().saveState(advisorState)); result &= GetAdvisor()->SaveState(advisorState); // Save the workbench windows. QList windows(GetWorkbenchWindows()); for (int nX = 0; nX < windows.size(); nX++) { WorkbenchWindow::Pointer window = windows[nX].Cast(); IMemento::Pointer childMem = memento->CreateChild(WorkbenchConstants::TAG_WINDOW); //result.merge(window.saveState(childMem)); result &= window->SaveState(childMem); } // result.add(getEditorHistory().saveState( // memento.createChild(IWorkbenchConstants.TAG_MRU_LIST))); return result; } XMLMemento::Pointer Workbench::RecordWorkbenchState() { XMLMemento::Pointer memento = XMLMemento ::CreateWriteRoot(WorkbenchConstants::TAG_WORKBENCH); //final IStatus status = saveState(memento); bool status = SaveState(memento); //if (status.getSeverity() != IStatus.OK) { if (!status) { // // don't use newWindow as parent because it has not yet been opened // // (bug 76724) // StartupThreading.runWithoutExceptions(new StartupRunnable() { // // public void runWithException() throws Throwable { // ErrorDialog.openError(null, // WorkbenchMessages.Workbench_problemsSaving, // WorkbenchMessages.Workbench_problemsSavingMsg, status); // }}); } return memento; } void Workbench::AddWorkbenchListener(IWorkbenchListener* listener) { workbenchEvents.AddListener(listener); } void Workbench::RemoveWorkbenchListener(IWorkbenchListener* listener) { workbenchEvents.RemoveListener(listener); } IWorkbenchListener::Events& Workbench::GetWorkbenchEvents() { return workbenchEvents; } void Workbench::AddWindowListener(IWindowListener* l) { windowEvents.AddListener(l); } void Workbench::RemoveWindowListener(IWindowListener* l) { windowEvents.RemoveListener(l); } IWindowListener::Events& Workbench::GetWindowEvents() { return windowEvents; } bool Workbench::FirePreShutdown(bool forced) { //SafeRunnable.run(new SafeRunnable() { // public void run() { typedef IWorkbenchListener::Events::PreShutdownEvent::ListenerList ListenerList; const ListenerList& listeners = workbenchEvents.preShutdown.GetListeners(); for ( auto iter = listeners.begin(); iter != listeners.end(); ++iter ) { // notify each listener if (! (*iter)->Execute(dynamic_cast(this), forced)) return false; } // } return true; } /** * Fire workbench postShutdown event. * * @since 3.2 */ void Workbench::FirePostShutdown() { // SafeRunnable.run(new SafeRunnable() { // public void run() { workbenchEvents.postShutdown(this); // } } void Workbench::FireWindowOpened(IWorkbenchWindow::Pointer window) { // SafeRunner.run(new SafeRunnable() { // public void run() { windowEvents.windowOpened(window); // } } void Workbench::FireWindowClosed(IWorkbenchWindow::Pointer window) { if (activatedWindow == window) { // Do not hang onto it so it can be GC'ed activatedWindow = nullptr; } // SafeRunner.run(new SafeRunnable() { // public void run() { windowEvents.windowClosed(window); // } } void Workbench::FireWindowActivated(IWorkbenchWindow::Pointer window) { // SafeRunner.run(new SafeRunnable() { // public void run() { windowEvents.windowActivated(window); // } } void Workbench::FireWindowDeactivated(IWorkbenchWindow::Pointer window) { // SafeRunner.run(new SafeRunnable() { // public void run() { windowEvents.windowDeactivated(window); // } } IWorkbenchWindow::Pointer Workbench::RestoreWorkbenchWindow(IMemento::Pointer memento) { WorkbenchWindow::Pointer newWindow = this->NewWorkbenchWindow(); //newWindow.create(); windowManager.Add(newWindow); // whether the window was opened bool opened = false; try { newWindow->RestoreState(memento, IPerspectiveDescriptor::Pointer(nullptr)); newWindow->FireWindowRestored(); newWindow->Open(); opened = true; } catch (...) { if (!opened) { newWindow->Close(); } } return newWindow; } void Workbench::Shutdown() { // shutdown application-specific portions first advisor->PostShutdown(); // notify regular workbench clients of shutdown, and clear the list when // done this->FirePostShutdown(); //workbenchListeners.clear(); //cancelEarlyStartup(); // for dynamic UI // Platform.getExtensionRegistry().removeRegistryChangeListener( // extensionEventHandler); // Platform.getExtensionRegistry().removeRegistryChangeListener( // startupRegistryListener); // ((GrabFocus) Tweaklets.get(GrabFocus.KEY)).dispose(); // Bring down all of the services. serviceLocator->Dispose(); // workbenchActivitySupport.dispose(); // WorkbenchHelpSystem.disposeIfNecessary(); // shutdown the rest of the workbench // WorkbenchColors.shutdown(); // activityHelper.shutdown(); // uninitializeImages(); // if (WorkbenchPlugin.getDefault() != null) { // WorkbenchPlugin.getDefault().reset(); // } // WorkbenchThemeManager.getInstance().dispose(); // PropertyPageContributorManager.getManager().dispose(); // ObjectActionContributorManager.getManager().dispose(); // if (tracker != null) { // tracker.close(); // } Tweaklets::Clear(); } void Workbench::InitializeSourcePriorities() { WorkbenchServiceRegistry::GetRegistry()->InitializeSourcePriorities(); } void Workbench::StartSourceProviders() { /* * Phase 3 of the initialization of commands. The source providers that * the workbench provides are creating and registered with the above * services. These source providers notify the services when particular * pieces of workbench state change. */ IEvaluationService* const evaluationService = serviceLocator->GetService(); //IContextService* const contextService = serviceLocator->GetService(); auto sps = new SourceProviderService(serviceLocator.GetPointer()); sourceProviderService.reset(sps); sourceProviderService->Register(); serviceLocator->RegisterService(sourceProviderService.data()); struct SafeSourceProviderRunnable : public ISafeRunnable { SafeSourceProviderRunnable(SourceProviderService* sps, IEvaluationService* es) : sps(sps), es(es) {} void Run() override { // this currently instantiates all players ... sigh sps->ReadRegistry(); QList sp = sps->GetSourceProviders(); for (int i = 0; i < sp.size(); i++) { es->AddSourceProvider(sp[i]); //if (!(sp[i] instanceof ActiveContextSourceProvider)) //{ // contextService.addSourceProvider(sp[i]); //} } } void HandleException(const ctkException& exception) override { WorkbenchPlugin::Log("Failed to initialize a source provider", exception); } private: SourceProviderService* sps; IEvaluationService* es; }; ISafeRunnable::Pointer sourceProviderRunnable( new SafeSourceProviderRunnable(sps, evaluationService)); SafeRunner::Run(sourceProviderRunnable); // SafeRunner.run(new ISafeRunnable() { // public void run() throws Exception { // // these guys are need to provide the variables they say // // they source // actionSetSourceProvider = (ActionSetSourceProvider) sourceProviderService // .getSourceProvider(ISources.ACTIVE_ACTION_SETS_NAME); // FocusControlSourceProvider focusControl = (FocusControlSourceProvider) sourceProviderService // .getSourceProvider(ISources.ACTIVE_FOCUS_CONTROL_ID_NAME); // serviceLocator.registerService(IFocusService.class, focusControl); // menuSourceProvider = (MenuSourceProvider) sourceProviderService // .getSourceProvider(ISources.ACTIVE_MENU_NAME); // } // public void handleException(Throwable exception) { // WorkbenchPlugin.log("Failed to initialize a source provider", exception); //$NON-NLS-1$ // } // }); } void Workbench::UpdateActiveWorkbenchWindowMenuManager(bool textOnly) { if (activeWorkbenchWindow != nullptr) { // if (actionSetSourceProvider != null) // { // activeWorkbenchWindow->RemoveActionSetsListener(actionSetSourceProvider); // } activeWorkbenchWindow = nullptr; } //bool actionSetsUpdated = false; IWorkbenchWindow::Pointer workbenchWindow = GetActiveWorkbenchWindow(); if (WorkbenchWindow::Pointer wbWnd = workbenchWindow.Cast()) { activeWorkbenchWindow = wbWnd.GetPointer(); if (activeWorkbenchWindow->IsClosing()) { return; } // Update the action sets. // Shell::Pointer windowShell = activeWorkbenchWindow->GetShell(); // Shell::Pointer activeShell = GetDisplay()->GetActiveShell(); // IContextService* service = GetService(); // if ((Util.equals(windowShell, activeShell) || service.getShellType(activeShell) == IContextService.TYPE_WINDOW) // && actionSetSourceProvider != null) // { // activeWorkbenchWindow->AddActionSetsListener(actionSetSourceProvider); // final WorkbenchPage page = activeWorkbenchWindow.getActiveWorkbenchPage(); // final IActionSetDescriptor[] newActionSets; // if (page != null) // { // newActionSets = page.getActionSets(); // final ActionSetsEvent event = new ActionSetsEvent(newActionSets); // actionSetSourceProvider.actionSetsChanged(event); // actionSetsUpdated = true; // } // } MenuManager* menuManager = activeWorkbenchWindow->GetMenuManager(); if (textOnly) { menuManager->Update(QActionProperties::TEXT); } else { menuManager->Update(true); } } // if (!actionSetsUpdated && actionSetSourceProvider != null) // { // ActionSetsEvent event = new ActionSetsEvent(null); // actionSetSourceProvider.actionSetsChanged(event); // } } void Workbench::WindowActivated(const IWorkbenchWindow::Pointer&) { this->UpdateActiveWorkbenchWindowMenuManager(true); } void Workbench::WindowDeactivated(const IWorkbenchWindow::Pointer&) { this->UpdateActiveWorkbenchWindowMenuManager(true); } void Workbench::WindowClosed(const IWorkbenchWindow::Pointer&) { this->UpdateActiveWorkbenchWindowMenuManager(true); } void Workbench::WindowOpened(const IWorkbenchWindow::Pointer&) { this->UpdateActiveWorkbenchWindowMenuManager(true); } } diff --git a/Plugins/org.blueberry.ui.qt/src/internal/berryWorkbenchPage.h b/Plugins/org.blueberry.ui.qt/src/internal/berryWorkbenchPage.h index 85d05670da..a1b83bac33 100644 --- a/Plugins/org.blueberry.ui.qt/src/internal/berryWorkbenchPage.h +++ b/Plugins/org.blueberry.ui.qt/src/internal/berryWorkbenchPage.h @@ -1,1428 +1,1428 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef BERRYWORKBENCHPAGE_H_ #define BERRYWORKBENCHPAGE_H_ #include #include "berryIWorkbenchPage.h" #include "berryIWorkbenchPartReference.h" #include "berryIReusableEditor.h" #include "berryILayoutContainer.h" #include "berryIStickyViewManager.h" #include "berryWorkbenchPagePartList.h" #include "berryWorkbenchPartReference.h" #include "berryPageSelectionService.h" #include "berryEditorManager.h" #include "berryViewFactory.h" #include "berryPartPane.h" #include namespace berry { struct IExtensionPoint; struct IExtensionTracker; //class PartPane; //class PartPane::Sashes; class EditorAreaHelper; class WorkbenchWindow; class Perspective; class PerspectiveHelper; class PerspectiveDescriptor; class LayoutPartSash; class LayoutTree; class LayoutTreeNode; class PartService; /** * \ingroup org_blueberry_ui_internal * * A collection of views and editors in a workbench. */ class BERRY_UI_QT WorkbenchPage: public IWorkbenchPage { public: berryObjectMacro(WorkbenchPage); protected: //TODO Weakpointer WorkbenchWindow* window; friend class ViewFactory; friend class WorkbenchWindow; friend class EditorAreaHelper; friend class WWinPartService; private: /** * Manages editor contributions and action set part associations. */ class ActionSwitcher { public: ActionSwitcher(WorkbenchPage* page); /** * Updates the contributions given the new part as the active part. * * @param newPart * the new active part, may be null */ void UpdateActivePart(IWorkbenchPart::Pointer newPart); /** * Updates the contributions given the new part as the topEditor. * * @param newEditor * the new top editor, may be null */ void UpdateTopEditor(IEditorPart::Pointer newEditor); private: /** * Activates the contributions of the given part. If enable * is true the contributions are visible and enabled, * otherwise they are disabled. * * @param part * the part whose contributions are to be activated * @param enable * true the contributions are to be enabled, * not just visible. */ void ActivateContributions(IWorkbenchPart::Pointer part, bool enable); /** * Deactivates the contributions of the given part. If remove * is true the contributions are removed, otherwise they * are disabled. * * @param part * the part whose contributions are to be deactivated * @param remove * true the contributions are to be removed, * not just disabled. */ void DeactivateContributions(IWorkbenchPart::Pointer part, bool remove); WorkbenchPage* page; IWorkbenchPart::WeakPtr activePart; IEditorPart::WeakPtr topEditor; }; class ActivationList { public: //List of parts in the activation order (oldest first) typedef std::deque PartListType; typedef std::deque::iterator PartListIter; typedef std::deque::reverse_iterator PartListReverseIter; private: PartListType parts; WorkbenchPage* page; public: ActivationList(WorkbenchPage* page); /* * Add/Move the active part to end of the list; */ void SetActive(SmartPointer part); /* * Ensures that the given part appears AFTER any other part in the same * container. */ void BringToTop(SmartPointer ref); /* * Returns the last (most recent) iterator (index) of the given container in the activation list, or returns * end() if the given container does not appear in the activation list. */ PartListIter LastIndexOfContainer(SmartPointer container); /* * Add/Move the active part to end of the list; */ void SetActive(SmartPointer ref); /* * Add the active part to the beginning of the list. */ void Add(SmartPointer ref); /* * Return the active part. Filter fast views. */ SmartPointer GetActive(); /* * Return the previously active part. Filter fast views. */ SmartPointer GetPreviouslyActive(); SmartPointer GetActiveReference(bool editorsOnly); /* * Returns the index of the part within the activation list. The higher * the index, the more recently it was used. */ PartListIter IndexOf(SmartPointer part); /* * Returns the index of the part reference within the activation list. * The higher the index, the more recent it was used. */ PartListIter IndexOf(SmartPointer ref); /* * Remove a part from the list */ bool Remove(SmartPointer ref); /* * Returns the topmost editor on the stack, or null if none. */ SmartPointer GetTopEditor(); /* * Returns the editors in activation order (oldest first). */ QList > GetEditors(); /* * Return a list with all parts (editors and views). */ QList > GetParts(); private: SmartPointer GetActive(PartListIter start); SmartPointer GetActiveReference(PartListIter start, bool editorsOnly); /* * Find a part in the list starting from the end and filter * and views from other perspectives. Will filter fast views * unless 'includeActiveFastViews' is true; */ SmartPointer GetActiveReference(PartListIter start, bool editorsOnly, bool skipPartsObscuredByZoom); }; /** * Helper class to keep track of all opened perspective. Both the opened * and used order is kept. */ struct PerspectiveList { public: typedef QList > PerspectiveListType; typedef PerspectiveListType::iterator iterator; private: /** * List of perspectives in the order they were opened; */ PerspectiveListType openedList; /** * List of perspectives in the order they were used. Last element is * the most recently used, and first element is the least recently * used. */ PerspectiveListType usedList; /** * The perspective explicitly set as being the active one */ SmartPointer active; void UpdateActionSets(SmartPointer oldPersp, SmartPointer newPersp); public: /** * Creates an empty instance of the perspective list */ PerspectiveList(); /** * Update the order of the perspectives in the opened list * * @param perspective * @param newLoc */ void Reorder(IPerspectiveDescriptor::Pointer perspective, int newLoc); /** * Return all perspectives in the order they were activated. * * @return an array of perspectives sorted by activation order, least * recently activated perspective last. */ PerspectiveListType GetSortedPerspectives(); /** * Adds a perspective to the list. No check is done for a duplicate when * adding. * @param perspective the perspective to add * @return boolean true if the perspective was added */ bool Add(SmartPointer perspective); /** * Returns an iterator on the perspective list in the order they were * opened. */ PerspectiveListType::iterator Begin(); PerspectiveListType::iterator End(); /** * Returns an array with all opened perspectives */ PerspectiveListType GetOpenedPerspectives(); /** * Removes a perspective from the list. */ bool Remove(SmartPointer perspective); /** * Swap the opened order of old perspective with the new perspective. */ void Swap(SmartPointer oldPerspective, SmartPointer newPerspective); /** * Returns whether the list contains any perspectives */ bool IsEmpty(); /** * Returns the most recently used perspective in the list. */ SmartPointer GetActive(); /** * Returns the next most recently used perspective in the list. */ SmartPointer GetNextActive(); /** * Returns the number of perspectives opened */ PerspectiveListType::size_type Size(); /** * Marks the specified perspective as the most recently used one in the * list. */ void SetActive(SmartPointer perspective); }; IAdaptable* input; QWidget* composite; //Could be delete. This information is in the active part list; ActivationList* activationList; EditorManager* editorMgr; EditorAreaHelper* editorPresentation; //ListenerList propertyChangeListeners = new ListenerList(); PageSelectionService* selectionService; QScopedPointer partList; // = new WorkbenchPagePartList(selectionService); //IActionBars actionBars; ViewFactory* viewFactory; PerspectiveList perspList; SmartPointer deferredActivePersp; //NavigationHistory navigationHistory = new NavigationHistory(this); IStickyViewManager::Pointer stickyViewMan; /** * Returns true if perspective with given id contains view with given id */ bool HasView(const QString& perspectiveId, const QString& viewId); /** * If we're in the process of activating a part, this points to the new part. * Otherwise, this is null. */ IWorkbenchPartReference::Pointer partBeingActivated; /** * Contains a list of perspectives that may be dirty due to plugin * installation and removal. */ std::set dirtyPerspectives; ActionSwitcher actionSwitcher; mutable QScopedPointer tracker; // Deferral count... delays disposing parts and sending certain events if nonzero int deferCount; // Parts waiting to be disposed QList pendingDisposals; SmartPointer GetPerspectiveExtensionPoint(); public: /** * Constructs a new page with a given perspective and input. * * @param w * the parent window * @param layoutID * must not be null * @param input * the page input * @throws WorkbenchException * on null layout id */ WorkbenchPage(WorkbenchWindow* w, const QString& layoutID, IAdaptable* input); /** * Constructs a page. restoreState(IMemento) should be * called to restore this page from data stored in a persistence file. * * @param w * the parent window * @param input * the page input * @throws WorkbenchException */ WorkbenchPage(WorkbenchWindow* w, IAdaptable* input); ~WorkbenchPage() override; /** * Activates a part. The part will be brought to the front and given focus. * * @param part * the part to activate */ void Activate(IWorkbenchPart::Pointer part) override; /** * Adds an IPartListener to the part service. */ void AddPartListener(IPartListener* l) override; /* * (non-Javadoc) Method declared on ISelectionListener. */ void AddSelectionListener(ISelectionListener* listener) override; /* * (non-Javadoc) Method declared on ISelectionListener. */ void AddSelectionListener(const QString& partId, ISelectionListener* listener) override; /* * (non-Javadoc) Method declared on ISelectionListener. */ void AddPostSelectionListener(ISelectionListener* listener) override; /* * (non-Javadoc) Method declared on ISelectionListener. */ void AddPostSelectionListener(const QString& partId, ISelectionListener* listener) override; /** * Moves a part forward in the Z order of a perspective so it is visible. * If the part is in the same stack as the active part, the new part is * activated. * * @param part * the part to bring to move forward */ void BringToTop(IWorkbenchPart::Pointer part) override; private: /** * Activates a part. The part is given focus, the pane is highlighted. */ void ActivatePart(const IWorkbenchPart::Pointer part); ILayoutContainer::Pointer GetContainer(IWorkbenchPart::Pointer part); ILayoutContainer::Pointer GetContainer(IWorkbenchPartReference::Pointer part); SmartPointer GetPane(IWorkbenchPart::Pointer part); SmartPointer GetPane(IWorkbenchPartReference::Pointer part); /** * Brings a part to the front of its stack. Does not update the active part or * active editor. This should only be called if the caller knows that the part * is not in the same stack as the active part or active editor, or if the caller * is prepared to update activation after the call. * * @param part */ bool InternalBringToTop(IWorkbenchPartReference::Pointer part); /** * Resets the layout for the perspective. The active part in the old layout * is activated in the new layout for consistent user context. * * Assumes the busy cursor is active. */ void BusyResetPerspective(); /** * Implements setPerspective. * * Assumes that busy cursor is active. * * @param desc * identifies the new perspective. */ void BusySetPerspective(IPerspectiveDescriptor::Pointer desc); /* * Performs showing of the view in the given mode. */ void BusyShowView(IViewPart::Pointer part, int mode); /** * Returns whether a part exists in the current page. */ bool CertifyPart(IWorkbenchPart::Pointer part); protected: /** * Shows a view. * * Assumes that a busy cursor is active. */ IViewPart::Pointer BusyShowView(const QString& viewID, const QString& secondaryID, int mode); public: void UpdateActionBars(); /** * Removes the perspective which match the given description. * * @param desc * identifies the perspective to be removed. */ void RemovePerspective(IPerspectiveDescriptor::Pointer desc); /** * Closes the perspective. */ bool Close() override; /** * See IWorkbenchPage */ bool CloseAllSavedEditors(); /** * See IWorkbenchPage */ bool CloseAllEditors(bool save) override; /** * See IWorkbenchPage */ bool CloseEditors(const QList& refArray, bool save) override; private: void UpdateActivePart(); /** * Makes the given part active. Brings it in front if necessary. Permits null * (indicating that no part should be active). * * @param ref new active part (or null) */ void MakeActive(IWorkbenchPartReference::Pointer ref); /** * Makes the given editor active. Brings it to front if necessary. Permits null * (indicating that no editor is active). * * @param ref the editor to make active, or null for no active editor */ void MakeActiveEditor(IEditorReference::Pointer ref); /** * Enables or disables listener notifications. This is used to delay listener notifications until the * end of a public method. * * @param shouldDefer */ void DeferUpdates(bool shouldDefer); void StartDeferring(); void HandleDeferredEvents(); bool IsDeferred(); public: /** * See IWorkbenchPage#closeEditor */ bool CloseEditor(IEditorReference::Pointer editorRef, bool save); /** * See IWorkbenchPage#closeEditor */ bool CloseEditor(IEditorPart::Pointer editor, bool save) override; /** * Closes current perspective. If last perspective, then entire page * is closed. * * @param saveParts * whether the page's parts should be saved if closed * @param closePage * whether the page itself should be closed if last perspective */ void CloseCurrentPerspective(bool saveParts, bool closePage); /** * @see IWorkbenchPage#closePerspective(IPerspectiveDescriptor, boolean, boolean) */ void ClosePerspective(IPerspectiveDescriptor::Pointer desc, bool saveParts, bool closePage) override; /** * @see IWorkbenchPage#closeAllPerspectives(boolean, boolean) */ void CloseAllPerspectives(bool saveEditors, bool closePage) override; protected: /** * Closes the specified perspective. If last perspective, then entire page * is closed. * * @param persp * the perspective to be closed * @param saveParts * whether the parts that are being closed should be saved * (editors if last perspective, views if not shown in other * parspectives) */ void ClosePerspective(SmartPointer persp, bool saveParts, bool closePage); /** * This is called by child objects after a part has been added to the page. * The page will in turn notify its listeners. */ void PartAdded(WorkbenchPartReference::Pointer ref); /** * This is called by child objects after a part has been added to the page. * The part will be queued for disposal after all listeners have been notified */ void PartRemoved(WorkbenchPartReference::Pointer ref); private: /** * Creates the client composite. */ void CreateClientComposite(); /** * Creates a new view set. Return null on failure. * * @param desc the perspective descriptor * @param notify whether to fire a perspective opened event */ SmartPointer CreatePerspective(SmartPointer desc, bool notify); void DisposePart(WorkbenchPartReference::Pointer ref); /** * Deactivates a part. The pane is unhighlighted. */ void DeactivatePart(IWorkbenchPart::Pointer part); /** * Dispose a perspective. * * @param persp the perspective descriptor * @param notify whether to fire a perspective closed event */ void DisposePerspective(SmartPointer persp, bool notify); public: /** * Detaches a view from the WorkbenchWindow. */ void DetachView(IViewReference::Pointer ref); /** * Removes a detachedwindow. */ void AttachView(IViewReference::Pointer ref); /** * Returns the first view manager with given ID. */ SmartPointer FindPerspective(IPerspectiveDescriptor::Pointer desc); /** * See IWorkbenchPage@findView. */ IViewPart::Pointer FindView(const QString& id) override; /* * (non-Javadoc) * * @see org.blueberry.ui.IWorkbenchPage */ IViewReference::Pointer FindViewReference(const QString& viewId) override; /* * (non-Javadoc) * * @see org.blueberry.ui.IWorkbenchPage */ IViewReference::Pointer FindViewReference(const QString& viewId, const QString& secondaryId) override; /** * Notify property change listeners about a property change. * * @param changeId * the change id * @param oldValue * old property value * @param newValue * new property value */ //private: void FirePropertyChange(String changeId, Object oldValue, // Object newValue) { // // UIListenerLogging.logPagePropertyChanged(this, changeId, oldValue, newValue); // // Object[] listeners = propertyChangeListeners.getListeners(); // PropertyChangeEvent event = new PropertyChangeEvent(this, changeId, // oldValue, newValue); // // for (int i = 0; i < listeners.length; i++) { // ((IPropertyChangeListener) listeners[i]).propertyChange(event); // } // } /** * @see IWorkbenchPage */ IEditorPart::Pointer GetActiveEditor() override; /** * Returns the reference for the active editor, or null * if there is no active editor. * * @return the active editor reference or null */ IEditorReference::Pointer GetActiveEditorReference(); /* * (non-Javadoc) Method declared on IPartService */ IWorkbenchPart::Pointer GetActivePart() override; /* * (non-Javadoc) Method declared on IPartService */ IWorkbenchPartReference::Pointer GetActivePartReference() override; /** * Returns the active perspective for the page, null if * none. */ SmartPointer GetActivePerspective(); /** * Returns the client composite. */ QWidget* GetClientComposite(); // for dynamic UI - change access from private to protected // for testing purposes only, changed from protected to public /** * Answer the editor manager for this window. */ EditorManager* GetEditorManager(); /** * Answer the perspective presentation. */ PerspectiveHelper* GetPerspectivePresentation(); /** * Answer the editor presentation. */ EditorAreaHelper* GetEditorPresentation(); /** * Allow access to the part service for this page ... used internally to * propagate certain types of events to the page part listeners. * @return the part service for this page. */ PartService* GetPartService(); /** * See IWorkbenchPage. */ QList GetEditors() override; QList GetDirtyEditors() override; QList GetDirtyParts(); /** * See IWorkbenchPage. */ IEditorPart::Pointer FindEditor(IEditorInput::Pointer input) override; /** * See IWorkbenchPage. */ QList FindEditors( IEditorInput::Pointer input, const QString& editorId, int matchFlags) override; /** * See IWorkbenchPage. */ QList GetEditorReferences() override; /** * @see IWorkbenchPage */ IAdaptable* GetInput() override; /** * Returns the page label. This is a combination of the page input and * active perspective. */ QString GetLabel() override; /** * Returns the perspective. */ IPerspectiveDescriptor::Pointer GetPerspective() override; /* * (non-Javadoc) Method declared on ISelectionService */ ISelection::ConstPointer GetSelection() const override; /* * (non-Javadoc) Method declared on ISelectionService */ ISelection::ConstPointer GetSelection(const QString& partId) override; //public: // SelectionEvents& GetSelectionEvents(const QString& partId = ""); /* * Returns the view factory. */ ViewFactory* GetViewFactory(); /** * See IWorkbenchPage. */ QList GetViewReferences() override; /** * See IWorkbenchPage. */ QList GetViews() override; protected: /** * Returns all view parts in the specified perspective * * @param persp the perspective * @return an array of view parts */ /*package*/ QList GetViews(SmartPointer persp, bool restore); /* package */ void RefreshActiveView(); public: /** * See IWorkbenchPage. */ IWorkbenchWindow::Pointer GetWorkbenchWindow() const override; /* * (non-Javadoc) * * @see org.blueberry.ui.IWorkbenchPage#hideView(org.blueberry.ui.IViewReference) */ void HideView(IViewReference::Pointer ref) override; /** * See IPerspective */ void HideView(IViewPart::Pointer view) override; private: /** * Initialize the page. * * @param w * the parent window * @param layoutID * may be null if restoring from file * @param input * the page input * @param openExtras * whether to process the perspective extras preference */ void Init(WorkbenchWindow* w, const QString& layoutID, IAdaptable* input, bool openExtras); /** * Opens the perspectives specified in the PERSPECTIVE_BAR_EXTRAS preference (see bug 84226). */ public: void OpenPerspectiveExtras(); /** * See IWorkbenchPage. */ bool IsPartVisible(IWorkbenchPart::Pointer part) override; /** * See IWorkbenchPage. */ bool IsEditorAreaVisible(); /** * Returns whether the view is fast. */ bool IsFastView(IViewReference::Pointer ref); /** * Return whether the view is closeable or not. * * @param ref the view reference to check. Must not be null. * @return true if the part is closeable. */ bool IsCloseable(IViewReference::Pointer ref); /** * Return whether the view is moveable or not. * * @param ref the view reference to check. Must not be null. * @return true if the part is moveable. */ bool IsMoveable(IViewReference::Pointer ref); /** * Returns whether the layout of the active * perspective is fixed. */ bool IsFixedLayout(); protected: /** * Return true if the perspective has a dirty editor. */ bool IsSaveNeeded(); /** * This method is called when the page is activated. */ void OnActivate(); /** * This method is called when the page is deactivated. */ void OnDeactivate(); public: /** * See IWorkbenchPage. */ void ReuseEditor(IReusableEditor::Pointer editor, IEditorInput::Pointer input) override; /** * See IWorkbenchPage. */ IEditorPart::Pointer OpenEditor(IEditorInput::Pointer input, const QString& editorID) override; /** * See IWorkbenchPage. */ IEditorPart::Pointer OpenEditor(IEditorInput::Pointer input, const QString& editorID, bool activate) override; /** * See IWorkbenchPage. */ IEditorPart::Pointer OpenEditor(IEditorInput::Pointer input, const QString& editorID, bool activate, int matchFlags) override; /** * This is not public API but for use internally. editorState can be null. */ IEditorPart::Pointer OpenEditor(IEditorInput::Pointer input, const QString& editorID, bool activate, int matchFlags, IMemento::Pointer editorState); /* * Added to fix Bug 178235 [EditorMgmt] DBCS 3.3 - Cannot open file with external program. * Opens a new editor using the given input and descriptor. (Normally, editors are opened using * an editor ID and an input.) */ IEditorPart::Pointer OpenEditorFromDescriptor(IEditorInput::Pointer input, IEditorDescriptor::Pointer editorDescriptor, bool activate, IMemento::Pointer editorState); private: /** * @see #openEditor(IEditorInput, String, boolean, int) */ IEditorPart::Pointer BusyOpenEditor(IEditorInput::Pointer input, const QString& editorID, bool activate, int matchFlags, IMemento::Pointer editorState); /* * Added to fix Bug 178235 [EditorMgmt] DBCS 3.3 - Cannot open file with external program. * See openEditorFromDescriptor(). */ IEditorPart::Pointer BusyOpenEditorFromDescriptor( IEditorInput::Pointer input, EditorDescriptor::Pointer editorDescriptor, bool activate, IMemento::Pointer editorState); /* * Added to fix Bug 178235 [EditorMgmt] DBCS 3.3 - Cannot open file with external program. * See openEditorFromDescriptor(). */ IEditorPart::Pointer BusyOpenEditorFromDescriptorBatched( IEditorInput::Pointer input, EditorDescriptor::Pointer editorDescriptor, bool activate, IMemento::Pointer editorState); public: void OpenEmptyTab(); /** * See IWorkbenchPage. */ bool IsEditorPinned(IEditorPart::Pointer editor) override; /** * Removes an IPartListener from the part service. */ void RemovePartListener(IPartListener* l) override; /* * (non-Javadoc) Method declared on ISelectionListener. */ void RemoveSelectionListener(ISelectionListener* listener) override; /* * (non-Javadoc) Method declared on ISelectionListener. */ void RemoveSelectionListener(const QString& partId, ISelectionListener* listener) override; /* * (non-Javadoc) Method declared on ISelectionListener. */ void RemovePostSelectionListener(ISelectionListener* listener) override; /* * (non-Javadoc) Method declared on ISelectionListener. */ void RemovePostSelectionListener(const QString& partId, ISelectionListener* listener) override; /** * This method is called when a part is activated by clicking within it. In * response, the part, the pane, and all of its actions will be activated. * * In the current design this method is invoked by the part pane when the * pane, the part, or any children gain focus. */ void RequestActivation(IWorkbenchPart::Pointer part); /** * Resets the layout for the perspective. The active part in the old layout * is activated in the new layout for consistent user context. */ void ResetPerspective() override; /** * Restore this page from the memento and ensure that the active * perspective is equals the active descriptor otherwise create a new * perspective for that descriptor. If activeDescriptor is null active the * old perspective. */ /*IStatus*/bool RestoreState(IMemento::Pointer memento, IPerspectiveDescriptor::Pointer activeDescriptor); /** * See IWorkbenchPage */ bool SaveAllEditors(bool confirm) override; /** * @param confirm * @param addNonPartSources true if saveables from non-part sources should be saved too * @return false if the user cancelled * */ bool SaveAllEditors(bool confirm, bool addNonPartSources); /** * Saves an editors in the workbench. If confirm is true * the user is prompted to confirm the command. * * @param confirm * if user confirmation should be sought * @return true if the command succeeded, or false * if the user cancels the command */ bool SaveEditor(IEditorPart::Pointer editor, bool confirm) override; /** * Saves the current perspective. */ void SavePerspective() override; /** * Saves the perspective. */ void SavePerspectiveAs(IPerspectiveDescriptor::Pointer newDesc) override; /** * Save the state of the page. */ /*IStatus*/bool SaveState(IMemento::Pointer memento); /** * See IWorkbenchPage. */ void SetEditorAreaVisible(bool showEditorArea); /** * Sets the perspective. * * @param desc * identifies the new perspective. */ void SetPerspective(IPerspectiveDescriptor::Pointer desc) override; /** * See IWorkbenchPage. */ IViewPart::Pointer ShowView(const QString& viewID) override; /* * (non-Javadoc) * * @see org.blueberry.ui.IWorkbenchPage#showView(java.lang.String, * java.lang.String, int) */ IViewPart::Pointer ShowView(const QString& viewID, const QString& secondaryID, int mode) override; /* * Returns the editors in activation order (oldest first). */ QList GetSortedEditors(); /** * @see IWorkbenchPage#getOpenPerspectives() */ QList GetOpenPerspectives() override; protected: /** * Do not call this method. Use busyOpenEditor. * * @see IWorkbenchPage#openEditor(IEditorInput, String, boolean) */ IEditorPart::Pointer BusyOpenEditorBatched(IEditorInput::Pointer input, const QString& editorID, bool activate, int matchFlags, IMemento::Pointer editorState); void ShowEditor(bool activate, IEditorPart::Pointer editor); /* * Saves the workbench part. */ bool SavePart(ISaveablePart::Pointer saveable, IWorkbenchPart::Pointer part, bool confirm); /** * Restore the toolbar layout for the active perspective. */ void ResetToolBarLayout(); /** * Return all open Perspective objects. * * @return all open Perspective objects */ /*package*/ QList > GetOpenInternalPerspectives(); /** - * Checks perspectives in the order they were activiated + * Checks perspectives in the order they were activated * for the specified part. The first sorted perspective * that contains the specified part is returned. * * @param part specified part to search for * @return the first sorted perspespective containing the part */ /*package*/ SmartPointer GetFirstPerspectiveWithView(IViewPart::Pointer part); // for dynamic UI void AddPerspective(SmartPointer persp); public: /** * Returns the perspectives in activation order (oldest first). */ QList GetSortedPerspectives() override; /* * Returns the parts in activation order (oldest first). */ QList GetSortedParts(); /** * Returns the reference to the given part, or null if it has no reference * (i.e. it is not a top-level part in this workbench page). * * @param part the part * @return the part's reference or null if the given part does not belong * to this workbench page */ IWorkbenchPartReference::Pointer GetReference(IWorkbenchPart::Pointer part) override; /* * (non-Javadoc) * * @see org.blueberry.ui.IWorkbenchPage#getViewStack(org.blueberry.ui.IViewPart) */ QList GetViewStack(IViewPart::Pointer part); /** * Allow for programmatically resizing a part. *

* EXPERIMENTAL *

*

* Known limitations: *

    *
  • currently applies only to views
  • *
  • has no effect when view is zoomed
  • *
*/ void ResizeView(IViewPart::Pointer part, int width, int height); /** * Sanity-checks the objects in this page. Throws an Assertion exception * if an object's internal state is invalid. ONLY INTENDED FOR USE IN THE * UI TEST SUITES. */ void TestInvariants(); IExtensionTracker* GetExtensionTracker() const override; /* * (non-Javadoc) * * @see org.blueberry.ui.IWorkbenchPage#getPerspectiveShortcuts() */ QList GetPerspectiveShortcuts() override; /* * (non-Javadoc) * * @see org.blueberry.ui.IWorkbenchPage#getShowViewShortcuts() */ QList GetShowViewShortcuts() override; bool IsPartVisible(IWorkbenchPartReference::Pointer reference); private: QString GetId(IWorkbenchPart::Pointer part); QString GetId(IWorkbenchPartReference::Pointer ref); /** * Sets the active part. */ void SetActivePart(IWorkbenchPart::Pointer newPart); /** * Sets the layout of the page. Assumes the new perspective is not null. * Keeps the active part if possible. Updates the window menubar and * toolbar if necessary. */ void SetPerspective(SmartPointer newPersp); /* * Update visibility state of all views. */ void UpdateVisibility(SmartPointer oldPersp, SmartPointer newPersp); /** * @param mode the mode to test * @return whether the mode is recognized */ bool CertifyMode(int mode); /** * Find the stack of view references stacked with this view part. * * @param part * the part * @return the stack of references */ QList GetViewReferenceStack( IViewPart::Pointer part); struct ActivationOrderPred { ActivationOrderPred(ActivationList* partList); ActivationList* activationList; bool operator()(const IViewReference::Pointer o1, const IViewReference::Pointer o2) const; }; // provides sash information for the given pane struct SashInfo { SmartPointer right; SmartPointer left; SmartPointer top; SmartPointer bottom; SmartPointer rightNode; SmartPointer leftNode; SmartPointer topNode; SmartPointer bottomNode; }; void FindSashParts(SmartPointer tree, const PartPane::Sashes& sashes, SashInfo& info); protected: /** * Returns all parts that are owned by this page * * @return */ QList GetAllParts(); /** * Returns all open parts that are owned by this page (that is, all parts * for which a part opened event would have been sent -- these would be * activated parts whose controls have already been created. */ QList GetOpenParts(); private: void SuggestReset(); }; } #endif /*BERRYWORKBENCHPAGE_H_*/ diff --git a/Plugins/org.mitk.gui.common/src/mitkIRenderWindowPart.h b/Plugins/org.mitk.gui.common/src/mitkIRenderWindowPart.h index 38bd85cbb5..b1993e66d8 100644 --- a/Plugins/org.mitk.gui.common/src/mitkIRenderWindowPart.h +++ b/Plugins/org.mitk.gui.common/src/mitkIRenderWindowPart.h @@ -1,230 +1,230 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef mitkIRenderWindowPart_h #define mitkIRenderWindowPart_h #include #include #include #include #include #include #include #include class QmitkRenderWindow; namespace mitk { struct IRenderingManager; class TimeNavigationController; /** * \ingroup org_mitk_gui_common * * \brief Interface for a MITK Workbench Part providing a render window. * * This interface allows generic access to Workbench parts which provide some * kind of render window. The interface is intended to be implemented by * subclasses of berry::IWorkbenchPart. Usually, the interface is implemented * by a Workbench editor. * * A IRenderWindowPart provides zero or more QmitkRenderWindow instances which can * be controlled via this interface. QmitkRenderWindow instances have an associated * \e id, which is implementation specific. * Additionally the defined values Axial, Sagittal, Coronal and Original from mitk::AnatomicalPlane * can be used to retrieve a specific QmitkRenderWindow. * * \see ILinkedRenderWindowPart * \see IRenderWindowPartListener * \see QmitkAbstractRenderEditor */ struct MITK_GUI_COMMON_PLUGIN IRenderWindowPart { static const QString DECORATION_BORDER; // = "border" static const QString DECORATION_LOGO; // = "logo" static const QString DECORATION_MENU; // = "menu" static const QString DECORATION_BACKGROUND; // = "background" static const QString DECORATION_CORNER_ANNOTATION; // = "corner annotation" virtual ~IRenderWindowPart(); /** * Get the currently active (focused) render window. * Focus handling is implementation specific. * * \return The active QmitkRenderWindow instance; nullptr * if no render window is active. */ virtual QmitkRenderWindow* GetActiveQmitkRenderWindow() const = 0; /** * Get all render windows with their ids. * * \return A hash map mapping the render window id to the QmitkRenderWindow instance. */ virtual QHash GetQmitkRenderWindows() const = 0; /** * Get a render window with a specific id. * * \param id The render window id. * \return The QmitkRenderWindow instance for id */ virtual QmitkRenderWindow* GetQmitkRenderWindow(const QString& id) const = 0; /** * Get a render window with a specific plane orientation. * * \param orientation The render window plane orientation. * \return The QmitkRenderWindow instance for orientation */ virtual QmitkRenderWindow* GetQmitkRenderWindow(const mitk::AnatomicalPlane& orientation) const = 0; /** * Get the rendering manager used by this render window part. * * \return The current IRenderingManager instance or nullptr * if no rendering manager is used. */ virtual mitk::IRenderingManager* GetRenderingManager() const = 0; /** * Request an update of all render windows. * * \param requestType Specifies the type of render windows for which an update * will be requested. */ virtual void RequestUpdate(mitk::RenderingManager::RequestType requestType = mitk::RenderingManager::REQUEST_UPDATE_ALL) = 0; /** * Force an immediate update of all render windows. * * \param requestType Specifies the type of render windows for which an immediate update * will be requested. */ virtual void ForceImmediateUpdate(mitk::RenderingManager::RequestType requestType = mitk::RenderingManager::REQUEST_UPDATE_ALL) = 0; /** * @brief Initialize the render windows of this render window part to the given geometry. * * @param geometry The geometry to be used to initialize / update a * render window's time and slice navigation controller. * @param resetCamera If true, the camera and crosshair will be reset to the default view (centered, no zoom). * If false, the current crosshair position and the camera zoom will be stored and reset * after the reference geometry has been updated. */ virtual void InitializeViews(const mitk::TimeGeometry* geometry, bool resetCamera) = 0; /** - * @brief Define the reference geometry for interaction withing a render window. + * @brief Define the reference geometry for interaction within a render window. * * The concrete implementation is subclass-specific, no default implementation is provided here. * An implementation can be found in 'QmitkAbstractMultiWidgetEditor' and will just * forward the argument to the contained multi widget. * * @param referenceGeometry The interaction reference geometry for the concrete multi widget. * For more details, see 'BaseRenderer::SetInteractionReferenceGeometry'. */ virtual void SetInteractionReferenceGeometry(const mitk::TimeGeometry* referenceGeometry) = 0; /** * @brief Returns true if the render windows are coupled; false if not. * * Render windows are coupled if the slice navigation controller of the render windows * are connected which means that always the same geometry is used for the render windows. */ virtual bool HasCoupledRenderWindows() const = 0; /** * Get the TimeNavigationController for controlling time positions. * * \return A TimeNavigationController if the render window supports this * operation; otherwise returns nullptr. */ virtual mitk::TimeNavigationController* GetTimeNavigationController() const = 0; /** * Get the selected position in the render window with id id * or in the active render window if id is an empty string. * * \param id The render window id. * \return The currently selected position in world coordinates. */ virtual mitk::Point3D GetSelectedPosition(const QString& id = QString()) const = 0; /** * Set the selected position in the render window with id id * or in the active render window if id is nullptr. * * \param pos The position in world coordinates which should be selected. * \param id The render window id in which the selection should take place. */ virtual void SetSelectedPosition(const mitk::Point3D& pos, const QString& id = QString()) = 0; /** * Get the time point selected in the render window with id id * or in the active render window if id is an empty string. * * \param id The render window id. * \return The currently selected position in world coordinates. */ virtual TimePointType GetSelectedTimePoint(const QString& id = QString()) const = 0; /** * Enable \e decorations like colored borders, menu widgets, logos, text annotations, etc. * * Decorations are implementation specific. A set of standardized decoration names is listed * in GetDecorations(). * * \param enable If true enable the decorations specified in decorations, * otherwise disable them. * \param decorations A list of decoration names. If empty, all supported decorations are affected. * * \see GetDecorations() */ virtual void EnableDecorations(bool enable, const QStringList& decorations = QStringList()) = 0; /** * Return if a specific decoration is enabled. * * \return true if the decoration is enabled, false if it is disabled * or unknown. * * \see GetDecorations() */ virtual bool IsDecorationEnabled(const QString& decoration) const = 0; /** * Get a list of supported decorations. * * The following decoration names are standardized and should not be used for other decoration types: *
    *
  • \e DECORATION_BORDER Any border decorations like colored rectangles, etc. *
  • \e DECORATION_MENU Menus associated with render windows *
  • \e DECORATION_BACKGROUND All kinds of backgrounds (patterns, gradients, etc.) except for solid colored backgrounds *
  • \e DECORATION_LOGO Any kind of logo overlayed on the rendered scene *
* * \return A list of supported decoration names. */ virtual QStringList GetDecorations() const = 0; }; } Q_DECLARE_INTERFACE(mitk::IRenderWindowPart, "org.mitk.ui.IRenderWindowPart") #endif diff --git a/Plugins/org.mitk.gui.qt.dicombrowser/documentation/UserManual/QmitkDicom.dox b/Plugins/org.mitk.gui.qt.dicombrowser/documentation/UserManual/QmitkDicom.dox index 913fea2601..8748c9a517 100644 --- a/Plugins/org.mitk.gui.qt.dicombrowser/documentation/UserManual/QmitkDicom.dox +++ b/Plugins/org.mitk.gui.qt.dicombrowser/documentation/UserManual/QmitkDicom.dox @@ -1,118 +1,118 @@ /** \page org_mitk_editors_dicombrowser The Dicom Browser Plugin \imageMacro{dicombrowser-dox.svg,"Icon of the DICOM Plugin",2.00} \note This article requires a basic knowledge of the DICOM Standard. \tableofcontents \section org_mitk_gui_qt_dicomOverview Overview The DICOM editor is an experimental editor which allows for loading of DICOM images as well as server communication. It features a highly experimental query/retrieve (you need to configure your PACS correspondingly) as well as a DICOM browser. The DICOM browser allows you to navigate the DICOM folder/cd depending on its metadata (patient/study/series) and import selected series for viewing in your MITK based application. It also allows you to store your dicom data in an internal database so you can easily access often used dicom images. -It is based on the commonTK (CTK) DICOM funcionality. +It is based on the commonTK (CTK) DICOM functionality. \section org_mitk_gui_qt_dicomDataHandling Data handling \imageMacro{QmitkDicom_PluginControls.png,"The dicom Plugin controls",7.37} In the image above you see the start page of the dicom plugin. On top of the start page you see four buttons. The Local Storage, the Import CD, the Import Folder and the Query Retrieve button. If you press one of these buttons, the dicom plugin will switch to your local dicom image storage or will start importing dicom images from CD or a folder on your hard drive or it will open the query retrieve screen.
  • Click the 'Local Storage' button to open the local storage screen.
  • Click the 'Import CD' button to import DICOM data from a CD.
  • Click the 'Import Folder' button to import DICOM date from a directory.
  • Click the 'Query Retrieve' button to open the query retrieve screen.
\subsection org_mitk_gui_qt_dicomStorage Data storage \imageMacro{QmitkDicom_PluginExtended.png,"The DICOM data storage",16.00} If you open the dicom plugin the dicom data storage will be displayed. You are able to see all your stored dicom image data. You can browse your data by clicking on the left arrow beside the name of your data. There are three levels available. The first level is the patient level where you can see the patient data. On the second level you can see the dicom studies for the patient. on the third level you can see all available series referring to its study. You can delete the data by selecting it and pressing the delete button. Be careful if you have selected a patient or a study all referring data be deleted. So if you delete a patient the patient and all studies and series referred to the patient will be deleted. If you delete a study all series of the study will be deleted. If you want to view the dicom data you have to select a series and click on the View button. The data will appear in the DataManager and will be displayed. \imageMacro{QmitkDicom_DisplayDataManager.png,"Viewed image",16.00}
  • Click on the arrow on the left of your data to expand or hide dicom data levels.
  • Click the 'Delete' button to delete selected DICOM data.
  • Click the 'View' button to view DICOM data.
\subsection org_mitk_gui_qt_dicomImport Data import \imageMacro{QmitkDicom_ImportDialog.png,"The import dialog checked",9.53} There are two different ways to import DICOM data. The First one is to directly import it into your DICOM data storage. To achieve this you should toggle the checkbox 'Copy on import'. The second approach is, to have a look at the data first before importing it. To do that you simply don't check 'Copy on import'. This will leed you to the leed you to the 'External Dicom Data' screen which provides you a preview of the data containing in you're chosen folder. You can import the data here by selecting it and pressing the 'Download' button. It is also possible to view DICOM series directly in Mitk by selecting it here and pressing the 'View' button.
  • Click 'Import Folder' or 'Import CD' button to open the import dialog.
    • Enable the 'Copy on import' checkbox and choose a folder to import into data storage directly.
    • Disable the 'Copy on import' checkbox to get to the 'External Dicom Data' screen.
      • Click on the arrow on the left of your data to expand or hide dicom data levels.
      • Click the 'Download' button to download selected DICOM data to your DICOM data storage.
      • Click the 'View' button to view DICOM data.
\section org_mitk_gui_qt_dicomQueryRetrieve Query/Retrieve \warning This plugin is experimental and not all of the described features behave as expected. \note The query retrieve plugin only works if the PACS you are calling knows your machine settings. There are also issues when you are running a firewall. The query retrieve workflow allows you to get DICOM data from a server. \imageMacro{QmitkDicom_QueryRetrieve.png,"The query retrieve screen",16.00} \subsection org_mitk_gui_qt_dicomQuery Query \imageMacro{QmitkDicom_Nodes.png,"The DICOM network configuration",11.26} By performing a DICOM query you will ask a server for it's DICOM data. This requires to setup the DICOM network configuration of your system and the server. By clicking on 'Add Server' a new plain server field will appear. Now you can give it a name of your choice. Fill the servers "DICOM name" the AETitle. Type in it's url, it's port and the specific DICOM protocol you want to use for image transfer. \note I recommend not to use CGET because most of the PACS systems (Image Servers) don't support that protocol. You can configure the DICOM network configuration of your machine by editing the 'Calling AETiltle', the 'Storage AETitle' and The 'Storage Port' text fields. But normally you don't have to change your configuration. \imageMacro{QmitkDicom_FilterWidget.png,"The DICOM search options",3.66} After you have finished your network configuration and before you start the query you should use the 'Search Options' to specify your query. Otherwise all data on the server will be queried and you will have to wait for a long time. You can specify your query by searching for a specific patient name or a study or a serie or a specific DICOM object by it's id. You are allowed to include or exclude DICOM modalities from your query and you can specify a specific time in which the DICOM images you are searching for might been captured. When you finished that you can click the query button and the queried DICOM data will appear.
  • Click on the 'Add Server' button.
    • Edit 'Name' field.
    • Edit 'AETitle' field.
    • Edit 'Address' field.
    • Edit 'Port' field.
  • Set search options.
  • Click on 'Query' button.
\subsection org_mitk_gui_qt_dicomRetrieve Retrieve \imageMacro{QmitkDicom_Retrieve.png,"The queried DICOM data.",15.22} After the query you are able to select the queried data and click the 'Retrieve' button. This will store the queried DICOM data into your DICOM storage. Click on the 'Local Storage' button and work with your new data.
  • Click on the 'Retrieve' button to retrieve the data to your DICOM storage.
  • Click on the 'Local Storage' button.
*/ diff --git a/Plugins/org.mitk.gui.qt.matchpoint.framereg/src/internal/QmitkMatchPointFrameCorrection.cpp b/Plugins/org.mitk.gui.qt.matchpoint.framereg/src/internal/QmitkMatchPointFrameCorrection.cpp index 3f723975ff..5a11521f4b 100644 --- a/Plugins/org.mitk.gui.qt.matchpoint.framereg/src/internal/QmitkMatchPointFrameCorrection.cpp +++ b/Plugins/org.mitk.gui.qt.matchpoint.framereg/src/internal/QmitkMatchPointFrameCorrection.cpp @@ -1,724 +1,724 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "org_mitk_gui_qt_matchpoint_framereg_Activator.h" // Blueberry #include #include #include #include // Mitk #include #include #include #include #include #include #include #include #include #include #include #include // Qmitk #include "QmitkMatchPointFrameCorrection.h" #include #include // Qt #include #include #include #include #include // MatchPoint #include #include #include #include #include #include #include #include #include const std::string QmitkMatchPointFrameCorrection::VIEW_ID = "org.mitk.views.matchpoint.algorithm.framereg"; QmitkMatchPointFrameCorrection::QmitkMatchPointFrameCorrection() : m_Parent(nullptr), m_LoadedDLLHandle(nullptr), m_LoadedAlgorithm(nullptr), m_CanLoadAlgorithm(false), m_Working(false) { m_spSelectedTargetData = nullptr; m_spSelectedTargetMaskData = nullptr; } QmitkMatchPointFrameCorrection::~QmitkMatchPointFrameCorrection() { // remove selection service berry::ISelectionService* s = this->GetSite()->GetWorkbenchWindow()->GetSelectionService(); if (s) { s->RemoveSelectionListener(m_AlgorithmSelectionListener.data()); } } void QmitkMatchPointFrameCorrection::SetFocus() { } void QmitkMatchPointFrameCorrection::CreateConnections() { connect(m_Controls.imageNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkMatchPointFrameCorrection::OnNodeSelectionChanged); connect(m_Controls.maskNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkMatchPointFrameCorrection::OnNodeSelectionChanged); // ------ // Tab 1 - Shared library loading interface // ------ connect(m_Controls.m_pbLoadSelected, SIGNAL(clicked()), this, SLOT(OnLoadAlgorithmButtonPushed())); // ----- // Tab 2 - Execution // ----- connect(m_Controls.m_pbStartReg, SIGNAL(clicked()), this, SLOT(OnStartRegBtnPushed())); connect(m_Controls.m_pbSaveLog, SIGNAL(clicked()), this, SLOT(OnSaveLogBtnPushed())); // ----- // Tab 4 - Frames // ----- connect(m_Controls.m_btnFrameSelAll, SIGNAL(clicked()), this, SLOT(OnFramesSelectAllPushed())); connect(m_Controls.m_btnFrameDeSelAll, SIGNAL(clicked()), this, SLOT(OnFramesDeSelectAllPushed())); connect(m_Controls.m_btnFrameInvert, SIGNAL(clicked()), this, SLOT(OnFramesInvertPushed())); } const map::deployment::DLLInfo* QmitkMatchPointFrameCorrection::GetSelectedAlgorithmDLL() const { return m_SelectedAlgorithmInfo; } void QmitkMatchPointFrameCorrection::OnSelectedAlgorithmChanged() { std::stringstream descriptionString; ::map::deployment::DLLInfo::ConstPointer currentItemInfo = GetSelectedAlgorithmDLL(); if (!currentItemInfo) { return; } m_Controls.m_teAlgorithmDetails->updateInfo(currentItemInfo); m_Controls.m_lbSelectedAlgorithm->setText(QString::fromStdString( currentItemInfo->getAlgorithmUID().getName())); // enable loading m_CanLoadAlgorithm = true; this->AdaptFolderGUIElements(); } void QmitkMatchPointFrameCorrection::OnLoadAlgorithmButtonPushed() { map::deployment::DLLInfo::ConstPointer dllInfo = GetSelectedAlgorithmDLL(); if (!dllInfo) { Error(QStringLiteral("No valid algorithm is selected. Cannot load algorithm. ABORTING.")); return; } ::map::deployment::DLLHandle::Pointer tempDLLHandle = ::map::deployment::openDeploymentDLL( dllInfo->getLibraryFilePath()); ::map::algorithm::RegistrationAlgorithmBase::Pointer tempAlgorithm = ::map::deployment::getRegistrationAlgorithm(tempDLLHandle); if (tempAlgorithm.IsNull()) { Error(QStringLiteral("Error. Cannot load selected algorithm.")); return; } this->m_LoadedAlgorithm = tempAlgorithm; this->m_LoadedDLLHandle = tempDLLHandle; this->m_Controls.m_AlgoConfigurator->setAlgorithm(m_LoadedAlgorithm); this->AdaptFolderGUIElements(); this->ConfigureNodeSelectorPredicates(); this->CheckInputs(); this->ConfigureRegistrationControls(); this->ConfigureProgressInfos(); this->m_Controls.m_tabs->setCurrentIndex(1); } void QmitkMatchPointFrameCorrection::Error(QString msg) { mitk::StatusBar::GetInstance()->DisplayErrorText(msg.toLatin1()); MITK_ERROR << msg.toStdString().c_str(); m_Controls.m_teLog->append(QString("") + msg + QString("")); } void QmitkMatchPointFrameCorrection::AdaptFolderGUIElements() { m_Controls.m_pbLoadSelected->setEnabled(m_CanLoadAlgorithm); } void QmitkMatchPointFrameCorrection::CreateQtPartControl(QWidget* parent) { // create GUI widgets from the Qt Designer's .ui file m_Controls.setupUi(parent); m_Parent = parent; this->m_Controls.imageNodeSelector->SetDataStorage(this->GetDataStorage()); this->m_Controls.imageNodeSelector->SetSelectionIsOptional(false); this->m_Controls.maskNodeSelector->SetDataStorage(this->GetDataStorage()); this->m_Controls.maskNodeSelector->SetSelectionIsOptional(true); - this->m_Controls.imageNodeSelector->SetInvalidInfo("Select dymamic image."); + this->m_Controls.imageNodeSelector->SetInvalidInfo("Select dynamic image."); this->m_Controls.imageNodeSelector->SetPopUpTitel("Select dynamic image."); this->m_Controls.imageNodeSelector->SetPopUpHint("Select a dynamic image (time resolved) that should be frame corrected."); this->m_Controls.maskNodeSelector->SetInvalidInfo("Select target mask."); this->m_Controls.maskNodeSelector->SetPopUpTitel("Select target mask."); this->m_Controls.maskNodeSelector->SetPopUpHint("Select a target mask (mask of the target/first frame)."); m_Controls.m_tabs->setCurrentIndex(0); m_Controls.m_mapperSettings->AllowSampling(false); m_AlgorithmSelectionListener.reset(new berry::SelectionChangedAdapter(this, &QmitkMatchPointFrameCorrection::OnAlgorithmSelectionChanged)); // register selection listener GetSite()->GetWorkbenchWindow()->GetSelectionService()->AddSelectionListener( m_AlgorithmSelectionListener.data()); this->ConfigureNodeSelectorPredicates(); this->CreateConnections(); this->AdaptFolderGUIElements(); this->CheckInputs(); this->ConfigureProgressInfos(); this->ConfigureRegistrationControls(); berry::ISelection::ConstPointer selection = GetSite()->GetWorkbenchWindow()->GetSelectionService()->GetSelection("org.mitk.views.matchpoint.algorithm.browser"); this->UpdateAlgorithmSelection(selection); } void QmitkMatchPointFrameCorrection::ConfigureNodeSelectorPredicates() { auto isImage = mitk::MITKRegistrationHelper::ImageNodePredicate(); mitk::NodePredicateBase::Pointer maskDimensionPredicate = mitk::NodePredicateOr::New(mitk::NodePredicateDimension::New(3), mitk::NodePredicateDimension::New(4)).GetPointer(); mitk::NodePredicateDimension::Pointer imageDimensionPredicate = mitk::NodePredicateDimension::New(4); m_Controls.imageNodeSelector->setEnabled(m_LoadedAlgorithm.IsNotNull()); m_Controls.maskNodeSelector->setEnabled(m_LoadedAlgorithm.IsNotNull()); m_Controls.imageNodeSelector->SetNodePredicate(mitk::NodePredicateAnd::New(isImage, imageDimensionPredicate)); mitk::NodePredicateAnd::Pointer maskPredicate = mitk::NodePredicateAnd::New(mitk::MITKRegistrationHelper::MaskNodePredicate(), maskDimensionPredicate); if (m_spSelectedTargetData != nullptr) { auto hasSameGeometry = mitk::NodePredicateGeometry::New(m_spSelectedTargetData->GetGeometry()); hasSameGeometry->SetCheckPrecision(1e-10); maskPredicate = mitk::NodePredicateAnd::New(maskPredicate, hasSameGeometry); } m_Controls.maskNodeSelector->SetNodePredicate(maskPredicate); } void QmitkMatchPointFrameCorrection::CheckInputs() { mitk::DataNode::Pointer oldTargetNode = m_spSelectedTargetNode; m_spSelectedTargetNode = nullptr; m_spSelectedTargetData = nullptr; m_spSelectedTargetMaskNode = nullptr; m_spSelectedTargetMaskData = nullptr; if (m_LoadedAlgorithm.IsNull()) { m_Controls.m_lbLoadedAlgorithmName->setText( QStringLiteral("No algorithm selected!")); } else { m_spSelectedTargetNode = m_Controls.imageNodeSelector->GetSelectedNode(); if (m_spSelectedTargetNode.IsNotNull()) { m_spSelectedTargetData = m_spSelectedTargetNode->GetData(); } m_spSelectedTargetMaskNode = m_Controls.maskNodeSelector->GetSelectedNode(); if (m_spSelectedTargetMaskNode.IsNotNull()) { m_spSelectedTargetMaskData = dynamic_cast(m_spSelectedTargetMaskNode->GetData()); } } if (oldTargetNode != m_spSelectedTargetNode) { ConfigureFrameList(); } } mitk::DataStorage::SetOfObjects::Pointer QmitkMatchPointFrameCorrection::GetRegNodes() const { mitk::DataStorage::SetOfObjects::ConstPointer nodes = this->GetDataStorage()->GetAll(); mitk::DataStorage::SetOfObjects::Pointer result = mitk::DataStorage::SetOfObjects::New(); for (mitk::DataStorage::SetOfObjects::const_iterator pos = nodes->begin(); pos != nodes->end(); ++pos) { if (mitk::MITKRegistrationHelper::IsRegNode(*pos)) { result->push_back(*pos); } } return result; } std::string QmitkMatchPointFrameCorrection::GetDefaultJobName() const { mitk::DataStorage::SetOfObjects::ConstPointer nodes = this->GetRegNodes().GetPointer(); mitk::DataStorage::SetOfObjects::ElementIdentifier newIndex = 0; bool isUnique = false; std::string baseName = "corrected #"; if (m_spSelectedTargetNode.IsNotNull()) { baseName = m_spSelectedTargetNode->GetName() + "corrected #"; } std::string result = baseName; while (!isUnique) { ++newIndex; result = baseName + ::map::core::convert::toStr(newIndex); isUnique = this->GetDataStorage()->GetNamedNode(result) == nullptr; } return result; } void QmitkMatchPointFrameCorrection::ConfigureRegistrationControls() { m_Controls.m_tabSelection->setEnabled(!m_Working); m_Controls.m_leRegJobName->setEnabled(!m_Working); m_Controls.m_pbStartReg->setEnabled(false); m_Controls.imageNodeSelector->setEnabled(!m_Working); m_Controls.maskNodeSelector->setEnabled(!m_Working); if (m_LoadedAlgorithm.IsNotNull()) { m_Controls.m_tabSettings->setEnabled(!m_Working); m_Controls.m_tabExclusion->setEnabled(!m_Working); m_Controls.m_tabExecution->setEnabled(true); m_Controls.m_pbStartReg->setEnabled(m_spSelectedTargetNode.IsNotNull() && !m_Working); m_Controls.m_leRegJobName->setEnabled(!m_Working); typedef ::map::algorithm::facet::MaskedRegistrationAlgorithmInterface<3, 3> MaskRegInterface; const MaskRegInterface* pMaskReg = dynamic_cast (m_LoadedAlgorithm.GetPointer()); m_Controls.maskNodeSelector->setVisible(pMaskReg != nullptr); m_Controls.label_TargetMask->setVisible(pMaskReg != nullptr); if (!pMaskReg) { m_Controls.maskNodeSelector->SetCurrentSelection(QmitkSingleNodeSelectionWidget::NodeList()); } this->m_Controls.m_lbLoadedAlgorithmName->setText( QString::fromStdString(m_LoadedAlgorithm->getUID()->toStr())); } else { m_Controls.m_tabSettings->setEnabled(false); m_Controls.m_tabExclusion->setEnabled(false); m_Controls.m_tabExecution->setEnabled(false); this->m_Controls.m_lbLoadedAlgorithmName->setText( QStringLiteral("no algorithm loaded!")); m_Controls.maskNodeSelector->setVisible(false); m_Controls.label_TargetMask->setVisible(false); } if (!m_Working) { this->m_Controls.m_leRegJobName->setText(QString::fromStdString(this->GetDefaultJobName())); } } void QmitkMatchPointFrameCorrection::ConfigureProgressInfos() { const IIterativeAlgorithm* pIterative = dynamic_cast (m_LoadedAlgorithm.GetPointer()); const IMultiResAlgorithm* pMultiRes = dynamic_cast (m_LoadedAlgorithm.GetPointer()); m_Controls.m_progBarIteration->setVisible(pIterative); m_Controls.m_lbProgBarIteration->setVisible(pIterative); if (pIterative) { QString format = "%p% (%v/%m)"; if (!pIterative->hasMaxIterationCount()) { format = "%v"; m_Controls.m_progBarIteration->setMaximum(0); } else { m_Controls.m_progBarIteration->setMaximum(pIterative->getMaxIterations()); } m_Controls.m_progBarIteration->setFormat(format); } if (pMultiRes) { m_Controls.m_progBarLevel->setMaximum(pMultiRes->getResolutionLevels()); } else { m_Controls.m_progBarLevel->setMaximum(1); } m_Controls.m_progBarIteration->reset(); m_Controls.m_progBarLevel->reset(); m_Controls.m_progBarFrame->reset(); } void QmitkMatchPointFrameCorrection::ConfigureFrameList() { m_Controls.m_listFrames->clear(); if (m_spSelectedTargetData.IsNotNull()) { mitk::TimeGeometry::ConstPointer tg = m_spSelectedTargetData->GetTimeGeometry(); for (unsigned int i = 1; i < tg->CountTimeSteps(); ++i) { QString lable = "Timepoint #" + QString::number(i) + QString(" (") + QString::number( tg->GetMinimumTimePoint(i)) + QString(" ms - " + QString::number(tg->GetMaximumTimePoint( i)) + QString(" ms)")); QListWidgetItem* item = new QListWidgetItem(lable, m_Controls.m_listFrames); item->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled); item->setCheckState(Qt::Checked); } } } void QmitkMatchPointFrameCorrection::OnFramesSelectAllPushed() { for (int row = 0; row < m_Controls.m_listFrames->count(); row++) { QListWidgetItem* item = m_Controls.m_listFrames->item(row); item->setCheckState(Qt::Checked); } }; void QmitkMatchPointFrameCorrection::OnFramesDeSelectAllPushed() { for (int row = 0; row < m_Controls.m_listFrames->count(); row++) { QListWidgetItem* item = m_Controls.m_listFrames->item(row); item->setCheckState(Qt::Unchecked); } }; void QmitkMatchPointFrameCorrection::OnFramesInvertPushed() { for (int row = 0; row < m_Controls.m_listFrames->count(); row++) { QListWidgetItem* item = m_Controls.m_listFrames->item(row); if (item->checkState() == Qt::Unchecked) { item->setCheckState(Qt::Checked); } else { item->setCheckState(Qt::Unchecked); } } }; mitk::TimeFramesRegistrationHelper::IgnoreListType QmitkMatchPointFrameCorrection::GenerateIgnoreList() const { mitk::TimeFramesRegistrationHelper::IgnoreListType result; for (int row = 0; row < m_Controls.m_listFrames->count(); row++) { QListWidgetItem* item = m_Controls.m_listFrames->item(row); if (item->checkState() == Qt::Unchecked) { result.push_back(row + 1); } } return result; } void QmitkMatchPointFrameCorrection::OnNodeSelectionChanged(QList /*nodes*/) { if (!m_Working) { ConfigureNodeSelectorPredicates(); CheckInputs(); ConfigureRegistrationControls(); } } void QmitkMatchPointFrameCorrection::OnStartRegBtnPushed() { this->m_Working = true; //////////////////////////////// //configure GUI this->ConfigureProgressInfos(); m_Controls.m_progBarIteration->reset(); m_Controls.m_progBarLevel->reset(); this->ConfigureRegistrationControls(); if (m_Controls.m_checkClearLog->checkState() == Qt::Checked) { this->m_Controls.m_teLog->clear(); } ///////////////////////// //create job and put it into the thread pool QmitkFramesRegistrationJob* pJob = new QmitkFramesRegistrationJob(m_LoadedAlgorithm); pJob->setAutoDelete(true); pJob->m_spTargetData = m_spSelectedTargetData; pJob->m_TargetDataUID = mitk::EnsureUID(this->m_spSelectedTargetNode->GetData()); pJob->m_IgnoreList = this->GenerateIgnoreList(); if (m_spSelectedTargetMaskData.IsNotNull()) { pJob->m_spTargetMask = m_spSelectedTargetMaskData; pJob->m_TargetMaskDataUID = mitk::EnsureUID(this->m_spSelectedTargetMaskNode->GetData()); } pJob->m_MappedName = m_Controls.m_leRegJobName->text().toStdString(); m_Controls.m_mapperSettings->ConfigureJobSettings(pJob); connect(pJob, SIGNAL(Error(QString)), this, SLOT(OnRegJobError(QString))); connect(pJob, SIGNAL(Finished()), this, SLOT(OnRegJobFinished())); connect(pJob, SIGNAL(ResultIsAvailable(mitk::Image::Pointer, const QmitkFramesRegistrationJob*)), this, SLOT(OnMapResultIsAvailable(mitk::Image::Pointer, const QmitkFramesRegistrationJob*)), Qt::BlockingQueuedConnection); connect(pJob, SIGNAL(AlgorithmInfo(QString)), this, SLOT(OnAlgorithmInfo(QString))); connect(pJob, SIGNAL(AlgorithmStatusChanged(QString)), this, SLOT(OnAlgorithmStatusChanged(QString))); connect(pJob, SIGNAL(AlgorithmIterated(QString, bool, unsigned long)), this, SLOT(OnAlgorithmIterated(QString, bool, unsigned long))); connect(pJob, SIGNAL(LevelChanged(QString, bool, unsigned long)), this, SLOT(OnLevelChanged(QString, bool, unsigned long))); connect(pJob, SIGNAL(FrameRegistered(double)), this, SLOT(OnFrameRegistered(double))); connect(pJob, SIGNAL(FrameMapped(double)), this, SLOT(OnFrameMapped(double))); connect(pJob, SIGNAL(FrameProcessed(double)), this, SLOT(OnFrameProcessed(double))); QThreadPool* threadPool = QThreadPool::globalInstance(); threadPool->start(pJob); } void QmitkMatchPointFrameCorrection::OnSaveLogBtnPushed() { QDateTime currentTime = QDateTime::currentDateTime(); QString fileName = tr("registration_log_") + currentTime.toString(tr("yyyy-MM-dd_hh-mm-ss")) + tr(".txt"); fileName = QFileDialog::getSaveFileName(nullptr, tr("Save registration log"), fileName, tr("Text files (*.txt)")); if (fileName.isEmpty()) { QMessageBox::critical(nullptr, tr("No file selected!"), tr("Cannot save registration log file. Please selected a file.")); } else { std::ofstream file; std::ios_base::openmode iOpenFlag = std::ios_base::out | std::ios_base::trunc; file.open(fileName.toStdString().c_str(), iOpenFlag); if (!file.is_open()) { mitkThrow() << "Cannot open or create specified file to save. File path: " << fileName.toStdString(); } file << this->m_Controls.m_teLog->toPlainText().toStdString() << std::endl; file.close(); } } void QmitkMatchPointFrameCorrection::OnRegJobError(QString err) { Error(err); }; void QmitkMatchPointFrameCorrection::OnRegJobFinished() { this->m_Working = false; auto* renderWindowPart = this->GetRenderWindowPart(); if (nullptr != renderWindowPart) renderWindowPart->RequestUpdate(); this->CheckInputs(); this->ConfigureRegistrationControls(); this->ConfigureProgressInfos(); }; void QmitkMatchPointFrameCorrection::OnMapResultIsAvailable(mitk::Image::Pointer spMappedData, const QmitkFramesRegistrationJob* job) { m_Controls.m_teLog->append(QString("Corrected image stored. Name: ") + QString::fromStdString(job->m_MappedName) + QString("")); mitk::DataNode::Pointer spResultNode = mitk::generateMappedResultNode(job->m_MappedName, spMappedData.GetPointer(), "", job->m_TargetDataUID, false, job->m_InterpolatorLabel); this->GetDataStorage()->Add(spResultNode, this->m_spSelectedTargetNode); auto* renderWindowPart = this->GetRenderWindowPart(); if (nullptr != renderWindowPart) renderWindowPart->RequestUpdate(); }; void QmitkMatchPointFrameCorrection::OnMapJobError(QString err) { Error(err); }; void QmitkMatchPointFrameCorrection::OnAlgorithmIterated(QString info, bool hasIterationCount, unsigned long currentIteration) { if (hasIterationCount) { m_Controls.m_progBarIteration->setValue(currentIteration); } m_Controls.m_teLog->append(info); }; void QmitkMatchPointFrameCorrection::OnLevelChanged(QString info, bool hasLevelCount, unsigned long currentLevel) { if (hasLevelCount) { m_Controls.m_progBarLevel->setValue(currentLevel); } m_Controls.m_teLog->append(QString("") + info + QString("")); }; void QmitkMatchPointFrameCorrection::OnAlgorithmStatusChanged(QString info) { m_Controls.m_teLog->append(QString("") + info + QString(" ")); }; void QmitkMatchPointFrameCorrection::OnAlgorithmInfo(QString info) { m_Controls.m_teLog->append(QString("") + info + QString("")); }; void QmitkMatchPointFrameCorrection::OnFrameProcessed(double progress) { m_Controls.m_teLog->append(QStringLiteral("Frame processed...")); m_Controls.m_progBarFrame->setValue(100 * progress); }; void QmitkMatchPointFrameCorrection::OnFrameRegistered(double progress) { m_Controls.m_teLog->append(QStringLiteral("Frame registered...")); m_Controls.m_progBarFrame->setValue(100 * progress); }; void QmitkMatchPointFrameCorrection::OnFrameMapped(double progress) { m_Controls.m_teLog->append(QStringLiteral("Frame mapped...")); m_Controls.m_progBarFrame->setValue(100 * progress); }; void QmitkMatchPointFrameCorrection::OnAlgorithmSelectionChanged(const berry::IWorkbenchPart::Pointer& sourcepart, const berry::ISelection::ConstPointer& selection) { // check for null selection if (selection.IsNull()) { return; } if (sourcepart != this) { UpdateAlgorithmSelection(selection); } } void QmitkMatchPointFrameCorrection::UpdateAlgorithmSelection(berry::ISelection::ConstPointer selection) { mitk::MAPAlgorithmInfoSelection::ConstPointer currentSelection = selection.Cast(); if (currentSelection) { mitk::MAPAlgorithmInfoSelection::AlgorithmInfoVectorType infoVector = currentSelection->GetSelectedAlgorithmInfo(); if (!infoVector.empty()) { // only the first selection is of interest, the rest will be skipped. this->m_SelectedAlgorithmInfo = infoVector[0]; } this->OnSelectedAlgorithmChanged(); } }; diff --git a/Plugins/org.mitk.gui.qt.matchpoint.framereg/src/internal/QmitkMatchPointFrameCorrection.h b/Plugins/org.mitk.gui.qt.matchpoint.framereg/src/internal/QmitkMatchPointFrameCorrection.h index d2edbd4687..1bdf92e044 100644 --- a/Plugins/org.mitk.gui.qt.matchpoint.framereg/src/internal/QmitkMatchPointFrameCorrection.h +++ b/Plugins/org.mitk.gui.qt.matchpoint.framereg/src/internal/QmitkMatchPointFrameCorrection.h @@ -1,191 +1,191 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef QmitkMatchPointFrameCorrection_h #define QmitkMatchPointFrameCorrection_h #include #include #include "ui_QmitkMatchPointFrameCorrectionControls.h" #include // MatchPoint #include #include #include #include #include #include #include #include /*! \brief View for motion artefact correction of images. -The view utalizes MatchPoint registration algorithms and the mitk::TimeFramesRegistrationHelper and implemnts the GUI +The view utalizes MatchPoint registration algorithms and the mitk::TimeFramesRegistrationHelper and implements the GUI business logic to make frame correction aka motion artefact correction on 3D+t images. */ class QmitkMatchPointFrameCorrection : public QmitkAbstractView { // this is needed for all Qt objects that should have a Qt meta-object // (everything that derives from QObject and wants to have signal/slots) Q_OBJECT public: static const std::string VIEW_ID; /** * Creates smartpointer typedefs */ berryObjectMacro(QmitkMatchPointFrameCorrection); QmitkMatchPointFrameCorrection(); ~QmitkMatchPointFrameCorrection() override; protected slots: /** * @brief Connect all GUI elements to its corresponding slots */ virtual void CreateConnections(); void OnLoadAlgorithmButtonPushed(); void OnSelectedAlgorithmChanged(); void OnStartRegBtnPushed(); void OnSaveLogBtnPushed(); void OnFramesSelectAllPushed(); void OnFramesDeSelectAllPushed(); void OnFramesInvertPushed(); void OnRegJobError(QString err); void OnRegJobFinished(); void OnMapJobError(QString err); void OnMapResultIsAvailable(mitk::Image::Pointer spMappedData, const QmitkFramesRegistrationJob* job); void OnAlgorithmIterated(QString info, bool hasIterationCount, unsigned long currentIteration); void OnLevelChanged(QString info, bool hasLevelCount, unsigned long currentLevel); void OnAlgorithmStatusChanged(QString info); void OnAlgorithmInfo(QString info); void OnFrameProcessed(double progress); void OnFrameRegistered(double progress); void OnFrameMapped(double progress); void OnNodeSelectionChanged(QList nodes); protected: void CreateQtPartControl(QWidget* parent) override; void SetFocus() override; private: /** * @brief Adapt the visibility of GUI elements depending on the current data loaded */ void AdaptFolderGUIElements(); void Error(QString msg); /** * checks if appropriated nodes are selected in the data manager. If nodes are selected, * they are stored m_MovingData and m_TargetData.*/ void CheckInputs(); /** * Updates the state of registration control buttons. Regarding to selected * inputs, loaded algorithm and its state.*/ void ConfigureRegistrationControls(); /** Configure the node selectors predicates according to the selected algorithm. */ void ConfigureNodeSelectorPredicates(); /** * Configures the progress bars according to the chosen algorithm. */ void ConfigureProgressInfos(); /**configure the frame list widget based on the selected target.*/ void ConfigureFrameList(); /**generates the ignore list based on the frame list widget selection.*/ mitk::TimeFramesRegistrationHelper::IgnoreListType GenerateIgnoreList() const; /** Methods returns a list of all nodes in the data manager containing a registration wrapper. * The list may be empty.*/ mitk::DataStorage::SetOfObjects::Pointer GetRegNodes() const; /** Returns a proposal for a (unique) default reg job name */ std::string GetDefaultJobName() const; /** Returns the Pointer to the DLL info of the algorithm currently selected by the system. The info is received via m_AlgorithmSelectionListener. @return If there is no item selected the returning pointer will be null. */ const map::deployment::DLLInfo* GetSelectedAlgorithmDLL() const; //! [Qt Selection Listener method and pointer] /** * @brief Method of berry::ISelectionListener that implements the selection listener functionality. * @param sourcepart The workbench part responsible for the selection change. * @param selection This parameter holds the current selection. * * @see ISelectionListener */ void OnAlgorithmSelectionChanged(const berry::IWorkbenchPart::Pointer& sourcepart, const berry::ISelection::ConstPointer& selection); void UpdateAlgorithmSelection(berry::ISelection::ConstPointer selection); friend struct berry::SelectionChangedAdapter; QWidget* m_Parent; /** @brief this pointer holds the algorithm selection listener */ QScopedPointer m_AlgorithmSelectionListener; ::map::deployment::DLLHandle::Pointer m_LoadedDLLHandle; ::map::algorithm::RegistrationAlgorithmBase::Pointer m_LoadedAlgorithm; ::map::deployment::DLLInfo::ConstPointer m_SelectedAlgorithmInfo; typedef map::algorithm::facet::IterativeAlgorithmInterface IIterativeAlgorithm; typedef map::algorithm::facet::MultiResRegistrationAlgorithmInterface IMultiResAlgorithm; typedef map::algorithm::facet::StoppableAlgorithmInterface IStoppableAlgorithm; mitk::DataNode::Pointer m_spSelectedTargetNode; /*Data of the selected target node that should be used for registration. Can be the direct return of node->GetData(), but can also be a sub set (like a special time frame).*/ mitk::BaseData::ConstPointer m_spSelectedTargetData; mitk::DataNode::Pointer m_spSelectedTargetMaskNode; mitk::Image::ConstPointer m_spSelectedTargetMaskData; // boolean variables to control visibility of GUI elements bool m_CanLoadAlgorithm; bool m_Working; Ui::MatchPointFrameCorrectionControls m_Controls; }; #endif diff --git a/Plugins/org.mitk.gui.qt.matchpoint.mapper/src/internal/QmitkMatchPointMapper.cpp b/Plugins/org.mitk.gui.qt.matchpoint.mapper/src/internal/QmitkMatchPointMapper.cpp index fd78a425ed..b129a3289a 100644 --- a/Plugins/org.mitk.gui.qt.matchpoint.mapper/src/internal/QmitkMatchPointMapper.cpp +++ b/Plugins/org.mitk.gui.qt.matchpoint.mapper/src/internal/QmitkMatchPointMapper.cpp @@ -1,559 +1,559 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "org_mitk_gui_qt_matchpoint_mapper_Activator.h" // Blueberry #include #include // Mitk #include #include #include "mitkImageMappingHelper.h" #include "mitkMAPRegistrationWrapper.h" #include "mitkMatchPointPropertyTags.h" #include "mitkRegistrationHelper.h" #include #include #include #include #include #include #include #include // Qmitk #include "QmitkMatchPointMapper.h" // Qt #include #include #include #include const std::string QmitkMatchPointMapper::VIEW_ID = "org.mitk.views.matchpoint.mapper"; QmitkMatchPointMapper::QmitkMatchPointMapper() : m_Parent(nullptr), m_preparedForBinaryInput(false) { } void QmitkMatchPointMapper::SetFocus() { //m_Controls.buttonPerformImageProcessing->setFocus(); } void QmitkMatchPointMapper::CreateConnections() { connect(m_Controls.registrationNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkMatchPointMapper::OnRegNodeSelectionChanged); connect(m_Controls.inputNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkMatchPointMapper::OnInputNodeSelectionChanged); connect(m_Controls.referenceNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkMatchPointMapper::OnReferenceNodeSelectionChanged); connect(m_Controls.m_cbManualRef, SIGNAL(clicked()), this, SLOT(OnManualRefChecked())); connect(m_Controls.m_cbLinkFactors, SIGNAL(clicked()), this, SLOT(OnLinkSampleFactorChecked())); connect(m_Controls.m_sbXFactor, SIGNAL(valueChanged(double)), this, SLOT(OnXFactorChanged(double))); connect(m_Controls.m_pbMap, SIGNAL(clicked()), this, SLOT(OnMapBtnPushed())); connect(m_Controls.m_pbRefine, SIGNAL(clicked()), this, SLOT(OnRefineBtnPushed())); } void QmitkMatchPointMapper::Error(QString msg) { mitk::StatusBar::GetInstance()->DisplayErrorText(msg.toLatin1()); MITK_ERROR << msg.toStdString().c_str(); m_Controls.m_teLog->append(QStringLiteral("") + msg + QStringLiteral("")); } void QmitkMatchPointMapper::CreateQtPartControl(QWidget* parent) { // create GUI widgets from the Qt Designer's .ui file m_Controls.setupUi(parent); m_Parent = parent; this->m_Controls.registrationNodeSelector->SetDataStorage(this->GetDataStorage()); this->m_Controls.registrationNodeSelector->SetSelectionIsOptional(true); this->m_Controls.inputNodeSelector->SetDataStorage(this->GetDataStorage()); this->m_Controls.inputNodeSelector->SetSelectionIsOptional(false); this->m_Controls.referenceNodeSelector->SetDataStorage(this->GetDataStorage()); this->m_Controls.referenceNodeSelector->SetSelectionIsOptional(false); this->m_Controls.registrationNodeSelector->SetInvalidInfo("Select valid registration."); this->m_Controls.registrationNodeSelector->SetEmptyInfo("Assuming identity mapping. Select registration to change."); this->m_Controls.registrationNodeSelector->SetPopUpTitel("Select registration."); this->m_Controls.registrationNodeSelector->SetPopUpHint("Select a registration object that should be used for the mapping of the input data. If no registration is selected, identity will be assumed for the mapping."); this->m_Controls.inputNodeSelector->SetInvalidInfo("Select input data."); this->m_Controls.inputNodeSelector->SetPopUpTitel("Select input data."); this->m_Controls.inputNodeSelector->SetPopUpHint("Select the input data for the mapping. (Images or point sets are supported so far)."); this->m_Controls.referenceNodeSelector->SetInvalidInfo("Select the reference image."); this->m_Controls.referenceNodeSelector->SetPopUpTitel("Select the reference image."); this->m_Controls.referenceNodeSelector->SetPopUpHint("Select the reference image that specifies the target geometry the input should be mapped into."); this->ConfigureRegNodePredicate(); this->ConfigureNodePredicates(); // show first page m_Controls.m_tabs->setCurrentIndex(0); this->CreateConnections(); this->CheckInputs(); this->ConfigureProgressInfos(); this->ConfigureMappingControls(); } /** Method checks if the currently selected reg node has a direct kernel that * can be decomposed in a rotation matrix and a offset. If this is true, true * is returned. In all other cases false is returned.*/ bool QmitkMatchPointMapper::IsAbleToRefineGeometry() const { bool result = false; if (this->m_spSelectedRegNode.IsNotNull()) { const mitk::MAPRegistrationWrapper* wrapper = dynamic_cast (this->m_spSelectedRegNode->GetData()); //if the helper does not return null, we can refine the geometry. result = mitk::MITKRegistrationHelper::getAffineMatrix(wrapper, false).IsNotNull(); } return result; } bool QmitkMatchPointMapper::IsBinaryInput() const { auto maskPredicate = mitk::MITKRegistrationHelper::MaskNodePredicate(); bool result = false; if(this->m_spSelectedInputNode.IsNotNull()) { result = maskPredicate->CheckNode(this->m_spSelectedInputNode); } return result; } bool QmitkMatchPointMapper::IsPointSetInput() const { bool result = false; if (this->m_spSelectedInputNode.IsNotNull()) { result = dynamic_cast(this->m_spSelectedInputNode->GetData()) != nullptr; } return result; } mitk::DataNode::Pointer QmitkMatchPointMapper::GetAutoRefNodeByReg() { mitk::DataNode::Pointer spResult = nullptr; if (this->m_spSelectedRegNode.IsNotNull() && this->m_spSelectedRegNode->GetData()) { std::string nodeName; mitk::BaseProperty* uidProp = m_spSelectedRegNode->GetData()->GetProperty(mitk::Prop_RegAlgTargetData); if (uidProp) { //search for the target node mitk::NodePredicateDataProperty::Pointer predicate = mitk::NodePredicateDataProperty::New(mitk::Prop_UID, uidProp); spResult = this->GetDataStorage()->GetNode(predicate); } } if (spResult.IsNull() && this->m_spSelectedInputNode.IsNotNull()) { //no really reference is available -> use the input as reference spResult = this->m_spSelectedInputNode; if (this->m_spSelectedRefNode != spResult) { m_Controls.m_teLog->append( QStringLiteral("Cannot determine reference automatically. Use input image as reference.")); } } return spResult; } void QmitkMatchPointMapper::ConfigureRegNodePredicate(const mitk::DataNode* input) { mitk::NodePredicateBase::ConstPointer nodePredicate = mitk::MITKRegistrationHelper::RegNodePredicate(); if (input != nullptr) { unsigned int dimension = 0; auto inputImage = dynamic_cast(input->GetData()); auto pointset = dynamic_cast(input->GetData()); if (inputImage) { dimension = inputImage->GetDimension(); if (inputImage->GetTimeSteps() > 1) { //images has multiple time steps -> remove one dimension. dimension -= 1; } } else if (pointset) { dimension = 3; } auto dimCheck = [dimension](const mitk::DataNode * node) { const mitk::MAPRegistrationWrapper* wrapper = dynamic_cast < const mitk::MAPRegistrationWrapper* >(node->GetData()); return wrapper != nullptr && wrapper->GetMovingDimensions() == dimension; }; mitk::NodePredicateFunction::Pointer hasCorrectDim = mitk::NodePredicateFunction::New(dimCheck); nodePredicate = mitk::NodePredicateAnd::New(nodePredicate, hasCorrectDim).GetPointer(); } this->m_Controls.registrationNodeSelector->SetNodePredicate(nodePredicate); } std::function GenerateDimCheckLambda(unsigned int dim) { auto dimCheck = [dim](const mitk::DataNode * node) { auto inputImage = dynamic_cast(node->GetData()); return inputImage != nullptr && (inputImage->GetDimension() == dim || (inputImage->GetDimension() == dim + 1 && inputImage->GetTimeSteps()>1)); }; return dimCheck; } void QmitkMatchPointMapper::ConfigureNodePredicates(const mitk::DataNode* reg) { auto isImage = mitk::MITKRegistrationHelper::ImageNodePredicate(); auto isPointSet = mitk::MITKRegistrationHelper::PointSetNodePredicate(); auto isData = mitk::NodePredicateOr::New(isImage, isPointSet); mitk::NodePredicateBase::ConstPointer inputPredicate = isData.GetPointer(); mitk::NodePredicateBase::ConstPointer refPredicate = isImage.GetPointer(); if (reg != nullptr) { const mitk::MAPRegistrationWrapper* wrapper = dynamic_cast (reg->GetData()); if (wrapper != nullptr) { auto movingDim = wrapper->GetMovingDimensions(); auto dimCheck = GenerateDimCheckLambda(movingDim); auto hasCorrectDim = mitk::NodePredicateFunction::New(dimCheck); if (movingDim == 3) { //Remark: Point sets are always 3D auto is3DInput = mitk::NodePredicateOr::New(isPointSet, mitk::NodePredicateAnd::New(isImage, hasCorrectDim)); inputPredicate = is3DInput.GetPointer(); } else { auto is2DInput = mitk::NodePredicateAnd::New(isImage, hasCorrectDim); inputPredicate = is2DInput.GetPointer(); } auto targetDim = wrapper->GetTargetDimensions(); auto targetDimCheck = GenerateDimCheckLambda(targetDim); auto hasCorrectTargetDim = mitk::NodePredicateFunction::New(targetDimCheck); auto isRef = mitk::NodePredicateAnd::New(isImage, hasCorrectTargetDim); refPredicate = isRef; } } this->m_Controls.inputNodeSelector->SetNodePredicate(inputPredicate); this->m_Controls.referenceNodeSelector->SetNodePredicate(refPredicate); } void QmitkMatchPointMapper::CheckInputs() { this->m_spSelectedRegNode = this->m_Controls.registrationNodeSelector->GetSelectedNode(); this->m_spSelectedInputNode = this->m_Controls.inputNodeSelector->GetSelectedNode(); this->m_spSelectedRefNode = this->m_Controls.referenceNodeSelector->GetSelectedNode(); if (!(m_Controls.m_cbManualRef->isChecked())) { auto autoRefNode = this->GetAutoRefNodeByReg(); if (this->m_spSelectedRefNode != autoRefNode) { this->m_spSelectedRefNode = autoRefNode; QmitkSingleNodeSelectionWidget::NodeList selection; if (this->m_spSelectedRefNode.IsNotNull()) { selection.append(this->m_spSelectedRefNode); } this->m_Controls.referenceNodeSelector->SetCurrentSelection(selection); } } if (this->m_spSelectedRefNode.IsNotNull() && this->m_spSelectedRefNode->GetData() && this->m_spSelectedRefNode->GetData()->GetTimeSteps() > 1) { m_Controls.m_teLog->append( QStringLiteral("Selected reference image has multiple time steps. Only geometry of time step 1 is used as reference.")); } } void QmitkMatchPointMapper::ConfigureMappingControls() { bool validInput = m_spSelectedInputNode.IsNotNull(); bool validRef = m_spSelectedRefNode.IsNotNull(); this->m_Controls.referenceNodeSelector->setEnabled(this->m_Controls.m_cbManualRef->isChecked()); this->m_Controls.m_pbMap->setEnabled(validInput && validRef); this->m_Controls.m_pbRefine->setEnabled(validInput && this->IsAbleToRefineGeometry() && !this->IsPointSetInput()); if (validInput) { if (m_spSelectedRegNode.IsNotNull()) { this->m_Controls.m_leMappedName->setText(tr("mapped_") + QString::fromStdString(m_spSelectedInputNode->GetName()) + tr("_by_") + QString::fromStdString(m_spSelectedRegNode->GetName())); } else { this->m_Controls.m_leMappedName->setText(tr("resampled_") + QString::fromStdString(m_spSelectedInputNode->GetName())); } } else { this->m_Controls.m_leMappedName->setText(tr("mappedData")); } if (this->IsBinaryInput() != this->m_preparedForBinaryInput) { if (this->IsBinaryInput()) { m_Controls.m_teLog->append( QStringLiteral("Binary input (mask) detected. Preparing for mask mapping (default interpolation: nearest neighbour; padding value: 0)")); this->m_Controls.m_comboInterpolator->setCurrentIndex(0); this->m_Controls.m_sbErrorValue->setValue(0); this->m_Controls.m_sbPaddingValue->setValue(0); } else { this->m_Controls.m_comboInterpolator->setCurrentIndex(1); } this->m_preparedForBinaryInput = this->IsBinaryInput(); } OnLinkSampleFactorChecked(); } void QmitkMatchPointMapper::ConfigureProgressInfos() { } void QmitkMatchPointMapper::OnRegNodeSelectionChanged(QList nodes) { mitk::DataNode::Pointer regNode; if (!nodes.isEmpty()) { regNode = nodes.front(); } this->ConfigureNodePredicates(regNode); this->CheckInputs(); this->ConfigureMappingControls(); } void QmitkMatchPointMapper::OnInputNodeSelectionChanged(QList nodes) { mitk::DataNode::Pointer inputNode; if (!nodes.isEmpty()) { inputNode = nodes.front(); } this->ConfigureRegNodePredicate(inputNode); this->CheckInputs(); this->ConfigureMappingControls(); } void QmitkMatchPointMapper::OnReferenceNodeSelectionChanged(QList /*nodes*/) { this->CheckInputs(); this->ConfigureMappingControls(); } void QmitkMatchPointMapper::OnManualRefChecked() { this->CheckInputs(); this->ConfigureMappingControls(); } void QmitkMatchPointMapper::OnLinkSampleFactorChecked() { this->m_Controls.m_sbYFactor->setEnabled(!(this->m_Controls.m_cbLinkFactors->isChecked())); this->m_Controls.m_sbZFactor->setEnabled(!(this->m_Controls.m_cbLinkFactors->isChecked())); if (m_Controls.m_cbLinkFactors->isChecked()) { this->m_Controls.m_sbYFactor->setValue(this->m_Controls.m_sbXFactor->value()); this->m_Controls.m_sbZFactor->setValue(this->m_Controls.m_sbXFactor->value()); } } void QmitkMatchPointMapper::OnMapBtnPushed() { SpawnMappingJob(); } void QmitkMatchPointMapper::OnRefineBtnPushed() { SpawnMappingJob(true); } void QmitkMatchPointMapper::SpawnMappingJob(bool doGeometryRefinement) { if (m_Controls.m_checkClearLog->checkState() == Qt::Checked) { this->m_Controls.m_teLog->clear(); } ///////////////////////// //create job and put it into the thread pool QmitkMappingJob* pJob = new QmitkMappingJob(); pJob->setAutoDelete(true); pJob->m_spInputData = this->m_spSelectedInputNode->GetData(); pJob->m_InputDataUID = mitk::EnsureUID(this->m_spSelectedInputNode->GetData()); pJob->m_doGeometryRefinement = doGeometryRefinement; pJob->m_spRegNode = m_spSelectedRegNode; if (m_spSelectedRegNode.IsNull()) { pJob->m_spRegNode = mitk::DataNode::New(); pJob->m_spRegNode->SetData(mitk::GenerateIdentityRegistration3D().GetPointer()); pJob->m_spRegNode->SetName("Auto_Generated_Identity_Transform"); m_Controls.m_teLog->append( - QStringLiteral("No registration selected. Preforming mapping with identity transform")); + QStringLiteral("No registration selected. Performing mapping with identity transform")); } if (!doGeometryRefinement) { pJob->m_spRefGeometry = m_spSelectedRefNode->GetData()->GetGeometry()->Clone().GetPointer(); //check for super/sub sampling if (m_Controls.m_groupActivateSampling->isChecked()) { pJob->m_spRefGeometry = mitk::ImageMappingHelper::GenerateSuperSampledGeometry(pJob->m_spRefGeometry, m_Controls.m_sbXFactor->value(), m_Controls.m_sbYFactor->value(), m_Controls.m_sbZFactor->value()); } } pJob->m_MappedName = m_Controls.m_leMappedName->text().toStdString(); pJob->m_allowUndefPixels = m_Controls.m_groupAllowUndefPixels->isChecked(); pJob->m_paddingValue = m_Controls.m_sbPaddingValue->value(); pJob->m_allowUnregPixels = m_Controls.m_groupAllowUnregPixels->isChecked(); pJob->m_errorValue = m_Controls.m_sbErrorValue->value(); pJob->m_InterpolatorLabel = m_Controls.m_comboInterpolator->currentText().toStdString(); switch (m_Controls.m_comboInterpolator->currentIndex()) { case 0: pJob->m_InterpolatorType = mitk::ImageMappingInterpolator::NearestNeighbor; break; case 1: pJob->m_InterpolatorType = mitk::ImageMappingInterpolator::Linear; break; case 2: pJob->m_InterpolatorType = mitk::ImageMappingInterpolator::BSpline_3; break; case 3: pJob->m_InterpolatorType = mitk::ImageMappingInterpolator::WSinc_Hamming; break; case 4: pJob->m_InterpolatorType = mitk::ImageMappingInterpolator::WSinc_Welch; break; } connect(pJob, SIGNAL(Error(QString)), this, SLOT(OnMapJobError(QString))); connect(pJob, SIGNAL(MapResultIsAvailable(mitk::BaseData::Pointer, const QmitkMappingJob*)), this, SLOT(OnMapResultIsAvailable(mitk::BaseData::Pointer, const QmitkMappingJob*)), Qt::BlockingQueuedConnection); connect(pJob, SIGNAL(AlgorithmInfo(QString)), this, SLOT(OnMappingInfo(QString))); m_Controls.m_teLog->append(QStringLiteral("Started mapping job. Name: ") + m_Controls.m_leMappedName->text() + QStringLiteral("")); QThreadPool* threadPool = QThreadPool::globalInstance(); threadPool->start(pJob); } void QmitkMatchPointMapper::OnMapJobError(QString err) { Error(err); } void QmitkMatchPointMapper::OnMapResultIsAvailable(mitk::BaseData::Pointer spMappedData, const QmitkMappingJob* job) { m_Controls.m_teLog->append(QStringLiteral("Mapped entity stored. Name: ") + QString::fromStdString(job->m_MappedName) + QStringLiteral("")); mitk::DataNode::Pointer spMappedNode = mitk::generateMappedResultNode(job->m_MappedName, spMappedData, job->GetRegistration()->getRegistrationUID(), job->m_InputDataUID, job->m_doGeometryRefinement, job->m_InterpolatorLabel); this->GetDataStorage()->Add(spMappedNode); auto* renderWindowPart = this->GetRenderWindowPart(); if (nullptr != renderWindowPart) renderWindowPart->RequestUpdate(); this->CheckInputs(); this->ConfigureMappingControls(); } void QmitkMatchPointMapper::OnMappingInfo(QString info) { m_Controls.m_teLog->append(QStringLiteral("") + info + QStringLiteral("")); } void QmitkMatchPointMapper::OnXFactorChanged(double d) { if (m_Controls.m_cbLinkFactors->isChecked()) { this->m_Controls.m_sbYFactor->setValue(d); this->m_Controls.m_sbZFactor->setValue(d); } } diff --git a/Plugins/org.mitk.gui.qt.matchpoint.visualizer/documentation/UserManual/map_view_visualizer_example.svg b/Plugins/org.mitk.gui.qt.matchpoint.visualizer/documentation/UserManual/map_view_visualizer_example.svg index 92c711068f..8feea4588c 100644 --- a/Plugins/org.mitk.gui.qt.matchpoint.visualizer/documentation/UserManual/map_view_visualizer_example.svg +++ b/Plugins/org.mitk.gui.qt.matchpoint.visualizer/documentation/UserManual/map_view_visualizer_example.svg @@ -1,399 +1,399 @@ image/svg+xml (1) Registration slot (2) Registation information + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:21.33333397px;font-family:Arial;-inkscape-font-specification:Arial;fill:#ff0000">(2) Registration information (3) Update visual settings (4) Selection of mapping direction (5) Selection of visualization style diff --git a/Plugins/org.mitk.gui.qt.matchpoint.visualizer/src/internal/QmitkMatchPointRegistrationVisualizer.ui b/Plugins/org.mitk.gui.qt.matchpoint.visualizer/src/internal/QmitkMatchPointRegistrationVisualizer.ui index 4dc8b4a05e..7cf2d36e25 100644 --- a/Plugins/org.mitk.gui.qt.matchpoint.visualizer/src/internal/QmitkMatchPointRegistrationVisualizer.ui +++ b/Plugins/org.mitk.gui.qt.matchpoint.visualizer/src/internal/QmitkMatchPointRegistrationVisualizer.ui @@ -1,1570 +1,1570 @@ MatchPointRegVisControls 0 0 332 738 5 5 5 5 5 255 0 0 255 0 0 120 120 120 Warning: This plugin is in an experimental state! Registration: 3 0 40 Info: 0 0 0 75 16777215 130 true 0 0 false 5 5 5 5 5 <html><head/><body><p>Updates the visualization with the current settings.</p></body></html> Update visualization false Show mapping direction: <html><head/><body><p>Select the direction/kernel of the registration you want to visualize:</p><p>- &quot;direct&quot;: select to show the direct mapping kernel (used for continuous spaced data like point sets).</p><p>- &quot;inverse&quot;: select to show the inverse mapping kernel (used for discrete spaced data like voxel images).</p><p><br/></p><p>If the selected registration does not support a direction, you cannot select it.</p></body></html> Direct Inverse 0 0 0 30 5 0 5 0 5 <html><head/><body><p>Select to visualize registration via grid(s).</p></body></html> Grid true true true true Select to visualize registration via glyphs. Glyph true true false Select to visualize registration via a pointset selected from the data manager... Points true true 2 Visualization 5 5 5 Color style: uni color: false 60 0 false Qt::Horizontal 40 20 vector magnitude: Color coding: 0 0 7 3 Qt::Horizontal 40 20 Qt::Horizontal 40 20 Qt::Horizontal 40 20 Qt::Horizontal 40 20 mm small: medium: 30 16777215 > Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 999.000000000000000 0.100000000000000 999.000000000000000 large: 30 16777215 > Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter mm 999.000000000000000 mm 30 16777215 > Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter negligible: 50 0 40 16777215 50 0 40 16777215 50 0 40 16777215 50 0 40 16777215 interpolate colors Qt::Vertical 20 40 true Grid <html><head/><body><p>General grid visualization settings.</p></body></html> 5 5 5 5 5 QFormLayout::AllNonFixedFieldsGrow 5 5 Grid frequency: <html><head/><body><p>Indicate the frequency of visible gridlines used for visualization.</p><p>e.g. 1: every line of the grid is visible; 2: only every second line is visible ...</p></body></html> 200 50 false <html><head/><body><p>Allows that pixels may not be defined in the mapped image because they are outside of the field of view of the used input image.</p><p>The pixels will be marked with the given padding value.</p><p>If unchecked the mapping will be aborted in a case of undefined pixels.</p></body></html> Show start grid false true false 11 5 11 5 Color 60 0 false Qt::Horizontal 40 20 Qt::Vertical QSizePolicy::Expanding 20 300 FOV - <html><head/><body><p>Field of view settings (FOV) for vizualizing the grid correctly (its origin, size and orientation).</p></body></html> + <html><head/><body><p>Field of view settings (FOV) for visualizing the grid correctly (its origin, size and orientation).</p></body></html> 5 5 5 5 5 Size (in mm): 0 0 0 x: 0 0 10 0 50 16777215 <html><head/><body><p>Indicate the size (in mm) of the grid in the x direction.</p></body></html> 2 0.010000000000000 9999.000000000000000 y: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 0 0 10 0 50 16777215 <html><head/><body><p>Indicate the size (in mm) of the grid in the y direction.</p></body></html> 2 0.010000000000000 9999.000000000000000 z: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 0 0 10 0 50 16777215 <html><head/><body><p>Indicate the size (in mm) of the grid in the z direction.</p></body></html> 2 0.010000000000000 9999.000000000000000 Origin (in mm): 0 0 0 x: 0 0 10 0 50 16777215 <html><head/><body><p>Indicate the origin (in mm) of the grid in the x direction.</p></body></html> -99999.000000000000000 99999.000000000000000 y: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 0 0 10 0 50 16777215 <html><head/><body><p>Indicate the origin (in mm) of the grid in the y direction.</p></body></html> -99999.000000000000000 99999.000000000000000 z: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 0 0 10 0 50 16777215 <html><head/><body><p>Indicate the origin (in mm) of the grid in the z direction.</p></body></html> -99999.000000000000000 99999.000000000000000 0.000000000000000 Spacing (in mm): 0 0 0 x: 0 0 10 0 50 16777215 <html><head/><body><p>Indicate the spacing/resolution of the grid in the x direction.</p></body></html> 0.010000000000000 9999.000000000000000 0.010000000000000 y: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 0 0 10 0 50 16777215 <html><head/><body><p>Indicate the spacing/resolution of the grid in the y direction.</p></body></html> 0.010000000000000 9999.000000000000000 0.010000000000000 z: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 0 0 10 0 50 16777215 <html><head/><body><p>Indicate the spacing/resolution of the grid in the z direction.</p></body></html> 0.010000000000000 9999.000000000000000 0.010000000000000 Orientation: true 0 0 160 50 7 false false false false false 20 50 false 20 R1 R2 R3 C1 C2 C3 0 ItemIsEnabled 0 ItemIsEnabled 0 ItemIsEnabled 0 ItemIsEnabled 0 ItemIsEnabled 0 ItemIsEnabled 0 ItemIsEnabled 0 ItemIsEnabled 0 ItemIsEnabled FOV Reference: 3 0 40 size true origin true spacing true orientation true Qt::Vertical 20 40 Qt::Vertical 20 40 QmitkSingleNodeSelectionWidget QWidget
QmitkSingleNodeSelectionWidget.h
1
ctkColorPickerButton QPushButton
ctkColorPickerButton.h
5 5 true true true
diff --git a/Plugins/org.mitk.gui.qt.mxnmultiwidgeteditor/documentation/UserManual/QmitkMxNMultiWidgetEditor.dox b/Plugins/org.mitk.gui.qt.mxnmultiwidgeteditor/documentation/UserManual/QmitkMxNMultiWidgetEditor.dox index f826235ca1..d6b5c15000 100644 --- a/Plugins/org.mitk.gui.qt.mxnmultiwidgeteditor/documentation/UserManual/QmitkMxNMultiWidgetEditor.dox +++ b/Plugins/org.mitk.gui.qt.mxnmultiwidgeteditor/documentation/UserManual/QmitkMxNMultiWidgetEditor.dox @@ -1,150 +1,150 @@ /** \page org_mitk_editors_mxnmultiwidget The MxN Display \imageMacro{QmitkMxNMultiWidgetEditor.svg,"Icon of the MxN Display",16.00} \tableofcontents \section org_mitk_editors_mxnmultiwidget_F1Help General remark on F1 help This is the help window of the MxN display. To display the help windows of other plugins, press F1 when the plugin of interest is active (please make sure the tab of the plugin of interest is highlighted). Alternatively, go to the Help menu and select Open Help Perspective. A help window opens from which you can select the documentation of the plugin of interest. \section org_mitk_editors_mxnmultiwidget_Overview Overview The MxN display is an alternative image viewer to the Standard display. It is a more flexible, customizable approach to displaying images with a strong focus on displaying different images simultaneously but independently. Unlike than the Standard Display it allows to show a different image for each window view while also allowing independent image navigation. The number of window views can be freely chosen and anatomical view planes / view directions can be changed for each window view. \section org_mitk_editors_mxnmultiwidget_Working Working with the MxN display The following sections describe how to work with the MxN display. The MxN display can be customized and several properties can be set to modify the behavior. \subsection org_mitk_editors_mxnmultiwidget_Configuration Configuration Toolbar The MxNDisplay is initialized with a single window view and a configuration toolbar on the right side. In the toolbar you can find three icons / actions, where two buttons have an on/off state.
\imageMacro{QtWidgets/resource/mwLayout.png, "Layout button", 16.00}
\imageMacro{QtWidgets/resource/mwSynchronized.png, "Synchronize button", 16.00} \imageMacro{QtWidgets/resource/mwDesynchronized.png, "Desynchronize button", 16.00}
\imageMacro{QtWidgets/resource/mwMITK.png, "MITK mode button", 16.00} \imageMacro{QtWidgets/resource/mwPACS.png, "PACS mode button", 16.00}
  • Layout button: Choose a layout from presets or select a custom number of available window views and their layout in a grid
  • Synchronization button: Enable or disable synchronized interaction across the window views -
  • Interaction mode button: Switch between the MITK interaction mouse mode and the PACS interation mouse mode +
  • Interaction mode button: Switch between the MITK interaction mouse mode and the PACS interaction mouse mode
\subsubsection org_mitk_editors_mxnmultiwidget_Layout MxN display layout There are several options to customize the MxN display layout. Using the layout button from the configuration toolbar allows to customize the layout the following way:
  • By choosing the desired amount of windows and confirming via "Set multi widget layout". The window views are arranged in a rectangular grid, as specified. Depending on the selected number of window views, existing window views will be removed or additional window views will be added. New window views will have a sagittal view direction. Window views that stay untouched will keep their settings and continue displaying their selected data.
  • By selecting one of the provided layout presets.
  • By saving / loading a custom layout, either created using the previous methods and further refinement or by handcrafting a suitable file.
\imageMacro{layoutWidget.png, "Layout widget" , 16.00} By moving the cursor to the upper right corner of any window you can activate the window menu. It consists of three buttons:
  • The crosshair button allows to toggle the crosshair and to reset the view.
  • The middle button expands the corresponding window to fullscreen.
  • The right button allows you to choose between many different arrangements of the available windows.
\imageMacro{layoutArrangements.png, "Layout arrangement", 16.00} \subsubsection org_mitk_editors_mxnmultiwidget_Synchronization Window synchronization If data is displayed inside a window view you can interact with the window / loaded image using your mouse. As long as the interaction synchronization is disabled (), mouse interactions, such as zooming, setting the point of interest, mouse wheel scrolling etc. will affect only the window view you click in. If the interaction synchronization is enabled (), performing any of the mouse interactions inside a window view will perform the same interaction in any other visible window view. \subsubsection org_mitk_editors_mxnmultiwidget_Interaction_Mode Mouse interaction mode As mentioned before, you can use the mouse to interact with each window / loaded image. There are two interaction modes which can be switched between using the interaction mode button: the MITK interaction mouse mode and the PACS interaction mouse mode. If the PACS interaction mouse mode is selected, an interaction tool bar is added to the left side of the MxN display: It allows to select the action to perform for the left mouse button. The difference between the MITK and PACS mode is as follows: MITK interaction mouse mode
  • left mouse button: setting the point of interest
  • right mouse button: zooming in and out while moving the mouse
  • middle mouse button / pressing mouse wheel: moving the image while moving the mouse
  • mouse wheel: scrolling through the displayed image slices
PACS interaction mouse mode
  • left mouse button: depending on the selected mode in the interaction tool bar
  • right mouse button: changing the level window for the topmost visible image under the mouse cursor
  • middle mouse button / clicking mouse wheel: no interaction
  • mouse wheel: scrolling through the displayed image slices
  • strg-key + right mouse button: zooming in and out while moving the mouse
  • shif-key + right mouse button: moving the image while moving the mouse
\subsection org_mitk_editors_mxnmultiwidget_Utility Utility Menu Each window view has a utility menu at the top with four UI elements that allow to perform the following actions:
  • Data selection button: Select which data should be selected and visible inside the window view
  • Lock / unlock button: Set the lock state of a window to couple / decouple the window from other windows.
  • Slice slider: Navigate along the view direction of this window
  • View direction drop down menu: Select the current anatomical view plane / view direction
\imageMacro{utilityMenu.png, "Utility menu", 8.00} \subsubsection org_mitk_editors_mxnmultiwidget_Data_Selection Data node selection Using the data selection button of a window view you can open a data node selection dialog which shows the currently selected data nodes for the window view. "Currently selected" means that you can either decide to show all available data nodes from the data storage or - for a better data node overview - only show an individually selected subset of the available data nodes. You can either remove a node from the selection by clicking the "x" on a data node or use the "Change selection" button to individually select data nodes. The "Select all nodes" checkbox denotes which of the selection types is currently used - showing all available data nodes or showing only a subset of data nodes. Checking the checkbox will reset the selection to all available data nodes. Selecting data nodes does not mean that this node always has to be visible in the window view (rendered). If you want to hide a node, e.g. for temporarily inspecting underlying images, you can do so by clicking the "eye" icon for each selected data node. If you want to reset the window view to a specific node / image, you can use the "arrow" icon. This will reset the camera to focus on the selected node, using default zoom and centered camera position. If you want so reset the camera to focus on the whole scene / all selected nodes, you can use the window menu, see \ref org_mitk_editors_mxnmultiwidget_Layout. Resetting a window only affects the corresponding window view, regardless of any synchronization / lock state. \subsubsection org_mitk_editors_mxnmultiwidget_Lock_Button Lock button So far showing / hiding nodes or removing them from a selection / changing a selection affected all window views. If you want to use individual selections for a window view, you can unlock / decouple the window view from other window views by clicking on the lock button in the utility menu. This will decouple the current window view. Changing the visibility of a node or selecting individual nodes / removing nodes using the data node selection button will affect only the current window view. This allows to show different images in each window view, e.g. by decoupling multiple window views and selecting individual nodes in each window. It is still possible to have a synchronized mouse interaction, see \ref org_mitk_editors_mxnmultiwidget_Synchronization and \ref org_mitk_editors_mxnmultiwidget_Interaction_Mode. \subsubsection org_mitk_editors_mxnmultiwidget_Slice_Slider Slice selection slider Displayed data is typically 3D data but each window view shows a 2D slice of the 3D volume. The direction in which the 3D volume is sliced is defined by the "view direction" (or "anatomical plane"). To define at which position in the view direction the 3D volume should be sliced, the slice selection slider can be used. It can either be moved using the mouse or by using the appropriate mouse interaction (see above, e.g. mouse wheel scroling). \subsubsection org_mitk_editors_mxnmultiwidget_View_Direction View direction - Each window view "is looking in" one of the three avaiable view directions, namely "Axial", "Sagittal" or "Coronal". + Each window view "is looking in" one of the three available view directions, namely "Axial", "Sagittal" or "Coronal". You can change the view direction by switching to another anatomical plane in the view direction drop down menu. Changing the view direction will reset the camera to its default for the new view direction, meaning default zoom and centered camera position. */ diff --git a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation.dox b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation.dox index efe9fff1a1..1eb15e7f8b 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation.dox +++ b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation.dox @@ -1,494 +1,494 @@ /** \page org_mitk_views_segmentation The Segmentation View \imageMacro{segmentation-dox.svg,"Icon of the Segmentation View",2.00} \tableofcontents \section org_mitk_views_segmentationoverview Overview Segmentation is the act of separating an image into foreground and background subsets by manual or automated delineation, while the foreground is defined to be part of the segmentation. Such a segmented image subset is also called a label as it typically labels a specific region of interest. A multilabel segmentation may contain multiple labels organized in distinct groups. You can create multiple labels for different regions of interest contained within a single segmentation image. Labels in the same group cannot overlap each other but labels from different groups may overlap. The MITK Segmentation Plugin allows you to create multilabel segmentations of anatomical and pathological structures in medical images. The plugin consists of three views:
  • Segmentation View: Manual and (semi-)automatic segmentation
  • \subpage org_mitk_views_segmentationutilities : Post-processing of segmentations
  • \subpage org_mitk_views_segmentationtasklist : Optimized workflow for batches of segmentation tasks based on a user-defined task list
In this user guide, the features of the Segmentation View are described. For an introduction to the Segmentation Utilities or Segmentation Task List, refer to the respective user guides. \imageMacro{QmitkSegmentationPlugin_Overview.png,"Segmentation View", 16.00} \section org_mitk_views_segmentationtechnicalissues Image and segmentation prerequisites The Segmentation View has a few prerequisites regarding segmentations and their reference image:
  • Images must be two or three-dimensional and may be either static or dynamic, e.g., are time-resolved resp. have different pixel values for different time steps.
  • Images must be single-valued, i.e. CT, MRI or ultrasound. Images from color doppler or photographic (RGB) images are only partially supported (please be aware that some tools might not be compatible with this image type).
  • Segmentations must be congruent to their reference images.
\section org_mitk_views_segmentationdataselection Image selection and creating new segmentations To select a reference image for a new segmentation, click on the Image widget in the Data selection section at the very top of the Segmentation View. Choose an image from the displayed list of Data Manager images. Once an image is selected, a new segmentation for this reference image can be created by clicking the button right next to the Segmentation widget in the Data selection section. A new multilabel segmentation with an initial, empty label is automatically generated if not set otherwise in the preferences. The new segmentation will be added to the Data Manager as a child node of its reference image node. It is automatically selected and can be edited in the Segmentation View right away. Instead of creating a new segmentation, an existing segmentation can be selected and edited as well. The selection list of existing segmentations for a certain reference image consists of matching/congruent segmentations only. \imageMacro{"QmitkSegmentation_DataSelection.png","Data selection and creating new segmentations",12} \section org_mitk_views_segmentationgroups Groups Segmentation images consist of at least a single group called "Group 0" in which the first default label is created. More groups can be added and removed but there will always be at least a single group. Labels of the same group cannot overlap each other. Labels of different groups may overlap each other. For example, you could segment the whole heart as "Heart" label in "Group 0", add "Group 1" and create multiple labels of the anatomical details of the heart in that group. Naturally, all these labels lie within the extents of the "Heart" label of "Group 0" but in principle they are completely independent of "Group 0". Some pixels are now labelled twice, e.g., as "Heart" and "Left ventricle". Since the labels of "Group 1" cannot overlap each other, it is impossible to accidentally label a pixel as both "Left ventricle" and "Right ventricle". If you would like to segment even more details you could create "Group 2" to have up to three labels per pixel. Nevertheless, groups are technically a flat data structure and cannot contain nested groups. It is all about possibly overlapping labels from distinct groups and spatially exclusive, non-overlapping labels within the same group. \imageMacro{"QmitkSegmentation_Groups.png","Groups",10} \section org_mitk_views_segmentationlabelinstances Labels vs. label instances The Segmentation View supports label instances. That is, segmenting multiple distributed entities of the same thing like metastases for example. A label, as we used the term before, is already a single instance of itself but it may consist of multiple label instances. If a label consists of multiple label instances, they each show their own distinct pixel value in square brackets as a clue for distinction and identification. -It is important to understand that this number is not a separate, consequtive index for each label. +It is important to understand that this number is not a separate, consecutive index for each label. It is just the plain pixel value of the label instance, which is unique across all label instances of the whole segmentation. \imageMacro{"QmitkSegmentation_LabelInstances.png","Label instances",10} \section org_mitk_views_segmentationlock_color_visibility Unlocking, changing color of, and hiding label instances Label instances are locked by default: label instances from the same group cannot accidentally override pixels from other label instances. Locked label instances behave like cookie cutters for other label instances of the same group. You can unlock label instances to remove that protection from other label instances of the same group. Their pixel contents can then be overridden by other label instances of the same group. Remember that label instances from distinct groups do not interact with each other. They can always overlap (not override) each other. You can also change the color of label instances as well as show (default) or hide their pixel contents. The icons at the right side of the rows of the groups and labels widget reflect their state in all these regards. Renaming of labels and label instances can be found in their content menu as shown further below. \imageMacro{"QmitkSegmentation_LockColorVisibility.png","Unlocking\, changing color of\, and hiding label instances",10} \section org_mitk_views_segmentationlabelhighlighting Label highlighting Especially if you have segmentations with many label instances or the label names are not telling, it can be nontrivial to identify the label instance in the label inspector of the segmentation view. To mitigate this problem MITK uses label highlighting in the render windows. As long as you hover with the mouse cursor over a group, label or label instance, the respective label instances will be highlighted in the render windows. Highlighted labels will visually pop out by being shown with full opacity (no transparency) while the opacity of all non-highlighted labels of the same segmentation will be reduced to 30% of the current opacity value (they become very transparent). By default, label instances that are set to be invisible are not shown while highlighted. By pressing the shift key, one can enforce also invisible label instances to be shown while highlighting. Remark: the highlighting is supported in all views that use the label inspector (e.g. also the segmentation utilities). \section org_mitk_views_segmentationcontextmenus Context menus Actions for organization of groups, labels, and label instances (as well as other operations) can be also found in their right-click context menus. \imageMacro{"QmitkSegmentation_ContextMenus.png","Context menus of groups\, labels\, and label instances",12} Most actions available in these context menus are self-explanatory or were already described above by other means of access like the tool button bar for adding and removing groups, labels, and label instances. Labels and label instances can be renamed, while groups have fixed names. Note that renaming a label instance will make a separate label out of it, since all instances of the same label share a single common name. Clear content only clears the pixels of a label instance but won't delete the actual label instance. -Groups can be locked and unlocked as a whole from their context menu, while label instances can be directly locked and unlocked outside the context menu as decribed further below. +Groups can be locked and unlocked as a whole from their context menu, while label instances can be directly locked and unlocked outside the context menu as described further below. \section org_mitk_views_segmentationlabelsuggestions Label name and color suggestions When renaming label instances or creating new label instances with enforced manual naming in the Segmentation preferences, entering names is supported by auto-completion for common label names. The list of predefined label names and colors for the auto-completion feature can be either extended or replaced by a custom list of label name and color suggestions. This custom list must be specified as a JSON file, just containing an array of objects, each with a mandatory "name" string and an optional "color" string. The JSON file can be set in the Segmentation preferences as well as a few options on how to apply these suggestions. \section org_mitk_views_segmentationlabelpresets Saving and loading label set presets Label set presets are useful to share a certain style or scheme between different segmentation sessions or to provide templates for new segmentation sessions. The properties of all label instances in all groups like their names, colors, and visibilities are saved as a label set preset by clicking on the 'Save label set preset' button. Label set presets are applied to any segmentation session by clicking on the 'Load label set preset' button. If a label instance for a certain value already exists, its properties are overridden by the preset. If a label instance for a certain value does not yet exist, an empty label instance with the label properties of the preset is created. The actual pixel contents of label instances are unaffected as label set presets only store label properties. \imageMacro{QmitkSegmentation_Preset.png,"Saving and loading label set presets", 10.00} If you work on a repetitive segmentation task, manually loading the same label set preset for each and every new segmentation can be tedious. To streamline your workflow, you can set a default label set preset in the Segmentation preferences (Ctrl+P). When set, this label set preset will be applied to all new segmentations instead of creating the default red "Label 1" label instance. If you work on a repetitive segmentation task, manually loading the same label set preset for each and every new segmentation can be tedious. To streamline your workflow, you can set a default label set preset in the Segmentation preferences (Ctrl+P). When set, this label set preset will be applied to all new segmentations instead of creating the default red "Label 1" label instance. \section org_mitk_views_segmentationpreferences Preferences The Segmentation Plugin offers a number of preferences which can be set via the MITK Workbench application preferences (Ctrl+P): \imageMacro{QmitkSegmentationPreferences.png,"Segmentation preferences", 10.00}
  • Compact view: Hide the tool button texts to save some space on screen (6 instead of 4 buttons per row)
  • 2D display: Draw segmentations as as outlines or transparent overlays
  • Data node selection mode: Hide everything but the selected segmentation and its reference image
  • Default label set preset: Start a new segmentation with this preset instead of a default label
  • Label creation: Assign default names and colors to new label instances or ask users for name and color
  • Label suggestions: Specify custom suggestions for label names and colors
\section org_mitk_views_segmentationtooloverview Segmentation tool overview MITK offers a comprehensive set of slice-based 2D and (semi-)automated 3D segmentation tools. The manual 2D tools require some user interaction and can only be applied to a single image slice whereas the 3D tools operate on the whole image. The 3D tools usually only require a small amount of user interaction, i.e. placing seed points or setting / adjusting parameters. You can switch between the different toolsets by selecting the 2D or 3D tab in the segmentation view. \imageMacro{QmitkSegmentation_ToolOverview.png,"An overview of the existing 2D and 3D tools in MITK.",5.50} \section org_mitk_views_segmentation2dsegmentation 2D segmentation tools With 2D manual contouring you define which voxels are part of the segmentation and which are not. This allows you to create segmentations of any structures of interest in an image. You can also use manual contouring to correct segmentations that result from sub-optimal automatic methods. The drawback of manual contouring is that you might need to define contours on many 2D slices. However, this is mitigated by the interpolation feature, which will make suggestions for a segmentation. To start using one of the editing tools, click its button from the displayed toolbox. The selected editing tool will be active and its corresponding button will stay pressed until you click the button again. Selecting a different tool also deactivates the previous one.\n If you have to delineate a lot of images, shortcuts to switch between tools becomes convenient. For that, just hit the first letter of each tool to activate it (A for Add, S for Subtract, etc.). All of the editing tools work by the same principle: using the mouse (left button) to click anywhere in a 2D window (any of the orientations axial, sagittal, or coronal), moving the mouse while holding the mouse button and releasing the button to finish the editing action. Multi-step undo and redo is fully supported by all editing tools by using the application-wide undo / redo buttons in the toolbar. Remark: Clicking and moving the mouse in any of the 2D render windows will move the crosshair that defines what part of the image is displayed. This behavior is disabled as long as any of the manual segmentation tools are active - otherwise you might have a hard time concentrating on the contour you are drawing. \subsection org_mitk_views_segmentationaddsubtracttools Add and subtract tools \imageMacro{QmitkSegmentation_IMGIconAddSubtract.png,"Add and subtract tools",7.70} Use the left mouse button to draw a closed contour. When releasing the mouse button, the contour will be added (Add tool) to or removed (Subtract tool) from the current segmentation. Adding and subtracting voxels can be iteratively repeated for the same segmentation. Holding CTRL / CMD while drawing will invert the current tool's behavior (i.e. instead of adding voxels, they will be subtracted). \subsection org_mitk_views_segmentationlassotool Lasso tool \imageMacro{QmitkSegmentation_Lasso.png,"Lasso tool",7.70} The tool is a more advanced version of the add/subtract tool. It offers you the following features:
  1. Generating a polygon segmentation (click left mouse button to set ancor point)
  2. Freehand contouring (like the add tool; press left mouse button while moving the mouse)
  3. Move ancor points (select an ancor point, press left mouse button while moving the mouse)
  4. Add new ancor points (press CTRL while click left mouse to add an ancor to the contour)
  5. Delete an ancor point (press Del while ancor point is selected)
  6. Segmentation can be added to the label (Add mode) or subtracted (Subtract mode)
To start a segmentation double left click where the first ancor point should be. To end the segmentation double left click where the last ancor point should be. Please note that:
  • feature 3-6 are only available, if auto confirm is *not* activated
  • feature 3-5 is not available for freehand contour segments
\subsection org_mitk_views_segmentationpaintwipetools Paint and wipe tools \imageMacro{QmitkSegmentation_IMGIconPaintWipe.png,"Paint and wipe tools",7.68} Use the Size slider to change the radius of the round paintbrush tool. Move the mouse in any 2D window and press the left button to draw or erase pixels. Holding CTRL / CMD while drawing will invert the current tool's behavior (i.e. instead of painting voxels, they will be wiped). \subsection org_mitk_views_segmentationregiongrowingtool Region growing tool \imageMacro{QmitkSegmentation_IMGIconRegionGrowing.png,"Region growing tool",3.81} Click at one point in a 2D slice widget to add an image region to the segmentation with the region growing tool. Region Growing selects all pixels around the mouse cursor that have a similar gray value as the pixel below the mouse cursor. This allows to quickly create segmentations of structures that have a good contrast to surrounding tissue. The tool operates based on the current level window, so changing the level window to optimize the contrast for the ROI is encouraged. Moving the mouse up / down is different from left / right: Moving up the cursor while holding the left mouse button widens the range for the included grey values; moving it down narrows it. Moving the mouse left and right will shift the range. The tool will select more or less pixels, corresponding to the changing gray value range. \if THISISNOTIMPLEMENTEDATTHEMOMENT A common issue with region growing is the so called "leakage" which happens when the structure of interest is connected to other pixels, of similar gray values, through a narrow "bridge" at the border of the structure. The Region Growing tool comes with a "leakage detection/removal" feature. If leakage happens, you can left-click into the leakage region and the tool will try to automatically remove this region (see illustration below). \imageMacro{QmitkSegmentation_Leakage.png,"Leakage correction feature of the region growing tool",11.28} \endif \subsection org_mitk_views_segmentationfilltool Fill tool \imageMacro{QmitkSegmentation_IMGIconFill.png,"Fill tool",3.81} Left-click inside a region/segmentation to flood fill all connected pixels that have the same label with the active label. This tool will only work on regions of unlocked labels or on regions that are not labeled at all. \subsection org_mitk_views_segmentationerasetool Erase tool \imageMacro{QmitkSegmentation_IMGIconErase.png,"Erase tool",3.79} This tool removes a connected part of pixels that form a segmentation. You may use it to remove single segmented regions by left-click on specific segmentation region. This tool will only work and regions of unlocked labels or on regions of the active label. \subsection org_mitk_views_segmentationclosetool Close tool \imageMacro{QmitkSegmentation_IMGIconClose.png,"Close tool",3.79} Left-click inside the region/segmentation to fill all "holes" (pixels labelled with another label or no label) inside the region. Therefore this tool behaves like a local closing operation. This tool will not work, when a non-labeled region is selected and holes of locked labels will not be filled. \remark This tool always uses the label of the selected region (even if this label is not the active label). Therefore you can use this tool on regions of the active label and of none locked labels (without the need to change the active label). \subsection org_mitk_views_segmentationlivewiretool Live wire tool \imageMacro{QmitkSegmentation_IMGIconLiveWire.png,"Live wire tool",3.01} The Live Wire Tool acts as a magnetic lasso with a contour snapping to edges of objects. \imageMacro{QmitkSegmentation_IMGLiveWireUsage.PNG,"Steps for using the Live Wire Tool",16.00} The tool handling is the same like the Lasso tool (see for more info), except it generates live wire contours instead of straight lines. \subsection org_mitk_views_segmentationSegmentAnything Segment Anything Tool \imageMacro{QmitkSegmentation_nnUnetTool.png,"Segment Anything tool",10.00} \imageMacro{QmitkSegmentation_SAMTool.png,"Segment Anything tool",10.00} The Segment Anything Tool is a deep learning-based interactive segmentation tool. Originally created by MetaAI, MITK presents this model for medical image segmentation tasks. The tool requires that you have Python 3 installed and available on your machine. Note: On Debian/Ubuntu systems, you need to install git, python3-pip, python3-venv package using the following command: `apt install git python3-pip python3-venv`. For best experience, your machine should be ideally equipped with a CUDA-enabled GPU. For a detailed explanation of what this algorithm is able to, please refer to https://ai.facebook.com/research/publications/segment-anything/
Any adjustments to the \subpage org_mitk_editors_stdmultiwidget_Levelwindow setting impacts the segmentation. However, any applied color maps are ignored. \subsubsection org_mitk_views_segmentationSegmentAnythingWorkflow Workflow: -# Install Segment Anything: Goto Preferences (Ctrl+P) > Segment Anything and click "Install Segment Anything with MedSAM" to install Segment Anything (version: 1.0). The installation process implicitly creates a python virtual environment using the Python located in "System Python" in an MITK mainitained directory. Make sure you have a working internet connection. This might take a while. It is a one time job, though. Once installed, the "Install Segment Anything" button is grayed out. \imageMacro{QmitkSegmentation_SAMTool_Preferences.png,"Segment Anything Preferences",10.00} -# Note: If Python is not listed by MITK in "System Python", click "Select..." in the dropdown to choose an unlisted installation of Python. Note that, while selecting an arbitrary environment folder, only select the base folder, e.g. "/usr/bin/". No need to navigate all the way into "../usr/bin/python3", for example. -# Select a specific model type in the "Model Type" dropdown. The default is "vit_b" for low memory footprint. However, more larger models "vit_l" and "vit_h" are also available for selection. -# Select inference hardware, i.e. any GPU or CPU. This is internally equivalent to setting the CUDA_VISIBLE_DEVICES environment variable. -# Click "OK" to save the preference settings. -# Goto Segmentation View > 2D tools > Segment Anything. -# Click "Initialize Segment Anything" to start the tool backend. This will invoke downloading of the selected model type from the internet. This might take a while. It is a one time job, though. -# Once the tool is initialized, Press SHIFT+Left Click on any of the 3 render windows to start click guided segmentation on that slice. -# Press SHIFT+Right Click for negative clicks to adjust the preview mask on the render window. \subsection org_mitk_views_segmentationMedSAM MedSAM Tool \imageMacro{QmitkSegmentation_nnUnetTool.png,"MedSAM tool",10.00} \imageMacro{QmitkSegmentation_MedSAMTool.png,"MedSAM tool",10.00} The MedSAM (Segment Anything in Medical Images) tool is a specialization of the the Segment Anything (SAM) tool. A new foundation model in the back end is dedicated to universal medical image segmentation. Just like the Segment Anything tool, the MedSAM tool requires that you have Python 3 installed and available on your machine. Note: On Debian/Ubuntu systems, you need to install the git, python3-pip, and python3-venv packages using the following command: `sudo apt install git python3-pip python3-venv`. For best experience, your machine should be ideally equipped with a CUDA-enabled GPU. Any adjustments to the \subpage org_mitk_editors_stdmultiwidget_Levelwindow setting impacts the segmentation. However, any applied color maps are ignored. \subsubsection org_mitk_views_segmentationMedSAMWorkflow Workflow -# Install MedSAM: Goto Preferences (Ctrl+P) > Segment Anything and click "Install Segment Anything with MedSAM" to install Segment Anything (version: 1.0) & MedSAM tool backends together. The installation process implicitly creates a python virtual environment using the Python located in "System Python" in a directory maintained by MITK. Make sure you have a working internet connection, which might take a while. It is a one-time job, though. Once installed, the "Install Segment Anything with MedSAM" button is grayed out. For details, refer to the Segment Anything tool workflow. -# Goto Segmentation View > 2D tools > MedSAM. -# Click "Initialize MedSAM" to start the tool. This will start downloading the model weights from the internet first, if not done before. This might take a while. It is a one-time job, though. -# Once the tool is initialized, press Shift+Left Click on any of the render windows to create a bounding box for that image slice. -# Click on the anchor points of the bounding box to adjust the region of interest. -# Click on Preview to generate segmentation from MedSAM model. Note: For a detailed explanation of what this algorithm is able to, please refer to https://www.nature.com/articles/s41467-024-44824-z \subsection org_mitk_views_segmentationinterpolation 2D and 3D Interpolation Creating segmentations using 2D manual contouring for large image volumes may be very time-consuming, because structures of interest may cover a large range of slices. The segmentation view offers two helpful features to mitigate this drawback:
  • 2D Interpolation
  • 3D Interpolation
The 2D Interpolation creates suggestions for a segmentation whenever you have a slice that
  • has got neighboring slices with segmentations (these do not need to be direct neighbors but could also be a couple of slices away) AND
  • is completely clear of a manual segmentation, i.e. there will be no suggestion if there is even only a single pixel of segmentation in the current slice.
\imageMacro{QmitkSegmentation_2DInterpolation.png,"2D interpolation usage",3.01} Interpolated suggestions are displayed as outlines, until you confirm them as part of the segmentation. To confirm single slices, click the Confirm for single slice button below the toolbox. You may also review the interpolations visually and then accept all of them at once by selecting Confirm for all slices. The 3D interpolation creates suggestions for 3D segmentations. That means if you start contouring, from the second contour onwards, the surface of the segmented area will be interpolated based on the given contour information. The interpolation works with all available manual tools. Please note that this is currently a pure mathematical interpolation, i.e. image intensity information is not taken into account. With each further contour the interpolation result will be improved, but the more contours you provide the longer the recalculation will take. To achieve an optimal interpolation result and in this way a most accurate segmentation you should try to describe the surface with sparse contours by segmenting in arbitrary oriented planes. The 3D interpolation is not meant to be used for parallel slice-wise segmentation, but rather segmentations in i.e. the axial, coronal and sagittal plane. \imageMacro{QmitkSegmentation_3DInterpolationWrongRight.png,"3D interpolation usage",16.00} You can accept the interpolation result by clicking the Confirm-button below the tool buttons. In this case the 3D interpolation will be deactivated automatically so that the result can be post-processed without any interpolation running in the background. Additional to the surface, black contours are shown in the 3D render window, which mark all the drawn contours used for the interpolation. You can navigate between the drawn contours by clicking on the corresponding position nodes in the data manager which are stored as sub-nodes of the selected segmentation. If you do not want to see these nodes just uncheck the Show Position Nodes checkbox and these nodes will be hidden. If you want to delete a drawn contour we recommend to use the Erase-Tool since undo / redo is not yet working for 3D interpolation. The current state of the 3D interpolation can be saved across application restart. For that, just click on save project during the interpolation is active. After restarting the application and load your project you can click on "Reinit Interpolation" within the 3D interpolation GUI area. \section org_mitk_views_segmentation3dsegmentation 3D segmentation tools The 3D tools operate on the whole image and require usually a small amount of interaction like placing seed-points or specifying certain parameters. All 3D tools provide an immediate segmentation feedback, which is displayed as a transparent green overlay. For accepting a preview you have to press the Confirm button of the selected tool. The following 3D tools are available: \subsection org_mitk_views_segmentation3dthresholdtool 3D Threshold tool The thresholding tool simply applies a 3D threshold to the patient image. All pixels with values equal or above the selected threshold are labeled as part of the segmentation. You can change the threshold by either moving the slider of setting a certain value in the spinbox. \imageMacro{QmitkSegmentation_3DThresholdTool.png,"3D Threshold tool",10.00} \subsection org_mitk_views_segmentation3dulthresholdTool 3D upper / lower threshold tool The Upper/Lower Thresholding tool works similar to the simple 3D threshold tool but allows you to define an upper and lower threshold. All pixels with values within this threshold interval will be labeled as part of the segmentation. \imageMacro{QmitkSegmentation_3DULThresholdTool.png,"3D upper / lower threshold tool",10.00} \subsection org_mitk_views_segmentation3dotsutool 3D Otsu tool The 3D Otsu tool provides a more sophisticated thresholding algorithm. It allows you to define a number of regions. Based on the image histogram the pixels will then be divided into different regions. The more regions you define the longer the calculation will take. You can select afterwards which of these regions you want to confirm as segmentation. \imageMacro{QmitkSegmentation_3DOtsuTool.png,"3D Otsu tool",10.00} \subsection org_mitk_views_segmentation3dgrowcuttool 3D GrowCut tool The 3D GrowCut tool uses previously created segmentation labels (e.g. by the "Add"-tool) stored in the segmentation layer 0. The GrowCut tool will use these segmentation labels to create a seedimage that will serve as input to the algorithm. As an advanced setting option, a Distance penalty can be set, which increases the cohesion in the immediate surroundings of the initial labels. Based on the seedimage and the Distance penalty, a growing is started, which includes all areas that are not initially assigned to a specific label. During this process, initially unassigned areas are assigned to the best fitting labels. After the segmentation process, the user can decide which newly generated labels should be confirmed. \imageMacro{QmitkSegmentation_3DGrowCutTool.png,"3D GrowCut tool",16.00} \subsection org_mitk_views_segmentationpickingtool Picking Tool The Picking tool offers two modes that allow you to manipulate "islands" within your segmentation. This is especially useful if e.g. a thresholding provided you with several areas within your image but you are just interested in one special region. - Picking mode: Allows you to select certain "islands". When the pick is confirmed, the complete content of the active label will be removed except the pick. This mode is beneficial if you have a lot segmentation noise and want to pick the relevant parts and dismiss the rest. Hint: You can also pick from other labels, but this will only work if these labels are unlocked. - Relabel mode: Allows you to select certain "islands". When the pick is confirmed, it will be relabeled and added to the active label content. Hint: This mode ignores the locks of other labels, hence you do not need to unlock them explicitly. \imageMacro{QmitkSegmentation_PickingTool.png,"Picking tool",10.00} \subsection org_mitk_views_segmentationnnUNetTool nnU-Net Tool (Ubuntu only) \imageMacro{QmitkSegmentation_nnUnetTool.png,"nnUNet tool",10.00} This tool provides a GUI to the deep learning-based segmentation algorithm called the nnU-Net v1. With this tool, you can get a segmentation mask predicted for the loaded image in MITK. Be ready with the pre-trained weights (a.k.a RESULTS_FOLDER) for your organ or task concerned, before using the tool. For a detailed explanation of the parameters and pre-trained weights folder structure etc., please refer to https://github.com/MIC-DKFZ/nnUNet.
Remark: The tool assumes that you have a Python3 environment with nnU-Net v1 (pip) installed. Your machine should be also equipped with a CUDA enabled GPU. \subsubsection org_mitk_views_segmentationnnUNetToolWorkflow Workflow: -# Select the "Python Path" drop-down to see if MITK has automatically detected other Python environments. Click on a fitting environment for the nnUNet inference or click "Select" in the dropdown to choose an unlisted python environment. Note that, while selecting an arbitrary environment folder, only select the base folder, e.g. "myenv". No need to select all the way until "../myenv/bin/python", for example. -# Click on the "nnUNet Results Folder" directory icon to navigate to the results folder on your hard disk. This is equivalent to setting the RESULTS_FOLDER environment variable. If your results folder is as per the nnUNet required folder structure, the configuration, trainers, tasks and folds are automatically parsed and correspondingly loaded in the drop-down boxes as shown below. Note that MITK automatically checks for the RESULTS_FOLDER environment variable value and, if found, auto parses that directory when the tool is started. \imageMacro{QmitkSegmentation_nnUNet_Settings.png,"nnUNet Segmentation Settings",10} -# Choose your required Task-Configuration-Trainer-Planner-Fold parameters, sequentially. By default, all entries are selected inside the "Fold" dropdown (shown: "All"). Note that, even if you uncheck all entries from the "Fold" dropdown (shown: "None"), then too, all folds would be considered for inferencing. -# For ensemble predictions, you will get the option to select parameters irrespective on postprocessing files available in the ensembles folder of RESULTS_FOLDER. Note that, if a postprocessing json file exists for the selected combination then it will used for ensembling, by default. To choose not to, uncheck the "Use PostProcessing JSON" in the "Advanced" section. \imageMacro{QmitkSegmentation_nnUNet_ensemble.png,"nnUNet Segmentation Settings",10} -# If your task is trained with multi-modal inputs, then "Multi-Modal" checkbox is checked and the no.of modalities are preloaded and shown next to "Required Modalities". Instantly, as much node selectors with corresponding modality names should appear below to select the Data Manager along including a selector with preselected with the reference node. Now, select the image nodes in the node selectors accordingly for accurate inferencing. \imageMacro{QmitkSegmentation_nnUNet_multimodal.png,"nnUNet Multi Modal Settings",10.00} -# Click on "Preview". -# In the "Advanced" section, you can also activate other options like "Mixed Precision" and "Enable Mirroring" (for test time data augmentation) pertaining to nnUNet. \imageMacro{QmitkSegmentation_nnUNet_Advanced.png,"nnUNet Advanced Settings",10.00} -# Use "Advanced" > "GPU Id" combobox to change the preferred GPU for inferencing. This is internally equivalent to setting the CUDA_VISIBLE_DEVICES environment variable. -# Every inferred segmentation is cached to prevent a redundant computation. In case, a user doesn't wish to cache a Preview, uncheck the "Enable Caching" in the "Advanced" section. This will ensure that the current parameters will neither be checked against the existing cache nor a segmentation be loaded from it when Preview is clicked. -# You may always clear all the cached segmentations by clicking "Clear Cache" button. \subsubsection org_mitk_views_segmentationnnUNetToolMisc Miscellaneous: -# In case you want to reload/reparse the folders in the "nnUNet Results Folder", eg. after adding new tasks into it, you may do so without reselecting the folder again by clicking the "Refresh Results Folder" button. -# The "Advanced" > "GPU Id" combobox lists the Nvidia GPUs available by parsing the nvidia-smi utility output. In case your machine has Nvidia CUDA enabled GPUs but the nvidia-smi fails for some reason, the "GPU Id" combobox will show no entries. In such a situation, it's still possible to execute inferencing by manually entering the preferred GPU Id, eg. 0 in the combobox. -# The "Advanced" > "Available Models" lists the available pre-trained tasks for download. Make sure you have internet connection. Then, choose a Task from the dropdown and click the Download button. The pre-trained models for the selected Task will be downloaded and placed to the RESULTS_FOLDER directory automatically. -# In the RESULTS_FOLDER directory, inside the trainer-planner folder of every task, MITK keeps a "mitk_export.json" file for fast loading for multi-modal information. It is recommended not to delete this file(s) for a fast responsive UI. Tip: If multi-modal information shown on MITK is not correct for a given task, you may modify this JSON file and try again. \subsection org_mitk_views_segmentationTotalSegmentator TotalSegmentator Tool \imageMacro{QmitkSegmentation_nnUnetTool.png,"TotalSegmentator tool",10.00} This tool provides a GUI to the deep learning-based segmentation algorithm called the TotalSegmentator (v2). With this tool, you can get a segmentation mask predicted for 117 classes in CT images, loaded in MITK. For a detailed explanation on tasks and supported classes etc., please refer to https://github.com/wasserth/TotalSegmentator
The tool assumes that you have Python >= 3.9 installed and available on your machine. We recommend to install TotalSegmentator via MITK. The "Install TotalSegmentator" action implicitly creates a python virtual environment in an MITK mainitained directory. Note: on Debian/Ubuntu systems, you need to install the python3-venv package using the following command: `apt install python3-venv`. For best results, your machine should be ideally equipped with a CUDA-enabled GPU. \imageMacro{QmitkSegmentation_TotalsegmentatorTool.png, "TotalSegmentator Settings",5} \subsubsection org_mitk_views_segmentationTotalSegmentatorWorkflow Workflow: -# Install TotalSegmentator: Click "Install TotalSegmentator" to install TotalSegmentator (version: 2.0.5) in a virtual environment. Make sure you have a working internet connection. This might take a while. It is a one time job, though. Once installed, the "Install TotalSegmentator" button is grayed out. -# If Python is not found by MITK goto "Install Options" & select the "System Python Path" drop-down to see if MITK has automatically detected other Python environments. Click on a fitting Python installation for TotalSegmentator to use or click "Select" in the dropdown to choose an unlisted installation of Python. Note that, while selecting an arbitrary environment folder, only select the base folder, e.g. "/usr/bin/". No need to navigate all the way into "../usr/bin/python3", for example. -# Select a specific subtask in the "Tasks" drop-downs. The default is "total" for non-specific total segmentation. -# Click on "Run TotalSegmentator" for a preview. -# In the "Advanced" section, you can also activate other options like "Fast" for faster runtime and less memory requirements. Use "Fast" if you only have a CPU for inferencing. -# Use "Advanced" > "GPU Id" combobox to change the preferred GPU for inferencing. This is internally equivalent to setting the CUDA_VISIBLE_DEVICES environment variable. -# In case you want to use your own virtual environment containing TotalSegmentator, goto "Install Options" & check "Use Custom Installation" checkbox. Then, select the environment of your choice by using "Custom Env. Path". \section org_mitk_views_segmentationpostprocessing Additional things you can do with segmentations Segmentations are never an end in themselves. Consequently, the segmentation view adds a couple of "post-processing" actions, accessible through the context-menu of the data manager. \imageMacro{QmitkSegmentation_IMGDataManagerContextMenu.png,"Context menu items for segmentations",10.58}
  • Create polygon %model applies the marching cubes algorithm to the segmentation. This polygon %model can be used for visualization in 3D or other applications such as stereolithography (3D printing).
  • Create smoothed polygon %model uses smoothing in addition to the marching cubes algorithm, which creates models that do not follow the exact outlines of the segmentation, but look smoother.
  • Autocrop can save memory. Manual segmentations have the same extent as the patient image, even if the segmentation comprises only a small sub-volume. This invisible and meaningless margin is removed by autocropping.
\section org_mitk_views_segmentationof3dtimages Segmentation of 3D+t images For segmentation of 3D+t images, some tools give you the option to choose between creating dynamic and static masks.
  • Dynamic masks can be created on each time frame individually.
  • Static masks will be defined on one time frame and will be the same for all other time frames.
In general, segmentation is applied on the time frame that is selected when execution is performed. If you alter the time frame, the segmentation preview is adapted. */ diff --git a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentationTaskList.dox b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentationTaskList.dox index af1669d092..bb31c61acd 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentationTaskList.dox +++ b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentationTaskList.dox @@ -1,186 +1,186 @@ /** \page org_mitk_views_segmentationtasklist The Segmentation Task List View \imageMacro{segmentation_task_list-dox.svg,"Icon of the Segmentation Task List View",2.00} \tableofcontents \section org_mitk_views_segmentationtasklist_disclaimer Disclaimer - The Segmentation Task List View and MITK Segmentation Task Lists in general are still in an experimental stage. - While we try to minimize breaking changes in the future, we cannot give any guarantees at the moment. - We strongly advise to ignore the Data Manager regarding loading and unloading of data while using the Segmentation Task List View, as it will completely take over these operations and may run into invalid states otherwise. \section org_mitk_views_segmentationtasklist_overview Overview \imageMacro{QmitkSegmentationTaskList.png,"Segmentation Task List View", 12.00} As the Segmentation Task List View is typically used in combination with the Segmentation View, we suggest to move the Segmentatiom View to the other side of the application (e.g. on top of the Data Manager), to see both views at the same time. To unlock the Segmentation Task List View, unload everything but a single MITK Segmentation Task List. The remaining Segmentation Task List will be automatically selected. The Segmentation Task List View shows the progress of the whole Segmentation Task List, e.g., the number of the tasks marked as done vs. the total number of available tasks. Below the progress indictator you can navigate between tasks, read their descriptions and related information, as well as load/activate the currently shown task. This will unload all data from a previously active task, if any, and load all data of the current task. -To prevent any accidental data loss, unsaved task data will interfer task switches and you can decide on how to proceed. +To prevent any accidental data loss, unsaved task data will interfere task switches and you can decide on how to proceed. Above the task description, the status of tasks is displayed as a pair of colored labels, indicating if a task is either active or inactive and if it is considered not to be done, having unsaved changes, or to be done. With the bottom two buttons you can either save an interim result (the task is considered not to be done), or accept and save the task result. You can still edit accepted tasks but in contrast to interim results, the task is considered to be done. \section org_mitk_views_segmentationtasklist_shortcuts Keyboard shortcuts The Segmentation Task List View can be used even more efficiently with the following set of keyboard shortcuts: - Ctrl + Alt + P: Navigate to previous task - Ctrl + Shift + P: Navigate to previous undone task (or Shift + click on resp. button) - Ctrl + Alt + N: Navigate to next task - Ctrl + Shift + N: Navigate to next undone task (or Shift + click on resp. button) - Ctrl + Alt + L: Load currently shown task - Ctrl + Alt + S: Store interim result - Ctrl + Alt + A: Accept task and store result - Ctrl + F: Find task... \section org_mitk_views_segmentationtasklist_find Finding tasks \imageMacro{QmitkFindSegmentationTaskDialog.png,"Find Segmentation Task dialog", 12.00} To find a specific task in a segmentation task list, click on the "Find task..." button (magnifier icon) or press Ctrl + F. A dedicated dialog will appear for filtering and searching the current segmentation task list. Enter a task number and press Return to immediately load the according task or filter the shown list of tasks and double click on a result to load the task. \section org_mitk_views_segmentationtasklist_fileformat MITK Segmentation Task List file format MITK Segmentation Task List files are JSON files containing a JSON object as root. It must contain the two mandatory properties FileFormat and Version: \code{.json} { "FileFormat": "MITK Segmentation Task List", "Version": 1 } \endcode We also recommend to specify an optional Name that is used in the application if present instead of the plain filename of the JSON file: \code{.json} { "FileFormat": "MITK Segmentation Task List", "Version": 1, "Name": "My First Task List" } \endcode \subsection org_mitk_views_segmentationtasklist_fileformat_tasks Tasks The root object must also contain a mandatory Tasks array, containing JSON objects that specify the individual tasks of the task list. A minimum task object must contain Image and Result file paths. Image refers to the patient image and Result refers to the path were the resulting segmentation is expected to be stored. Paths can be absolute or relative to the JSON file. \code{.json} { "FileFormat": "MITK Segmentation Task List", "Version": 1, "Tasks": [ { "Image": "images/Pic3D.nrrd", "Result": "results/liver.nrrd" } ] } \endcode In addition, tasks can contain a bunch of optional properties that mainly specify a segmentation a user starts with: - Name (string): A name for the task. - Description (string): A short description/definition of the task. - LabelName (string): The name of the first label in a new segmentation that is created for the task on the fly. - LabelNameSuggestions (file path): A Label Suggestions JSON file (example in next comment) specifying names and optional colors, that are suggested to the user for new labels in the segmentation. - Preset (file path): A Label Set Preset XML file in MITK's .lsetp file format. The preset is applied to a new segmentation that is created for the task on the fly. We recommend to use the Segmentation plugin of the MITK Workbench to create such label set preset files as described in its {key F1} user guide. - Segmentation (file path): A pre-segmentation that a user can start with or has to refine for example. - Dynamic (boolean): In case Image refers to a dynamic (3d+t) image, specifies whether the segmentation should be static (false), i.e. equal for all time steps, or dynamic (true), i.e. individual for each time step. \subsection org_mitk_views_segmentationtasklist_fileformat_taskdefaults Task defaults / common properties If a task list contains multiple tasks with common properties, they do not have to be specified for each and every task again and again. Instead, the root object can contain an optional Defaults object that is identical in format to the tasks specified above. As the name indicates, default properties can still be overridden by individual tasks if they are specified explicitly. There is a single exception, though: A Defaults object must not contain a Result file path, since result files of tasks must be distinct by definition. \subsection org_mitk_views_segmentationtasklist_fileformat_example Example The following example is a complete showcase of the properties and features listed above. It specifies 4 tasks. 3 tasks refer to the same patient image so it is specified as default. Remember that the only task property required to be distinct is Result so you are pretty free in your task design. For simplicity, we chose to define tasks around organs for this example and named the tasks accordingly: \code{.json} { "FileFormat": "MITK Segmentation Task List", "Version": 1, "Name": "Example Segmentation Task List", "Defaults": { "Image": "images/Pic3D.nrrd" }, "Tasks": [ { "Name": "Liver", "LabelName": "Liver", "LabelNameSuggestions": "suggestions/label_suggestions.json", "Description": "This task provides an image and label name suggestions for new labels. The segmentation will start with an empty label named Liver.", "Result": "results/liver.nrrd" }, { "Name": "Kidneys", "Description": "This task provides an image and a label set preset that is applied to the new segmentation.", "Preset": "presets/kidneys.lsetp", "Result": "results/kidneys.nrrd" }, { "Name": "Spleen", "Description": "This task provides an image and an initial (pre-)segmentation.", "Segmentation": "segmentations/spleen.nrrd", "Result": "results/spleen.nrrd" }, { "Name": "Surprise", "Description": "And now for something completely different. This task overrides the default Image and starts with an empty static segmentation for a dynamic image.", "Image": "images/US4DCyl.nrrd", "Result": "results/US4DCyl.nrrd", "Dynamic": false } ] } \endcode \section org_mitk_views_segmentationtasklist_labelsuggestions MITK Label Suggestions file format The Label Suggestions JSON file format mentioned above to specify a list of suggested names and optional colors for new labels is as follows: \code{.json} [ { "name": "Abdomen", "color": "red" }, { "name": "Lung", "color": "#00ff00" }, { "name": "Heart" }, { "name": "Aortic Valve", "color": "CornflowerBlue" } ] \endcode */ diff --git a/Utilities/qtsingleapplication/qtlockedfile.cpp b/Utilities/qtsingleapplication/qtlockedfile.cpp index 8e45624f42..92f67014ad 100644 --- a/Utilities/qtsingleapplication/qtlockedfile.cpp +++ b/Utilities/qtsingleapplication/qtlockedfile.cpp @@ -1,192 +1,192 @@ /**************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the Qt Solutions component. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names ** of its contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qtlockedfile.h" /*! \class QtLockedFile \brief The QtLockedFile class extends QFile with advisory locking functions. A file may be locked in read or write mode. Multiple instances of \e QtLockedFile, created in multiple processes running on the same machine, may have a file locked in read mode. Exactly one instance may have it locked in write mode. A read and a write lock cannot exist simultaneously on the same file. The file locks are advisory. This means that nothing prevents another process from manipulating a locked file using QFile or file system functions offered by the OS. Serialization is only guaranteed if all processes that access the file use QLockedFile. Also, while holding a lock on a file, a process must not open the same file again (through any API), or locks can be unexpectedly lost. The lock provided by an instance of \e QtLockedFile is released whenever the program terminates. This is true even when the program crashes and no destructors are called. */ /*! \enum QtLockedFile::LockMode This enum describes the available lock modes. \value ReadLock A read lock. \value WriteLock A write lock. \value NoLock Neither a read lock nor a write lock. */ /*! Constructs an unlocked \e QtLockedFile object. This constructor behaves in the same way as \e QFile::QFile(). \sa QFile::QFile() */ QtLockedFile::QtLockedFile() : QFile() { #ifdef Q_OS_WIN wmutex = 0; rmutex = 0; #endif m_lock_mode = NoLock; } /*! Constructs an unlocked QtLockedFile object with file \a name. This constructor behaves in the same way as \e QFile::QFile(const QString&). \sa QFile::QFile() */ QtLockedFile::QtLockedFile(const QString &name) : QFile(name) { #ifdef Q_OS_WIN wmutex = 0; rmutex = 0; #endif m_lock_mode = NoLock; } /*! Opens the file in OpenMode \a mode. This is identical to QFile::open(), with the one exception that the Truncate mode flag is disallowed. Truncation would conflict with the advisory file locking, since the file would be modified before the write lock is obtained. If truncation is required, use resize(0) after obtaining the write lock. Returns true if successful; otherwise false. \sa QFile::open(), QFile::resize() */ bool QtLockedFile::open(OpenMode mode) { if (mode & QIODevice::Truncate) { qWarning("QtLockedFile::open(): Truncate mode not allowed."); return false; } return QFile::open(mode); } /*! Returns \e true if this object has a in read or write lock; otherwise returns \e false. \sa lockMode() */ bool QtLockedFile::isLocked() const { return m_lock_mode != NoLock; } /*! Returns the type of lock currently held by this object, or \e QtLockedFile::NoLock. \sa isLocked() */ QtLockedFile::LockMode QtLockedFile::lockMode() const { return m_lock_mode; } /*! \fn bool QtLockedFile::lock(LockMode mode, bool block = true) Obtains a lock of type \a mode. The file must be opened before it can be locked. If \a block is true, this function will block until the lock is - aquired. If \a block is false, this function returns \e false - immediately if the lock cannot be aquired. + acquired. If \a block is false, this function returns \e false + immediately if the lock cannot be acquired. If this object already has a lock of type \a mode, this function returns \e true immediately. If this object has a lock of a different type than \a mode, the lock is first released and then a new lock is obtained. This function returns \e true if, after it executes, the file is locked by this object, and \e false otherwise. \sa unlock(), isLocked(), lockMode() */ /*! \fn bool QtLockedFile::unlock() Releases a lock. If the object has no lock, this function returns immediately. This function returns \e true if, after it executes, the file is not locked by this object, and \e false otherwise. \sa lock(), isLocked(), lockMode() */ /*! \fn QtLockedFile::~QtLockedFile() Destroys the \e QtLockedFile object. If any locks were held, they are released. */