Ver código fonte

Merge branch 'python_wrapping'

* python_wrapping:
  COMP: Superbuild - PYTHON_EXECUTABLE is now passed to CTK inner build
  COMP: Since PythonQt wrapper library are static, make sure the flag -fPIC is passed.
  ENH: Add PYTHONQT_INSTALL_DIR to CTKConfig
  ENH: Added ctkMacroWrapPythonQt
Jean-Christophe Fillion-Robin 15 anos atrás
pai
commit
8d59ab6523

+ 15 - 0
CMake/ctkMacroBuildLib.cmake

@@ -131,6 +131,21 @@ MACRO(ctkMacroBuildLib)
     DESTINATION ${CTK_INSTALL_INCLUDE_DIR} COMPONENT Development
     )
 
+  IF(CTK_WRAP_PYTHONQT_LIGHT OR CTK_WRAP_PYTHONQT_FULL)
+    ctkMacroWrapPythonQt("org.commontk" ${lib_name}
+      KIT_PYTHONQT_SRCS "${MY_SRCS}" ${CTK_WRAP_PYTHONQT_FULL})
+    ADD_LIBRARY(${lib_name}PythonQt STATIC ${KIT_PYTHONQT_SRCS})
+    TARGET_LINK_LIBRARIES(${lib_name}PythonQt ${lib_name})
+    IF(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64")
+      SET_TARGET_PROPERTIES(${lib_name}PythonQt PROPERTIES COMPILE_FLAGS "-fPIC")
+    ENDIF()
+
+    # Update list of libraries wrapped with PythonQt
+    SET(CTK_WRAPPED_LIBRARIES_PYTHONQT
+      ${CTK_WRAPPED_LIBRARIES_PYTHONQT} ${lib_name}
+      CACHE INTERNAL "CTK libraries wrapped using PythonQt" FORCE)
+  ENDIF()
+
 ENDMACRO()
 
 

+ 305 - 0
CMake/ctkMacroWrapPythonQt.cmake

@@ -0,0 +1,305 @@
+###########################################################################
+#
+#  Library:   CTK
+# 
+#  Copyright (c) 2010  Kitware Inc.
+#
+#  Licensed under the Apache License, Version 2.0 (the "License");
+#  you may not use this file except in compliance with the License.
+#  You may obtain a copy of the License at
+#
+#      http://www.commontk.org/LICENSE
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+# 
+###########################################################################
+
+#
+# ctkMacroWrapPythonQt
+#
+
+#
+# Depends on:
+#  PythonQt 
+#  PythonQtGenerator (Only if IS_WRAP_FULL is TRUE)
+#  PythonInterp (See function reSearchFile)
+#
+
+#
+# The different parameters are:
+#
+#    WRAPPING_NAMESPACE: Namespace that should contain the library. For example: org.commontk
+#         
+#    TARGET ...........: Name of the wrapped library. For example: CTKWidget
+# 
+#    SRCS_LIST_NAME ...: Name of the variable that should contain the generated wrapper source.
+#                        For example: KIT_PYTHONQT_SRCS
+#
+#    SOURCES ..........: List of source files that should be wrapped.
+#
+#    IS_WRAP_FULL .....: Indicate if a Full wrapping if desired.
+#
+
+#
+# LOG FILE:
+#   File ctkMacroWrapPythonQt_log.txt will be created in the current directory. 
+#   It will contain the list of file and the reason why a given class hasn't been wrapped.
+#
+
+set(verbose 0)
+
+#
+# Convenient function allowing to log the reason why a given class hasn't been wrapped
+# If verbose=1, it will also be displayed on the standard output
+#
+FUNCTION(ctkMacroWrapPythonQt_log msg)
+  IF(verbose)
+    MESSAGE(${msg})
+  ENDIF()
+  FILE(APPEND "${CMAKE_CURRENT_BINARY_DIR}/ctkMacroWrapPythonQt_log.txt" "${msg}\n")
+ENDFUNCTION()
+
+#
+# Convenient function allowing to invoke re.search(regex, string) using the given interpreter.
+# Note that is_matching will be set to True if there is a match
+#
+FUNCTION(ctkMacroWrapPythonQt_reSearchFile python_exe regex file is_matching)
+
+  set(python_cmd "import re\; f = open('${file}', 'r')\;
+res = re.search\(\"${regex}\", f.read(), re.MULTILINE\)\;
+if res == None: print \"FALSE\"
+else: print \"TRUE\"
+")
+  #message("python_cmd: ${python_cmd}")
+
+  EXECUTE_PROCESS(
+    COMMAND ${python_exe} -c ${python_cmd};
+    RESULT_VARIABLE result
+    OUTPUT_VARIABLE output
+    ERROR_VARIABLE error
+    OUTPUT_STRIP_TRAILING_WHITESPACE
+    )
+  
+  IF(result)
+    MESSAGE(SEND_ERROR "reSearchFile - Problem with regex: ${regex}\n${error}")
+  ENDIF()
+  SET(is_matching ${output} PARENT_SCOPE)
+ENDFUNCTION()
+
+MACRO(ctkMacroWrapPythonQt WRAPPING_NAMESPACE TARGET SRCS_LIST_NAME SOURCES IS_WRAP_FULL)
+  
+  # Sanity check
+  IF(IS_WRAP_FULL AND NOT EXISTS "${PYTHONQTGENERATOR_EXECUTABLE}")
+    MESSAGE(SEND_ERROR "PYTHONQTGENERATOR_EXECUTABLE not specified or inexistent when calling ctkMacroWrapPythonQt")
+  ENDIF()
+  
+  find_package(PythonInterp)
+  IF(NOT EXISTS "${PYTHON_EXECUTABLE}")
+    MESSAGE(SEND_ERROR "PYTHON_EXECUTABLE not specified or inexistent when calling ctkMacroWrapPythonQt")
+  ENDIF()
+  
+  # Clear log file
+  FILE(WRITE "${CMAKE_CURRENT_BINARY_DIR}/ctkMacroWrapPythonQt_log.txt" "")
+  
+  # Convert wrapping namespace to subdir
+  STRING(REPLACE "." "_" WRAPPING_NAMESPACE_UNDERSCORE ${WRAPPING_NAMESPACE})
+  
+  SET(SOURCES_TO_WRAP)
+  # For each class
+  FOREACH(FILE ${SOURCES})
+  
+    SET(skip_wrapping FALSE)
+    
+    IF(NOT skip_wrapping)
+      # Skip wrapping if file is NOT regular header
+      IF(NOT ${FILE} MATCHES "^.*\\.[hH]$")
+        SET(skip_wrapping TRUE)
+        ctkMacroWrapPythonQt_log("${FILE}: skipping - Not a regular header")
+      ENDIF()
+    ENDIF()
+    
+    IF(NOT skip_wrapping)
+      # Skip wrapping if file is a pimpl header
+      IF(${FILE} MATCHES "^.*_[pP]\\.[hH]$")
+        SET(skip_wrapping TRUE)
+        ctkMacroWrapPythonQt_log("${FILE}: skipping - Pimpl header (*._p.h)")
+      ENDIF()
+    ENDIF()
+    
+    IF(NOT skip_wrapping)
+      # Skip wrapping if file should excluded
+      SET(skip_wrapping TRUE)
+      GET_SOURCE_FILE_PROPERTY(TMP_WRAP_EXCLUDE ${FILE} WRAP_EXCLUDE)
+      IF(NOT TMP_WRAP_EXCLUDE)
+        SET(skip_wrapping FALSE)
+      ENDIF()
+      IF(skip_wrapping)
+        ctkMacroWrapPythonQt_log("${FILE}: skipping - WRAP_EXCLUDE")
+      ENDIF()
+    ENDIF()
+    
+    # what is the filename without the extension
+    GET_FILENAME_COMPONENT(TMP_FILENAME ${FILE} NAME_WE)
+    
+    # Extract classname - NOTE: We assume the filename matches the associated class
+    SET(className ${TMP_FILENAME})
+    
+    IF(NOT skip_wrapping)
+      # Skip wrapping if IS_WRAP_FULL=FALSE and if file do NOT contain Q_OBJECT
+      IF(NOT IS_WRAP_FULL)
+        file(READ ${CMAKE_CURRENT_SOURCE_DIR}/${FILE} file_content)
+        IF(NOT "${file_content}" MATCHES "Q_OBJECT")
+          SET(skip_wrapping TRUE)
+          ctkMacroWrapPythonQt_log("${FILE}: skipping - No Q_OBJECT macro")
+        ENDIF()
+      ENDIF()
+    ENDIF()
+    
+    IF(NOT skip_wrapping)
+      # Skip wrapping if IS_WRAP_FULL=FALSE and if constructor doesn't match:
+      #    my_class()
+      #    my_class(QObject* newParent ...)
+      #    my_class(QWidget* newParent ...)
+      IF(NOT IS_WRAP_FULL)
+        # Constructor with either QWidget or QObject as first parameter
+        SET(regex "[^~]${className}[\\s\\n]*\\([\\s\\n]*((QObject|QWidget)[\\s\\n]*\\*[\\s\\n]*\\w+[\\s\\n]*(\\=[\\s\\n]*(0|NULL)|,.*\\=.*\\)|\\)|\\)))")
+        ctkMacroWrapPythonQt_reSearchFile(${PYTHON_EXECUTABLE} ${regex} ${CMAKE_CURRENT_SOURCE_DIR}/${FILE} is_matching)
+        IF(NOT is_matching)
+          SET(skip_wrapping TRUE)
+          ctkMacroWrapPythonQt_log("${FILE}: skipping - Missing expected constructor signature")
+        ENDIF()
+      ENDIF()
+    ENDIF()
+    
+    IF(NOT skip_wrapping)
+      # Skip wrapping if object has a virtual pure method 
+      SET(regex "virtual[\\w\\n\\s\\(\\)]+\\=[\\s\\n]*(0|NULL)[\\s\\n]*\\x3b")
+      ctkMacroWrapPythonQt_reSearchFile(${PYTHON_EXECUTABLE} ${regex} ${CMAKE_CURRENT_SOURCE_DIR}/${FILE} is_matching)
+      IF(is_matching)
+        SET(skip_wrapping TRUE)
+        ctkMacroWrapPythonQt_log("${FILE}: skipping - Contains a virtual pure method")
+      ENDIF()
+    ENDIF()
+    
+    # if we should wrap it
+    IF (NOT skip_wrapping)
+      
+      # the input file might be full path so handle that
+      GET_FILENAME_COMPONENT(TMP_FILEPATH ${FILE} PATH)
+      
+      # compute the input filename
+      IF (TMP_FILEPATH)
+        SET(TMP_INPUT ${TMP_FILEPATH}/${TMP_FILENAME}.h) 
+      ELSE (TMP_FILEPATH)
+        SET(TMP_INPUT ${CMAKE_CURRENT_SOURCE_DIR}/${TMP_FILENAME}.h)
+      ENDIF (TMP_FILEPATH)
+        
+      LIST(APPEND SOURCES_TO_WRAP ${TMP_INPUT})
+        
+    ENDIF()
+  ENDFOREACH()
+  
+  # PythonQtGenerator expects a colon ':' separated list
+  SET(INCLUDE_DIRS_TO_WRAP)
+  FOREACH(include ${CTK_BASE_INCLUDE_DIRS})
+    SET(INCLUDE_DIRS_TO_WRAP "${INCLUDE_DIRS_TO_WRAP}:${include}")
+  ENDFOREACH()
+  
+  # Prepare custom_command argument
+  SET(SOURCES_TO_WRAP_ARG)
+  FOREACH(source ${SOURCES_TO_WRAP})
+    SET(SOURCES_TO_WRAP_ARG "${SOURCES_TO_WRAP_ARG}^^${source}")
+  ENDFOREACH()
+  
+  # Define wrap type and wrap intermediate directory
+  SET(wrap_type "Light")
+  SET(wrap_int_dir generated_cpp/${WRAPPING_NAMESPACE_UNDERSCORE}_${TARGET}/)
+  SET(extra_files )
+  IF(${IS_WRAP_FULL})
+    SET(wrap_type "Full")
+    SET(extra_files ${wrap_int_dir}ctkPythonQt_${TARGET}_masterinclude.h)
+  ENDIF()
+  #message("wrap_type:${wrap_type} - wrap_int_dir:${wrap_int_dir}")
+  
+  # Create intermediate output directory
+  EXECUTE_PROCESS(COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/${wrap_int_dir})
+  
+  set(wrapper_init_cpp_filename ${WRAPPING_NAMESPACE_UNDERSCORE}_${TARGET}_init.cpp)
+  set(wrapper_init_cpp_file ${CMAKE_CURRENT_BINARY_DIR}/${wrap_int_dir}${wrapper_init_cpp_filename})
+  
+  # Custom command allow to generate ${WRAPPING_NAMESPACE_UNDERSCORE}_${TARGET}_init.cpp and 
+  # associated wrappers ${WRAPPING_NAMESPACE_UNDERSCORE}_${TARGET}{0-N}.cpp
+  ADD_CUSTOM_COMMAND(
+    OUTPUT ${wrap_int_dir}${wrapper_init_cpp_filename} ${extra_files}
+    DEPENDS ${pythonqtgenerator_executable_depends} ${SOURCES_TO_WRAP} ${CTK_CMAKE_DIR}/ctkScriptWrapPythonQt_${wrap_type}.cmake
+    COMMAND ${CMAKE_COMMAND}
+      -DPYTHONQTGENERATOR_EXECUTABLE:FILEPATH=${PYTHONQTGENERATOR_EXECUTABLE}
+      -DPYTHON_EXECUTABLE:FILEPATH=${PYTHON_EXECUTABLE}
+      -DWRAPPING_NAMESPACE:STRING=${WRAPPING_NAMESPACE}
+      -DTARGET:STRING=${TARGET}
+      -DSOURCES:STRING=${SOURCES_TO_WRAP_ARG}
+      -DINCLUDE_DIRS:STRING=${INCLUDE_DIRS_TO_WRAP}
+      -DOUTPUT_DIR:PATH=${CMAKE_CURRENT_BINARY_DIR}
+      -DWRAP_INT_DIR:STRING=${wrap_int_dir}
+      -DQT_QMAKE_EXECUTABLE:FILEPATH=${QT_QMAKE_EXECUTABLE}
+      -P ${CTK_CMAKE_DIR}/ctkScriptWrapPythonQt_${wrap_type}.cmake
+    COMMENT "PythonQt ${wrap_type} Wrapping - Generating ${wrapper_init_cpp_filename}"
+    VERBATIM
+    )
+    
+  # Prepare custom_command argument
+  SET(moc_flags_arg)
+  FOREACH(flag ${moc_flags})
+    SET(moc_flags_arg "${moc_flags_arg}^^${flag}")
+  ENDFOREACH()
+  
+  # Use Moc to process the wrapper headers
+  QT4_GET_MOC_FLAGS(moc_flags)
+  
+  SET(wrapper_master_moc_filename moc_${WRAPPING_NAMESPACE_UNDERSCORE}_${TARGET}_all.cpp)
+  SET(wrapper_master_moc_file ${CMAKE_CURRENT_BINARY_DIR}/${wrap_int_dir}${wrapper_master_moc_filename})
+  
+  # Custom command allowing to call moc to process the wrapper headers
+  ADD_CUSTOM_COMMAND(
+    OUTPUT ${wrap_int_dir}${wrapper_master_moc_filename}
+    DEPENDS ${wrap_int_dir}${wrapper_init_cpp_filename} ${extra_files} ${CTK_CMAKE_DIR}/ctkScriptMocPythonQtWrapper.cmake
+    COMMAND ${CMAKE_COMMAND}
+      -DWRAPPING_NAMESPACE:STRING=${WRAPPING_NAMESPACE}
+      -DTARGET:STRING=${TARGET}
+      -DMOC_FLAGS:STRING=${moc_flags_arg}
+      -DWRAP_INT_DIR:STRING=${wrap_int_dir}
+      -DWRAPPER_MASTER_MOC_FILE:STRING=${wrapper_master_moc_file}
+      -DOUTPUT_DIR:PATH=${CMAKE_CURRENT_BINARY_DIR}
+      -DQT_MOC_EXECUTABLE:FILEPATH=${QT_MOC_EXECUTABLE}
+      -P ${CTK_CMAKE_DIR}/ctkScriptMocPythonQtWrapper.cmake
+    COMMENT "PythonQt ${wrap_type} Wrapping - Moc'ing ${WRAPPING_NAMESPACE_UNDERSCORE}_${TARGET} wrapper headers"
+    VERBATIM
+    )
+  
+  #The following files are generated
+  SET_SOURCE_FILES_PROPERTIES(
+    ${wrap_int_dir}${wrapper_init_cpp_filename}
+    ${wrap_int_dir}${wrapper_master_moc_filename}
+    PROPERTIES GENERATED TRUE)
+    
+  # Create the Init File
+  SET(${SRCS_LIST_NAME} 
+    ${${SRCS_LIST_NAME}}
+    ${wrap_int_dir}${wrapper_init_cpp_filename}
+    ${wrap_int_dir}${wrapper_master_moc_filename})
+  
+  #
+  # Let's include the headers associated with PythonQt
+  #
+  FIND_PACKAGE(PythonQt)
+  IF(NOT PYTHONQT_FOUND)
+    MESSAGE(FATAL_ERROR "error: PythonQt package is required to build ${TARGET}PythonQt")
+  ENDIF()
+  INCLUDE_DIRECTORIES(${PYTHON_INCLUDE_DIRS} ${PYTHONQT_INCLUDE_DIR})
+
+ENDMACRO()
+

+ 95 - 0
CMake/ctkScriptMocPythonQtWrapper.cmake

@@ -0,0 +1,95 @@
+###########################################################################
+#
+#  Library:   CTK
+# 
+#  Copyright (c) 2010  Kitware Inc.
+#
+#  Licensed under the Apache License, Version 2.0 (the "License");
+#  you may not use this file except in compliance with the License.
+#  You may obtain a copy of the License at
+#
+#      http://www.commontk.org/LICENSE
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+# 
+###########################################################################
+
+#
+# ctkScriptMocPythonQtWrapper
+#
+
+#
+# This script should be invoked either as a CUSTOM_COMMAND 
+# or from the command line using the following syntax:
+#
+#    cmake -DWRAPPING_NAMESPACE:STRING=org.commontk -DTARGET:STRING=MyLib
+#          -DOUTPUT_DIR:PATH=/path -DINCLUDE_DIRS:STRING=/path1:/path2
+#          -DWRAPPER_MASTER_MOC_FILE:FILEPATH=/path/to/file
+#          -DQT_QMAKE_EXECUTABLE:PATH=/path/to/qt/qmake
+#           -P ctkScriptWrapPythonQt.cmake
+#
+#
+
+# Check for non-defined var
+FOREACH(var WRAPPING_NAMESPACE TARGET MOC_FLAGS WRAPPER_MASTER_MOC_FILE WRAP_INT_DIR)
+  IF(NOT DEFINED ${var})
+    MESSAGE(SEND_ERROR "${var} not specified when calling ctkScriptMocPythonQtWrapper")
+  ENDIF()
+ENDFOREACH()
+
+# Check for non-existing ${var}
+FOREACH(var OUTPUT_DIR QT_MOC_EXECUTABLE)
+  IF(NOT EXISTS ${${var}})
+    MESSAGE(SEND_ERROR "Failed to find ${var} when calling ctkScriptWrapPythonQt")
+  ENDIF()
+ENDFOREACH()
+
+# Convert wrapping namespace to subdir
+STRING(REPLACE "." "_" WRAPPING_NAMESPACE_UNDERSCORE ${WRAPPING_NAMESPACE})
+
+# Convert ^^ separated string to list
+STRING(REPLACE "^^" ";" MOC_FLAGS "${MOC_FLAGS}")
+
+# Clear file where all moc'ified will be appended
+FILE(WRITE ${WRAPPER_MASTER_MOC_FILE} "// 
+// File auto-generated by cmake macro ctkScriptMocPythonQtWrapper\n//\n")
+
+# Collect wrapper headers
+set(glob_expression ${OUTPUT_DIR}/${WRAP_INT_DIR}${WRAPPING_NAMESPACE_UNDERSCORE}_${TARGET}*.h)
+FILE(GLOB wrapper_headers RELATIVE ${OUTPUT_DIR}/${WRAP_INT_DIR} ${glob_expression})
+
+IF(NOT wrapper_headers)
+  MESSAGE(SEND_ERROR "ctkScriptMocPythonQtWrapper - Failed to glob wrapper headers using expression:[${glob_expression}]")
+ENDIF()
+
+# Moc'ified each one of them
+FOREACH(header ${wrapper_headers})
+
+  # what is the filename without the extension
+  GET_FILENAME_COMPONENT(TMP_FILENAME ${header} NAME_WE)
+  
+  set(moc_options)
+  set(wrapper_h_file ${OUTPUT_DIR}/${WRAP_INT_DIR}/${header})
+  set(wrapper_moc_file ${OUTPUT_DIR}/${WRAP_INT_DIR}/moc_${header}.cpp)
+  #message("wrapper_h_file: ${wrapper_h_file}")
+  #message("wrapper_moc_file: ${wrapper_moc_file}")
+  EXECUTE_PROCESS(
+    COMMAND ${QT_MOC_EXECUTABLE} ${MOC_FLAGS} ${moc_options} -o ${wrapper_moc_file} ${wrapper_h_file}
+    WORKING_DIRECTORY ${OUTPUT_DIR}
+    RESULT_VARIABLE RESULT_VAR
+    ERROR_VARIABLE error
+    )
+  IF(RESULT_VAR)
+    MESSAGE(FATAL_ERROR "Failed to moc'ified .\n${RESULT_VAR}\n${error}")
+  ENDIF()
+  
+  # Append generated moc file to the master file
+  FILE(READ ${wrapper_moc_file} file_content)
+  FILE(APPEND ${WRAPPER_MASTER_MOC_FILE} "${file_content}")
+
+ENDFOREACH()
+

+ 131 - 0
CMake/ctkScriptWrapPythonQt_Full.cmake

@@ -0,0 +1,131 @@
+###########################################################################
+#
+#  Library:   CTK
+# 
+#  Copyright (c) 2010  Kitware Inc.
+#
+#  Licensed under the Apache License, Version 2.0 (the "License");
+#  you may not use this file except in compliance with the License.
+#  You may obtain a copy of the License at
+#
+#      http://www.commontk.org/LICENSE
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+# 
+###########################################################################
+
+#
+# ctkScriptWrapPythonQt_Full
+#
+
+#
+# Depends on:
+#  CTK/CMake/ctkMacroWrapPythonQt.cmake
+#
+
+#
+# This script should be invoked either as a CUSTOM_COMMAND 
+# or from the command line using the following syntax:
+#
+#    cmake -DWRAPPING_NAMESPACE:STRING=org.commontk -DTARGET:STRING=MyLib 
+#          -DSOURCES:STRING="file1^^file2" -DINCLUDE_DIRS:STRING=/path1:/path2
+#          -DWRAP_INT_DIR:STRING=subir/subir/
+#          -DPYTHONQTGENERATOR_EXECUTABLE:FILEPATH=/path/to/exec
+#          -DOUTPUT_DIR:PATH=/path  -DQT_QMAKE_EXECUTABLE:PATH=/path/to/qt/qmake
+#          -P ctkScriptWrapPythonQt_Full.cmake
+#
+
+# Check for non-defined var
+FOREACH(var SOURCES TARGET INCLUDE_DIRS WRAP_INT_DIR WRAPPING_NAMESPACE)
+  IF(NOT DEFINED ${var})
+    MESSAGE(SEND_ERROR "${var} not specified when calling ctkScriptWrapPythonQt")
+  ENDIF()
+ENDFOREACH()
+
+# Check for non-existing ${var}
+FOREACH(var PYTHONQTGENERATOR_EXECUTABLE QT_QMAKE_EXECUTABLE OUTPUT_DIR)
+  IF(NOT EXISTS ${${var}})
+    MESSAGE(SEND_ERROR "Failed to find ${var} when calling ctkScriptWrapPythonQt")
+  ENDIF()
+ENDFOREACH()
+
+# Convert wrapping namespace to subdir
+STRING(REPLACE "." "_" WRAPPING_NAMESPACE_UNDERSCORE ${WRAPPING_NAMESPACE})
+
+# Convert ^^ separated string to list
+STRING(REPLACE "^^" ";" SOURCES "${SOURCES}")
+
+FOREACH(FILE ${SOURCES})
+
+  # what is the filename without the extension
+  GET_FILENAME_COMPONENT(TMP_FILENAME ${FILE} NAME_WE)
+    
+  SET(includes 
+    "${includes}\n#include \"${TMP_FILENAME}.h\"")
+        
+  # Extract classname - NOTE: We assume the filename matches the associated class
+  set(className ${TMP_FILENAME})
+  #message(STATUS "FILE:${FILE}, className:${className}")
+
+  SET(objectTypes "${objectTypes}\n  <object-type name=\"${className}\"/>")
+
+ENDFOREACH()
+
+# Write master include file
+  FILE(WRITE ${OUTPUT_DIR}/${WRAP_INT_DIR}ctkPythonQt_${TARGET}_masterinclude.h "
+#ifndef __ctkPythonQt_${TARGET}_masterinclude_h
+#define __ctkPythonQt_${TARGET}_masterinclude_h
+${includes}
+#endif
+")
+
+# Write Typesystem file
+FILE(WRITE ${OUTPUT_DIR}/${WRAP_INT_DIR}typesystem_${TARGET}.xml "
+<typesystem package=\"${WRAPPING_NAMESPACE}.${TARGET}\">
+  ${objectTypes}
+</typesystem>
+")
+
+# Extract PYTHONQTGENERATOR_DIR 
+GET_FILENAME_COMPONENT(PYTHONQTGENERATOR_DIR ${PYTHONQTGENERATOR_EXECUTABLE} PATH)
+#message(PYTHONQTGENERATOR_DIR:${PYTHONQTGENERATOR_DIR})
+
+# Write Build file
+FILE(WRITE ${OUTPUT_DIR}/${WRAP_INT_DIR}build_${TARGET}.txt "
+<!-- File auto-generated by cmake macro ctkScriptWrapPythonQt_Full -->
+
+<typesystem>
+  <load-typesystem name=\"${PYTHONQTGENERATOR_DIR}/typesystem_core.xml\" generate=\"no\" />
+  <load-typesystem name=\"${PYTHONQTGENERATOR_DIR}/typesystem_gui.xml\" generate=\"no\" />
+  <load-typesystem name=\"${OUTPUT_DIR}/${WRAP_INT_DIR}/typesystem_${TARGET}.xml\" generate=\"yes\" />
+</typesystem>
+")
+
+# Compute QTDIR
+GET_FILENAME_COMPONENT(QTDIR ${QT_QMAKE_EXECUTABLE}/../../ REALPATH)
+SET(ENV{QTDIR} ${QTDIR})
+
+EXECUTE_PROCESS(
+  COMMAND ${PYTHONQTGENERATOR_EXECUTABLE} --debug-level=sparse --include-paths=${INCLUDE_DIRS} --output-directory=${OUTPUT_DIR} ${OUTPUT_DIR}/${WRAP_INT_DIR}ctkPythonQt_${TARGET}_masterinclude.h ${OUTPUT_DIR}/${WRAP_INT_DIR}build_${TARGET}.txt
+  WORKING_DIRECTORY ${PYTHONQTGENERATOR_DIR}
+  RESULT_VARIABLE result
+  #OUTPUT_VARIABLE output
+  ERROR_VARIABLE error
+  OUTPUT_QUIET
+  )
+#message(${error})
+IF(result)
+  MESSAGE(FATAL_ERROR "Failed to generate ${WRAPPING_NAMESPACE_UNDERSCORE}_${TARGET}_init.cpp\n${error}")
+ENDIF()
+
+# Since PythonQtGenerator or FILE(WRITE ) doesn't 'update the timestamp - Let's touch the files
+EXECUTE_PROCESS(
+  COMMAND ${CMAKE_COMMAND} -E touch 
+    ${OUTPUT_DIR}/${WRAP_INT_DIR}${WRAPPING_NAMESPACE_UNDERSCORE}_${TARGET}_init.cpp
+    ${OUTPUT_DIR}/${WRAP_INT_DIR}ctkPythonQt_${TARGET}_masterinclude.h
+  )
+

+ 244 - 0
CMake/ctkScriptWrapPythonQt_Light.cmake

@@ -0,0 +1,244 @@
+###########################################################################
+#
+#  Library:   CTK
+# 
+#  Copyright (c) 2010  Kitware Inc.
+#
+#  Licensed under the Apache License, Version 2.0 (the "License");
+#  you may not use this file except in compliance with the License.
+#  You may obtain a copy of the License at
+#
+#      http://www.commontk.org/LICENSE
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+# 
+###########################################################################
+
+#
+# ctkScriptWrapPythonQt_Light
+#
+
+#
+# Depends on:
+#  CTK/CMake/ctkMacroWrapPythonQt.cmake
+#
+
+#
+# This script should be invoked either as a CUSTOM_COMMAND 
+# or from the command line using the following syntax:
+#
+#    cmake -DWRAPPING_NAMESPACE:STRING=org.commontk -DTARGET:STRING=MyLib 
+#          -DSOURCES:STRING="file1^^file2" -DINCLUDE_DIRS:STRING=/path1:/path2
+#          -DWRAP_INT_DIR:STRING=subir/subir/
+#          -DOUTPUT_DIR:PATH=/path  -DQT_QMAKE_EXECUTABLE:PATH=/path/to/qt/qmake
+#          -DPYTHON_EXECUTABLE:FILEPATH=/path/to/python
+#          -P ctkScriptWrapPythonQt_Light.cmake
+#
+
+#
+# LOG FILE:
+#   File ctkScriptWrapPythonQt_Light_log.txt will be created in the current directory. 
+#   It will contain the list of class and the constructor signature that will be wrapped.
+#
+
+set(verbose 0)
+
+#
+# Convenient function allowing to log the reason why a given class hasn't been wrapped
+# If verbose=1, it will also be displayed on the standard output
+#
+FUNCTION(log msg)
+  IF(verbose)
+    MESSAGE(${msg})
+  ENDIF()
+  FILE(APPEND "${CMAKE_CURRENT_BINARY_DIR}/ctkScriptWrapPythonQt_Light_log.txt" "${msg}\n")
+ENDFUNCTION()
+
+#
+# Convenient function allowing to invoke re.search(regex, string) using the given interpreter.
+# Note that is_matching will be set to True if there is a match
+#
+FUNCTION(reSearchFile python_exe regex file is_matching)
+
+  set(python_cmd "import re\; f = open('${file}', 'r')\;
+res = re.search\(\"${regex}\", f.read(), re.MULTILINE\)\;
+if res == None: print \"FALSE\" 
+else: print \"TRUE\"
+")
+  #message("python_cmd: ${python_cmd}")
+
+  EXECUTE_PROCESS(
+    COMMAND ${python_exe} -c ${python_cmd};
+    RESULT_VARIABLE result
+    OUTPUT_VARIABLE output
+    ERROR_VARIABLE error
+    OUTPUT_STRIP_TRAILING_WHITESPACE
+    )
+  
+  IF(result)
+    MESSAGE(SEND_ERROR "reSearchFile - Problem with regex: ${regex}\n${error}")
+  ENDIF()
+  #message(${output})
+  SET(is_matching ${output} PARENT_SCOPE)
+  
+ENDFUNCTION()
+
+
+# Check for non-defined var
+FOREACH(var WRAPPING_NAMESPACE TARGET SOURCES INCLUDE_DIRS WRAP_INT_DIR)
+  IF(NOT DEFINED ${var})
+    MESSAGE(SEND_ERROR "${var} not specified when calling ctkScriptWrapPythonQt")
+  ENDIF()
+ENDFOREACH()
+
+# Check for non-existing ${var}
+FOREACH(var QT_QMAKE_EXECUTABLE OUTPUT_DIR PYTHON_EXECUTABLE)
+  IF(NOT EXISTS ${${var}})
+    MESSAGE(SEND_ERROR "Failed to find ${var} when calling ctkScriptWrapPythonQt")
+  ENDIF()
+ENDFOREACH()
+
+# Clear log file
+FILE(WRITE "${CMAKE_CURRENT_BINARY_DIR}/ctkScriptWrapPythonQt_Light_log.txt" "")
+  
+# Convert wrapping namespace to subdir
+STRING(REPLACE "." "_" WRAPPING_NAMESPACE_UNDERSCORE ${WRAPPING_NAMESPACE})
+
+# Convert ^^ separated string to list
+STRING(REPLACE "^^" ";" SOURCES "${SOURCES}")
+
+FOREACH(FILE ${SOURCES})
+
+  # what is the filename without the extension
+  GET_FILENAME_COMPONENT(TMP_FILENAME ${FILE} NAME_WE)
+      
+  SET(includes 
+    "${includes}\n#include \"${TMP_FILENAME}.h\"")
+        
+  # Extract classname - NOTE: We assume the filename matches the associated class
+  set(className ${TMP_FILENAME})
+  #message(STATUS "FILE:${FILE}, className:${className}")
+  
+  # Extract parent classname
+  SET(parentClassName)
+  
+  IF("${parentClassName}" STREQUAL "")
+    # Does constructor signature is of the form: myclass()
+    SET(regex "[^~]${className}[\\s\\n]*\\([\\s\\n]*\\)")
+    reSearchFile(${PYTHON_EXECUTABLE} ${regex} ${FILE} is_matching)
+    IF(is_matching)
+      SET(parentClassName "No")
+      log("${TMP_FILENAME} - constructor of the form: ${className}\(\)")
+    ENDIF()
+  ENDIF()
+  
+  IF("${parentClassName}" STREQUAL "")
+    # Does constructor signature is of the form: myclass(QObject * parent ...)
+    SET(regex "${className}[\\s\\n]*\\([\\s\\n]*QObject[\\s\\n]*\\*[\\s\\n]*\\w+[\\s\\n]*(\\=[\\s\\n]*(0|NULL)|,.*\\=.*\\)|\\))")
+    reSearchFile(${PYTHON_EXECUTABLE} ${regex} ${FILE} is_matching)
+    IF(is_matching)
+      SET(parentClassName "QObject")
+      log("${TMP_FILENAME} - constructor of the form: ${className}\(QObject * parent ... \)")
+    ENDIF()
+  ENDIF()
+  
+  IF("${parentClassName}" STREQUAL "")
+    # Does constructor signature is of the form: myclass(QWidget * parent ...)
+    SET(regex "${className}[\\s\\n]*\\([\\s\\n]*QWidget[\\s\\n]*\\*[\\s\\n]*\\w+[\\s\\n]*(\\=[\\s\\n]*(0|NULL)|,.*\\=.*\\)|\\))")
+    reSearchFile(${PYTHON_EXECUTABLE} ${regex} ${FILE} is_matching)
+    IF(is_matching)
+      SET(parentClassName "QWidget")
+      log("${TMP_FILENAME} - constructor of the form: ${className}\(QWidget * parent ... \)")
+    ENDIF()
+  ENDIF()
+ 
+  # Generate PythonQtWrapper class
+  IF("${parentClassName}" STREQUAL "QObject" OR "${parentClassName}" STREQUAL "QWidget")
+  
+    SET(pythonqtWrappers 
+      "${pythonqtWrappers}
+//-----------------------------------------------------------------------------
+class PythonQtWrapper_${className} : public QObject
+{
+Q_OBJECT
+public:
+public slots:
+  ${className}* new_${className}(${parentClassName}*  parent = 0)
+    {
+    return new ${className}(parent);
+    }
+  void delete_${className}(${className}* obj) { delete obj; }
+};
+")
+
+  ELSEIF("${parentClassName}" STREQUAL "No")
+  
+    SET(pythonqtWrappers 
+      "${pythonqtWrappers}
+//-----------------------------------------------------------------------------
+class PythonQtWrapper_${className} : public QObject
+{
+Q_OBJECT
+public:
+public slots:
+  ${className}* new_${className}()
+    {
+    return new ${className}();
+    }
+  void delete_${className}(${className}* obj) { delete obj; }
+};
+")
+
+  ELSE() # Case parentClassName is empty
+  
+    MESSAGE(WARNING "ctkScriptWrapPythonQt_Light - Problem wrapping ${FILE}")
+    
+  ENDIF()
+
+  # Generate code allowing to register the class metaobject and its associated "light" wrapper
+  SET(registerclasses "${registerclasses}
+  PythonQt::self()->registerClass(
+    &${className}::staticMetaObject, \"${TARGET}\",   
+    PythonQtCreateObject<PythonQtWrapper_${className}>);\n")
+
+ENDFOREACH()
+
+# Write master include file
+FILE(WRITE ${OUTPUT_DIR}/${WRAP_INT_DIR}${WRAPPING_NAMESPACE_UNDERSCORE}_${TARGET}0.h "//
+// File auto-generated by cmake macro ctkScriptWrapPythonQt_Light
+//
+
+#ifndef __${WRAPPING_NAMESPACE_UNDERSCORE}_${TARGET}0_h
+#define __${WRAPPING_NAMESPACE_UNDERSCORE}_${TARGET}0_h
+
+#include <QWidget>
+${includes}
+${pythonqtWrappers}
+#endif
+")
+
+# Write wrapper header
+FILE(WRITE ${OUTPUT_DIR}/${WRAP_INT_DIR}${WRAPPING_NAMESPACE_UNDERSCORE}_${TARGET}_init.cpp "//
+// File auto-generated by cmake macro ctkScriptWrapPythonQt_Light
+//
+
+#include <PythonQt.h>
+#include \"${WRAPPING_NAMESPACE_UNDERSCORE}_${TARGET}0.h\"
+
+void PythonQt_init_${WRAPPING_NAMESPACE_UNDERSCORE}_${TARGET}(PyObject* module)
+{
+  Q_UNUSED(module);
+  ${registerclasses}
+}
+")
+
+# Since FILE(WRITE ) doesn't update the timestamp - Let's touch the files
+EXECUTE_PROCESS(
+  COMMAND ${CMAKE_COMMAND} -E touch 
+    ${OUTPUT_DIR}/${WRAP_INT_DIR}${WRAPPING_NAMESPACE_UNDERSCORE}_${TARGET}_init.cpp
+  )
+

+ 66 - 0
CMakeExternals/PythonQtGenerator.cmake

@@ -0,0 +1,66 @@
+#
+# PythonQtGenerator
+#
+
+SET(PythonQtGenerator_DEPENDS)
+IF(CTK_WRAP_PYTHONQT_FULL)
+  
+  # Sanity checks
+  IF(DEFINED PYTHONQTGENERATOR_EXECUTABLE AND NOT EXISTS ${PYTHONQTGENERATOR_EXECUTABLE})
+    MESSAGE(FATAL_ERROR "PYTHONQTGENERATOR_EXECUTABLE variable is defined but corresponds to non-existing executable")
+  ENDIF()
+  
+  IF(NOT DEFINED PYTHONQTGENERATOR_EXECUTABLE)
+    
+    #
+    # PythonQtGenerator is the tool allowing to generate the PythonQt decorators using 
+    # typesystem xml files. If called without any option, it will generate the bindings for Qt.
+    #
+    # See http://www.pyside.org/docs/apiextractor/typesystem.html
+    # See http://doc.trolltech.com/qtjambi-4.5.2_01/com/trolltech/qt/qtjambi-typesystem.html
+    # See http://qt.gitorious.org/qt-labs/qtscriptgenerator
+    #
+    SET(proj PythonQtGenerator)
+  #   MESSAGE(STATUS "Adding project:${proj}")
+    SET(PythonQtGenerator_DEPENDS ${proj})
+    
+    #
+    # If directory PythonQt already exists, the corresponding project will 
+    # be cloned. Since PythonQt and PythonQtGenerator source code are hosted 
+    # on the same repository, we will assume that if the directory PythonQt 
+    # exists, the generator code will also be available.
+    #
+    IF(EXISTS ${ep_source_dir}/PythonQt)
+      #MESSAGE(STATUS "ExternalProject/PythonQtGenerator: PythonQt already added as ExternalProject")
+      ExternalProject_Add(${proj}
+        DOWNLOAD_COMMAND ""
+        CMAKE_GENERATOR ${gen}
+        INSTALL_COMMAND ""
+        SOURCE_DIR ${ep_source_dir}/PythonQt/generator
+        CMAKE_ARGS
+          ${ep_common_args}
+          -DQT_QMAKE_EXECUTABLE:FILEPATH=${QT_QMAKE_EXECUTABLE}
+        DEPENDS
+          "PythonQt" # To make sure the generator code is checked out, let's depent on PythonQt
+        )
+    ELSE()
+      #MESSAGE(STATUS "ExternalProject/PythonQtGenerator: PythonQt is NOT an ExternalProject")
+      ExternalProject_Add(${proj}
+        GIT_REPOSITORY "${git_protocol}://github.com/commontk/PythonQt.git"
+        GIT_TAG "patched"
+        CMAKE_GENERATOR ${gen}
+        INSTALL_COMMAND ""
+        SOURCE_DIR ${ep_source_dir}/${proj}/generator
+        CMAKE_ARGS
+          ${ep_common_args}
+          -DQT_QMAKE_EXECUTABLE:FILEPATH=${QT_QMAKE_EXECUTABLE}
+        )
+    ENDIF()
+    
+    SET(PYTHONQTGENERATOR_EXECUTABLE ${ep_build_dir}/PythonQtGenerator/PythonQtGenerator)
+    
+    # Since PythonQtGenerator is an executable, there is no need to add its corresponding 
+    # library output directory to CTK_EXTERNAL_LIBRARY_DIRS
+        
+  ENDIF()
+ENDIF()

+ 17 - 1
CMakeLists.txt

@@ -123,10 +123,11 @@ SET(CMAKE_MODULE_PATH
   ${CMAKE_MODULE_PATH})
 
 #-----------------------------------------------------------------------------
-# Clear CTK_BASE_INCLUDE_DIRS and CTK_BASE_LIBRARIES
+# Clear CTK_BASE_INCLUDE_DIRS, CTK_BASE_LIBRARIES and CTK_WRAPPED_LIBRARIES_PYTHONQT
 #
 SET(CTK_BASE_LIBRARIES CACHE INTERNAL "CTK base libraries" FORCE)
 SET(CTK_BASE_INCLUDE_DIRS CACHE INTERNAL "CTK includes" FORCE)
+SET(CTK_WRAPPED_LIBRARIES_PYTHONQT CACHE INTERNAL "CTK libraries wrapped using PythonQt" FORCE)
 
 # Variable use in CTKConfig.cmake.in
 SET(CTK_LIBRARIES CACHE INTERNAL "CTK libraries" FORCE)
@@ -141,6 +142,7 @@ INCLUDE(CMake/ctkMacroBuildPlugin.cmake)
 INCLUDE(CMake/ctkMacroBuildApp.cmake)
 INCLUDE(CMake/ctkMacroBuildQtDesignerPlugin.cmake)
 INCLUDE(CMake/ctkMacroCompilePythonScript.cmake)
+INCLUDE(CMake/ctkMacroWrapPythonQt.cmake)
 INCLUDE(CMake/ctkMacroSetupQt.cmake)
 INCLUDE(CMake/ctkMacroTargetLibraries.cmake) # Import multiple macros
 INCLUDE(CMake/ctkFunctionExtractOptionNameAndValue.cmake)
@@ -354,6 +356,20 @@ FOREACH(app ${CTK_APPLICATIONS})
 ENDFOREACH()
 
 #-----------------------------------------------------------------------------
+# CTK Python wrapping - Propose the option only if CTK_LIB_Scriping/Python/Core is ON
+IF(CTK_LIB_Scripting/Python/Core)
+  OPTION(CTK_WRAP_PYTHONQT_FULL "Wrap CTK classes using Qt meta-object system into Python language" OFF)
+  OPTION(CTK_WRAP_PYTHONQT_LIGHT "Wrap CTK classes using Qt meta-object system into Python language" OFF)
+  IF(CTK_WRAP_PYTHONQT_FULL AND CTK_WRAP_PYTHONQT_LIGHT)
+    MESSAGE(SEND_ERROR "CTK_WRAP_PYTHONQT_{LIGHT,FULL} options are mutually exclusive. Please enable only one !")
+  ENDIF()
+ELSE()
+  # Note that an unset boolean variable is considered to be False
+  UNSET(CTK_WRAP_PYTHONQT_FULL CACHE)
+  UNSET(CTK_WRAP_PYTHONQT_LIGHT CACHE)
+ENDIF()
+
+#-----------------------------------------------------------------------------
 # Generate target_directories list - List of directory corresponding to the different
 # libraries, plugins and applications associated with the corresponding option name.
 #

+ 7 - 1
CTKConfig.cmake.in

@@ -43,6 +43,9 @@
 # List all libraries
 SET(CTK_LIBRARIES @CTK_LIBRARIES@)
 
+# List all CTK libraries wrapped with PythonQt
+SET(CTK_WRAPPED_LIBRARIES_PYTHONQT @CTK_WRAPPED_LIBRARIES_PYTHONQT@)
+
 @CTK_CONFIG_INSTALL_ONLY@
 
 # The CTK include file directories.
@@ -80,7 +83,10 @@ SET(CTK_RUNTIME_LIBRARY_DIRS "@CTK_RUNTIME_LIBRARY_DIRS_CONFIG@")
 # will be the parent directories under which there will be a directory
 # of runtime libraries for each configuration type.
 SET(CTK_QTDESIGNERPLUGINS_DIR "@CTK_QTDESIGNERPLUGINS_DIR_CONFIG@")
- 
+
+# CTK PythonQt install directory
+SET(CTK_PYTHONQT_INSTALL_DIR "@CTK_PYTHONQT_INSTALL_DIR_CONFIG@")
+
 # The CTK version number
 SET(CTK_MAJOR_VERSION "@CTK_MAJOR_VERSION@")
 SET(CTK_MINOR_VERSION "@CTK_MINOR_VERSION@")

+ 8 - 1
SuperBuild.cmake

@@ -114,6 +114,7 @@ SET(external_projects
   Log4Qt
   KWStyle
   PythonQt
+  PythonQtGenerator # Should be added after PythonQt - See comment in CMakeExternals/PythonQtGenerator.cmake
   DCMTK
   ZMQ
   QtMobility
@@ -145,10 +146,11 @@ ExternalProject_Add(${proj}
     ${kwstyle_DEPENDS}
     ${DCMTK_DEPENDS}
     ${PythonQt_DEPENDS}
+    ${PythonQtGenerator_DEPENDS}
     ${ZMQ_DEPENDS}
     ${OpenIGTLink_DEPENDS}
     ${VTK_DEPENDS}
-    ${XIP_DEPENDS}
+    ${XIP_DEPENDS}    
 )
 
 #-----------------------------------------------------------------------------
@@ -179,6 +181,8 @@ SET(ctk_cmake_boolean_args
   WITH_COVERAGE
   DOCUMENTATION_TARGET_IN_ALL
   CTEST_USE_LAUNCHERS
+  CTK_WRAP_PYTHONQT_FULL
+  CTK_WRAP_PYTHONQT_LIGHT
   ${ctk_libs_bool_vars}
   ${ctk_plugins_bool_vars}
   ${ctk_applications_bool_vars}
@@ -217,15 +221,18 @@ ExternalProject_Add(${proj}
     -DCTK_C_FLAGS:STRING=${CTK_C_FLAGS}
     -DCTK_EXTERNAL_LIBRARY_DIRS:STRING=${CTK_EXTERNAL_LIBRARY_DIRS}
     -DQT_QMAKE_EXECUTABLE:FILEPATH=${QT_QMAKE_EXECUTABLE}
+    # External projects
     -DCTKData_DIR:PATH=${CTKData_DIR}
 	  -DZMQ_DIR:PATH=${ZMQ_DIR}                     # FindVTK expects VTK_DIR variable to be defined
 	  -DOpenIGTLink_DIR:PATH=${OpenIGTLink_DIR}     # FindOpenIGTLink expects OpenIGTLink_DIR variable to be defined
     -DCTK_KWSTYLE_EXECUTABLE:FILEPATH=${CTK_KWSTYLE_EXECUTABLE}
     -DDCMTK_DIR:PATH=${DCMTK_DIR} # FindDCMTK expects DCMTK_DIR variable to be defined
     -DVTK_DIR:PATH=${VTK_DIR}     # FindVTK expects VTK_DIR variable to be defined
+    -DPYTHON_EXECUTABLE:FILEPATH=${PYTHON_EXECUTABLE}   # FindPythonInterp expects PYTHON_EXECUTABLE variable to be defined
     -DPYTHON_INCLUDE_DIR:PATH=${PYTHON_INCLUDE_DIR}     # FindPythonQt expects PYTHON_INCLUDE_DIR variable to be defined
     -DPYTHON_LIBRARY:FILEPATH=${PYTHON_LIBRARY}         # FindPythonQt expects PYTHON_LIBRARY variable to be defined
     -DPYTHONQT_INSTALL_DIR:PATH=${PYTHONQT_INSTALL_DIR} # FindPythonQt expects PYTHONQT_INSTALL_DIR variable to be defined
+    -DPYTHONQTGENERATOR_EXECUTABLE:FILEPATH=${PYTHONQTGENERATOR_EXECUTABLE} #FindPythonQtGenerator expects PYTHONQTGENERATOR_EXECUTABLE to be defined
     -DLog4Qt_DIR:PATH=${Log4Qt_DIR} # FindLog4Qt expects Log4Qt_DIR variable to be defined
   SOURCE_DIR ${CTK_SOURCE_DIR}
   BINARY_DIR ${CTK_BINARY_DIR}/CTK-build

+ 13 - 0
Utilities/CMake/FindPythonQtGenerator.cmake

@@ -0,0 +1,13 @@
+# Find PythonQtGenerator
+#
+# Sets PYTHONQTGENERATOR_FOUND, PYTHONQTGENERATOR_EXECUTABLE
+#
+
+
+FIND_PROGRAM(PYTHONQTGENERATOR_EXECUTABLE PythonQtGenerator DOC "PythonQt Generator.")
+MARK_AS_ADVANCED(PYTHONQTGENERATOR_EXECUTABLE)
+
+SET(PYTHONQTGENERATOR_FOUND 0)
+IF(EXISTS PYTHONQTGENERATOR_EXECUTABLE)
+  SET(PYTHONQTGENERATOR_FOUND 1)
+ENDIF()

+ 3 - 0
Utilities/LastConfigureStep/CTKGenerateCTKConfig.cmake

@@ -87,6 +87,9 @@ SET(CTK_EXECUTABLE_DIRS_CONFIG ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
 # QtDesigner plugins directory
 SET(CTK_QTDESIGNERPLUGINS_DIR_CONFIG ${CMAKE_LIBRARY_OUTPUT_DIRECTORY})
 
+# PythonQt directory
+SET(CTK_PYTHONQT_INSTALL_DIR_CONFIG ${PYTHONQT_INSTALL_DIR})
+
 # Executable locations.
 
 # CMake extension module directory.