Skip to content

Commit 93c546e

Browse files
jcfrflorianlinkmwoehlke-kitwaremsmolenspatmarion
committed
[commontk] Re-enable CMake support importing historical improvements from commontk/PythonQt fork
This commit partially reverts r431 (which removed unsupported files and added documentation) and consolidates consolidates historical changes developed in the `commontk/PythonQt` fork between 2011 and 2021. Summary: * Qt 5support: * Added initial and ongoing support for Qt5, including modules like `PrintSupport`, `QuickWidgets`, `Multimedia`, and `OpenGL`. * Removed Qt4 support; fixed CMake logic for building across Qt 5.3–5.9. * Introduced `pythonqt_wrap_cpp` macro and cleaned up legacy Qt macros. * Build system enhancements: * Re-enabled and expanded CMake support with configurable install paths and testing. * Removed use of `INSTALL_NAME_DIR` to support relocatable installs. * Addressed build issues across toolchains (e.g., MSVC `/bigobj` workaround, debug mode fixes). * Added missing source files and build flags. * Code quality and compatibility: * Replaced deprecated/legacy constructs (e.g., use of `nullptr`, fixed property aliasing with `name` → `objectName`). * Added support for 511 wrappers. * Resolved warnings and linkage issues with optional build flags (e.g., `PythonQt_Wrap_Qtcore`). * Testing and cleanup: * Added cleanup/finalization unit tests. * Ensured test code compiles cleanly when wrapping is partially disabled. Co-authored-by: Florian Link <5535644+florianlink@users.noreply.github.com> Co-authored-by: Matthew Woehlke <matthew.woehlke@kitware.com> Co-authored-by: Max Smolens <max.smolens@kitware.com> Co-authored-by: Pat Marion <james.patrick.marion@gmail.com> Co-authored-by: Francois Budin <francois.budin@kitware.com> Co-authored-by: Christoph Willing <chris.willing@linux.com> Co-authored-by: Stefan Dinkelacker <s.dinkelacker@dkfz-heidelberg.de> Co-authored-by: Sylvain Bernhardt <sylvain.bernhardt@smith-nephew.com>
1 parent 751ec46 commit 93c546e

File tree

2 files changed

+383
-0
lines changed

2 files changed

+383
-0
lines changed

CMakeLists.txt

Lines changed: 380 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,380 @@
1+
#-----------------------------------------------------------------------------
2+
# NOTE: The CMake files have been contributed to PythonQt and have not been tested with the current
3+
# PythonQt version. They have not yet been updated to support Qt 5 and/or Python 3.
4+
#
5+
# If you are not a CMake expert, you should better use the provided qmake profiles.
6+
#-----------------------------------------------------------------------------
7+
8+
cmake_minimum_required(VERSION 3.5)
9+
10+
project(PythonQt)
11+
12+
#----------------------------------------------------------------------------
13+
# Qt version
14+
15+
# Set PythonQt_QT_VERSION
16+
set(PythonQt_QT_VERSION 5)
17+
18+
# Requirements
19+
set(minimum_required_qt5_version "5.3.0")
20+
set(minimum_required_qt_version ${minimum_required_qt${PythonQt_QT_VERSION}_version})
21+
22+
find_package(Qt5 ${minimum_required_qt_version} QUIET)
23+
set(QT_VERSION_MAJOR ${Qt5_VERSION_MAJOR})
24+
set(QT_VERSION_MINOR ${Qt5_VERSION_MINOR})
25+
26+
#----------------------------------------------------------------------------
27+
# Qt components
28+
set(qtlibs
29+
Core
30+
Widgets
31+
Network
32+
OpenGL
33+
Sql
34+
Svg
35+
Multimedia
36+
UiTools
37+
Xml
38+
XmlPatterns
39+
)
40+
# Webkit has been removed in Qt >= 5.6
41+
if("${QT_VERSION_MAJOR}.${QT_VERSION_MINOR}" VERSION_LESS "5.7")
42+
list(APPEND qtlibs
43+
WebKitWidgets
44+
)
45+
endif()
46+
if("${QT_VERSION_MAJOR}.${QT_VERSION_MINOR}" VERSION_GREATER "5.5")
47+
list(APPEND qtlibs
48+
Qml
49+
Quick
50+
)
51+
endif()
52+
53+
#-----------------------------------------------------------------------------
54+
# Python libraries
55+
56+
find_package(PythonLibs REQUIRED)
57+
include_directories("${PYTHON_INCLUDE_DIR}")
58+
add_definitions(
59+
-DPYTHONQT_USE_RELEASE_PYTHON_FALLBACK
60+
-DPYTHONQT_SUPPORT_NAME_PROPERTY
61+
)
62+
63+
#-----------------------------------------------------------------------------
64+
# Build options
65+
66+
if(NOT DEFINED PythonQt_INSTALL_RUNTIME_DIR)
67+
set(PythonQt_INSTALL_RUNTIME_DIR bin)
68+
endif()
69+
70+
if(NOT DEFINED PythonQt_INSTALL_LIBRARY_DIR)
71+
set(PythonQt_INSTALL_LIBRARY_DIR lib${LIB_SUFFIX})
72+
endif()
73+
74+
if(NOT DEFINED PythonQt_INSTALL_ARCHIVE_DIR)
75+
set(PythonQt_INSTALL_ARCHIVE_DIR lib${LIB_SUFFIX})
76+
endif()
77+
78+
if(NOT DEFINED PythonQt_INSTALL_INCLUDE_DIR)
79+
set(PythonQt_INSTALL_INCLUDE_DIR include/PythonQt)
80+
endif()
81+
82+
#-----------------------------------------------------------------------------
83+
# Set qtlib_to_wraplib_* variables
84+
85+
set(qtlib_to_wraplib_Widgets gui)
86+
set(qtlib_to_wraplib_WebKitWidgets webkit)
87+
88+
set(qt5_wrapped_lib_depends_gui Multimedia PrintSupport)
89+
set(qt5_wrapped_lib_depends_multimedia MultimediaWidgets)
90+
set(qt5_wrapped_lib_depends_quick QuickWidgets)
91+
92+
foreach(qtlib ${qtlibs})
93+
string(TOLOWER ${qtlib} qtlib_lowercase)
94+
if(DEFINED qtlib_to_wraplib_${qtlib})
95+
set(qtlib_lowercase ${qtlib_to_wraplib_${qtlib}})
96+
endif()
97+
set(qtlib_to_wraplib_${qtlib} ${qtlib_lowercase})
98+
endforeach()
99+
100+
#-----------------------------------------------------------------------------
101+
# Define PythonQt_Wrap_Qt* options
102+
option(PythonQt_Wrap_QtAll "Make all Qt components available in python" OFF)
103+
foreach(qtlib ${qtlibs})
104+
OPTION(PythonQt_Wrap_Qt${qtlib_to_wraplib_${qtlib}} "Make all of Qt${qtlib} available in python" OFF)
105+
endforeach()
106+
107+
#-----------------------------------------------------------------------------
108+
# Force option if it applies
109+
if(PythonQt_Wrap_QtAll)
110+
foreach(qtlib ${qtlibs})
111+
# XXX xmlpatterns wrapper does *NOT* build at all :(
112+
if(${qtlib} STREQUAL "XmlPatterns")
113+
continue()
114+
endif()
115+
set(qt_wrapped_lib ${qtlib_to_wraplib_${qtlib}})
116+
if(NOT ${PythonQt_Wrap_Qt${qt_wrapped_lib}})
117+
set(PythonQt_Wrap_Qt${qt_wrapped_lib} ON CACHE BOOL "Make all of Qt${qt_wrapped_lib} available in python" FORCE)
118+
message(STATUS "Enabling [PythonQt_Wrap_Qt${qt_wrapped_lib}] because of [PythonQt_Wrap_QtAll] evaluates to True")
119+
endif()
120+
endforeach()
121+
endif()
122+
123+
option(PythonQt_DEBUG "Enable/Disable PythonQt debug output" OFF)
124+
if(PythonQt_DEBUG)
125+
add_definitions(-DPYTHONQT_DEBUG)
126+
else()
127+
remove_definitions(-DPYTHONQT_DEBUG)
128+
endif()
129+
130+
#-----------------------------------------------------------------------------
131+
# Setup Qt
132+
133+
# Required components
134+
set(qt_required_components Core Widgets)
135+
foreach(qtlib ${qtlibs})
136+
set(qt_wrapped_lib ${qtlib_to_wraplib_${qtlib}})
137+
if(${PythonQt_Wrap_Qt${qt_wrapped_lib}})
138+
list(APPEND qt_required_components ${qtlib} ${qt${PythonQt_QT_VERSION}_wrapped_lib_depends_${qt_wrapped_lib}})
139+
endif()
140+
endforeach()
141+
if(BUILD_TESTING)
142+
list(APPEND qt_required_components Test)
143+
endif()
144+
list(REMOVE_DUPLICATES qt_required_components)
145+
146+
message(STATUS "${PROJECT_NAME}: Required Qt components [${qt_required_components}]")
147+
find_package(Qt5 ${minimum_required_qt_version} COMPONENTS ${qt_required_components} REQUIRED)
148+
149+
set(QT_LIBRARIES )
150+
foreach(qtlib ${qt_required_components})
151+
include_directories(${Qt5${qtlib}_INCLUDE_DIRS})
152+
add_definitions(${Qt5${qtlib}_DEFINITIONS})
153+
list(APPEND QT_LIBRARIES ${Qt5${qtlib}_LIBRARIES})
154+
endforeach()
155+
156+
# Required for use of "QtCore/private/qmetaobjectbuilder_p.h" in "PythonQt.cpp"
157+
include_directories(${Qt5Core_PRIVATE_INCLUDE_DIRS})
158+
159+
macro(pythonqt_wrap_cpp)
160+
qt5_wrap_cpp(${ARGV})
161+
endmacro()
162+
163+
if(UNIX)
164+
find_package(OpenGL)
165+
if(OPENGL_FOUND)
166+
list(APPEND QT_LIBRARIES ${OPENGL_LIBRARIES})
167+
endif()
168+
endif()
169+
170+
#-----------------------------------------------------------------------------
171+
# The variable "generated_cpp_suffix" allows to conditionnally compile the generated wrappers
172+
# associated with the Qt version being used.
173+
174+
set(generated_cpp_suffix_52 _50)
175+
set(generated_cpp_suffix_51 _50)
176+
177+
set(generated_cpp_suffix "_${QT_VERSION_MAJOR}${QT_VERSION_MINOR}")
178+
if(DEFINED generated_cpp_suffix_${QT_VERSION_MAJOR}${QT_VERSION_MINOR})
179+
set(generated_cpp_suffix "${generated_cpp_suffix_${QT_VERSION_MAJOR}${QT_VERSION_MINOR}}")
180+
elseif("${QT_VERSION_MAJOR}.${QT_VERSION_MINOR}" VERSION_GREATER "5.10")
181+
set(generated_cpp_suffix "_511")
182+
elseif("${QT_VERSION_MAJOR}.${QT_VERSION_MINOR}" VERSION_GREATER "5.5")
183+
set(generated_cpp_suffix "_56")
184+
elseif("${QT_VERSION_MAJOR}.${QT_VERSION_MINOR}" VERSION_GREATER "5.3")
185+
set(generated_cpp_suffix "_54")
186+
endif()
187+
188+
#-----------------------------------------------------------------------------
189+
# Sources
190+
191+
set(sources
192+
src/PythonQtBoolResult.cpp
193+
src/PythonQtClassInfo.cpp
194+
src/PythonQtClassWrapper.cpp
195+
src/PythonQtConversion.cpp
196+
src/PythonQt.cpp
197+
src/PythonQtImporter.cpp
198+
src/PythonQtInstanceWrapper.cpp
199+
src/PythonQtMethodInfo.cpp
200+
src/PythonQtMisc.cpp
201+
src/PythonQtObjectPtr.cpp
202+
src/PythonQtProperty.cpp
203+
src/PythonQtQFileImporter.cpp
204+
src/PythonQtSignalReceiver.cpp
205+
src/PythonQtSlot.cpp
206+
src/PythonQtSlotDecorator.cpp
207+
src/PythonQtSignal.cpp
208+
src/PythonQtStdDecorators.cpp
209+
src/PythonQtStdIn.cpp
210+
src/PythonQtStdOut.cpp
211+
src/PythonQtThreadSupport.cpp
212+
src/gui/PythonQtScriptingConsole.cpp
213+
214+
generated_cpp${generated_cpp_suffix}/PythonQt_QtBindings.cpp
215+
216+
generated_cpp${generated_cpp_suffix}/com_trolltech_qt_core_builtin/com_trolltech_qt_core_builtin0.cpp
217+
generated_cpp${generated_cpp_suffix}/com_trolltech_qt_core_builtin/com_trolltech_qt_core_builtin_init.cpp
218+
generated_cpp${generated_cpp_suffix}/com_trolltech_qt_gui_builtin/com_trolltech_qt_gui_builtin0.cpp
219+
generated_cpp${generated_cpp_suffix}/com_trolltech_qt_gui_builtin/com_trolltech_qt_gui_builtin_init.cpp
220+
)
221+
222+
#-----------------------------------------------------------------------------
223+
# List headers. This is list is used for the install command.
224+
225+
set(headers
226+
src/PythonQtBoolResult.h
227+
src/PythonQtClassInfo.h
228+
src/PythonQtClassWrapper.h
229+
src/PythonQtConversion.h
230+
src/PythonQtCppWrapperFactory.h
231+
src/PythonQtDoc.h
232+
src/PythonQt.h
233+
src/PythonQtImporter.h
234+
src/PythonQtImportFileInterface.h
235+
src/PythonQtInstanceWrapper.h
236+
src/PythonQtMethodInfo.h
237+
src/PythonQtMisc.h
238+
src/PythonQtObjectPtr.h
239+
src/PythonQtProperty.h
240+
src/PythonQtQFileImporter.h
241+
src/PythonQtSignalReceiver.h
242+
src/PythonQtSlot.h
243+
src/PythonQtSlotDecorator.h
244+
src/PythonQtSignal.h
245+
src/PythonQtStdDecorators.h
246+
src/PythonQtStdIn.h
247+
src/PythonQtStdOut.h
248+
src/PythonQtSystem.h
249+
src/PythonQtThreadSupport.h
250+
src/PythonQtUtils.h
251+
src/PythonQtVariants.h
252+
src/PythonQtPythonInclude.h
253+
generated_cpp${generated_cpp_suffix}/PythonQt_QtBindings.h
254+
)
255+
256+
#-----------------------------------------------------------------------------
257+
# Headers that should run through moc
258+
259+
set(moc_sources
260+
src/PythonQt.h
261+
src/PythonQtSignalReceiver.h
262+
src/PythonQtStdDecorators.h
263+
src/gui/PythonQtScriptingConsole.h
264+
265+
generated_cpp${generated_cpp_suffix}/com_trolltech_qt_core_builtin/com_trolltech_qt_core_builtin0.h
266+
generated_cpp${generated_cpp_suffix}/com_trolltech_qt_gui_builtin/com_trolltech_qt_gui_builtin0.h
267+
)
268+
269+
#-----------------------------------------------------------------------------
270+
# Add extra sources
271+
272+
foreach(qtlib ${qtlibs})
273+
274+
set(qt_wrapped_lib ${qtlib_to_wraplib_${qtlib}})
275+
276+
if (${PythonQt_Wrap_Qt${qt_wrapped_lib}})
277+
278+
ADD_DEFINITIONS(-DPYTHONQT_WRAP_Qt${qt_wrapped_lib})
279+
280+
set(file_prefix generated_cpp${generated_cpp_suffix}/com_trolltech_qt_${qt_wrapped_lib}/com_trolltech_qt_${qt_wrapped_lib})
281+
282+
foreach(index RANGE 0 12)
283+
284+
# Source files
285+
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${file_prefix}${index}.cpp)
286+
list(APPEND sources ${file_prefix}${index}.cpp)
287+
endif()
288+
289+
# Headers that should run through moc
290+
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${file_prefix}${index}.h)
291+
list(APPEND moc_sources ${file_prefix}${index}.h)
292+
endif()
293+
294+
endforeach()
295+
296+
list(APPEND sources ${file_prefix}_init.cpp)
297+
298+
endif()
299+
endforeach()
300+
301+
#-----------------------------------------------------------------------------
302+
# Do wrapping
303+
pythonqt_wrap_cpp(gen_moc_sources ${moc_sources})
304+
305+
#-----------------------------------------------------------------------------
306+
# Build the library
307+
308+
include_directories(
309+
${CMAKE_CURRENT_SOURCE_DIR}/src
310+
)
311+
312+
add_library(PythonQt SHARED
313+
${sources}
314+
${gen_moc_sources}
315+
)
316+
set_target_properties(PythonQt PROPERTIES DEFINE_SYMBOL PYTHONQT_EXPORTS)
317+
318+
target_compile_options(PythonQt PRIVATE
319+
$<$<CXX_COMPILER_ID:MSVC>:/bigobj>
320+
)
321+
322+
target_link_libraries(PythonQt
323+
${PYTHON_LIBRARY}
324+
${QT_LIBRARIES}
325+
)
326+
327+
#-----------------------------------------------------------------------------
328+
# Install library (on windows, put the dll in 'bin' and the archive in 'lib')
329+
330+
install(TARGETS PythonQt
331+
RUNTIME DESTINATION ${PythonQt_INSTALL_RUNTIME_DIR}
332+
LIBRARY DESTINATION ${PythonQt_INSTALL_LIBRARY_DIR}
333+
ARCHIVE DESTINATION ${PythonQt_INSTALL_ARCHIVE_DIR})
334+
install(FILES ${headers} DESTINATION ${PythonQt_INSTALL_INCLUDE_DIR})
335+
336+
#-----------------------------------------------------------------------------
337+
# Testing
338+
339+
option(BUILD_TESTING "Build the testing tree." OFF)
340+
include(CTest)
341+
342+
if(BUILD_TESTING)
343+
create_test_sourcelist(test_sources PythonQtCppTests.cpp
344+
tests/PythonQtTestMain.cpp
345+
)
346+
347+
set_property(SOURCE tests/PythonQtTestMain.cpp PROPERTY COMPILE_DEFINITIONS "main=tests_PythonQtTestMain")
348+
349+
list(APPEND test_sources
350+
tests/PythonQtTests.cpp
351+
tests/PythonQtTests.h
352+
)
353+
354+
pythonqt_wrap_cpp(test_sources
355+
tests/PythonQtTests.h
356+
)
357+
358+
if(PythonQt_Wrap_Qtcore)
359+
include_directories(generated_cpp${generated_cpp_suffix})
360+
361+
list(APPEND test_sources
362+
tests/PythonQtTestCleanup.cpp
363+
tests/PythonQtTestCleanup.h
364+
)
365+
pythonqt_wrap_cpp(test_sources
366+
tests/PythonQtTestCleanup.h
367+
)
368+
369+
set_property(SOURCE tests/PythonQtTestMain.cpp APPEND PROPERTY COMPILE_DEFINITIONS "PythonQt_Wrap_Qtcore")
370+
endif()
371+
372+
add_executable(PythonQtCppTests ${test_sources})
373+
target_link_libraries(PythonQtCppTests PythonQt)
374+
375+
add_test(
376+
NAME tests_PythonQtTestMain
377+
COMMAND ${Slicer_LAUNCH_COMMAND} $<TARGET_FILE:PythonQtCppTests> tests/PythonQtTestMain
378+
)
379+
endif()
380+

tests/PythonQtTestMain.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ int main(int argc, char *argv[])
7070
Py_Finalize();
7171
}
7272

73+
#ifdef PythonQt_Wrap_Qtcore
7374
PythonQtTestCleanup cleanup;
7475
failCount += QTest::qExec(&cleanup, argc, argv);
7576

@@ -78,6 +79,8 @@ int main(int argc, char *argv[])
7879
} else {
7980
std::cout << "All tests passed successfully." << std::endl;
8081
}
82+
#endif
83+
8184
return failCount != 0 ? EXIT_FAILURE : EXIT_SUCCESS;
8285
}
8386

0 commit comments

Comments
 (0)