Skip to content

WIP: NOT READY FOR REVIEW Slicer 2025 06 13 a2687828 #269

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 34 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
bf0ff9e
ENH: add CMake build system support and update CI
Nov 26, 2024
f1df0ec
DOC: Add documentation for installing Qt6
hjmjohnson Jun 25, 2025
a9c14ab
PERF: Don't create temporary QRegularExpression
hjmjohnson Jun 25, 2025
3a2482d
BUG: Logic bug in contenating strings.
hjmjohnson Jun 25, 2025
2d950c7
COMP: Remove use of deprecated 'count()' function
hjmjohnson Jun 25, 2025
251544a
BUG: Fix PyUnicode/PyString usage for Qt6 compatibility
hjmjohnson Jun 25, 2025
f0c4ee6
REF: Replace Q_FOREACH with range-based for loops
hjmjohnson Jun 26, 2025
b80788b
ENH: If not set, choose Release BUILD_TYPE
hjmjohnson Jun 25, 2025
f280ece
ENH: Remove QT5 modules from Qt6 QTMODULES
hjmjohnson Jun 25, 2025
e487347
BUG: Remove deprecated Qt6 paradigms
hjmjohnson Jun 25, 2025
78687fb
ENH: Set old QT_VERSION_* variables.
hjmjohnson Jun 25, 2025
7e51b05
ENH: Add src to default include directories for PythonQt.h
hjmjohnson Jun 25, 2025
2fde0d7
ENH: Improve Qt include path resolution macOS
hjmjohnson Jun 26, 2025
317240f
ENH: Removing obsolete python2 support
hjmjohnson Jun 26, 2025
e388ac3
REF: Remove outdated `PY_VERSION_HEX` conditional branches
hjmjohnson Jun 26, 2025
aadc998
ENH: Define environment variables for Python and Qt versions in CMake
hjmjohnson Jun 26, 2025
5b1bf63
ENH: Update CMake to exclude qtscript_masterinclude.h and improve Qt5…
hjmjohnson Jun 26, 2025
8d5e204
ENH: Improve Qt include directory detection in generator CMake
hjmjohnson Jun 26, 2025
f23f420
ENH: Add file existence and readability checks for generator inputs
hjmjohnson Jun 26, 2025
ab78685
ENH: Add Qt6-specific build_all file and update generator resource ha…
hjmjohnson Jun 26, 2025
fad2ce8
ENH: Simplify Qt version handling in CMake and consolidate component …
hjmjohnson Jun 26, 2025
eaadef3
ENH: Add directory existence and readability checks for include paths…
hjmjohnson Jun 26, 2025
bc9a2c8
ENH: Add debug output for object tree dumping in generator
hjmjohnson Jun 26, 2025
11c251a
ENH: Add compile definitions to enforce safer casting rules in CMake
hjmjohnson Jun 26, 2025
bbd2ed8
ENH: Refactor CMakeLists to explicitly define public headers and sour…
hjmjohnson Jun 26, 2025
261ae9b
ENH: Enhance Qt6 include directory resolution
hjmjohnson Jun 26, 2025
986707e
ENH: Update generated path in CMake to include Qt version-specific su…
hjmjohnson Jun 26, 2025
a5b65ea
ENH: Refine Qt6 include path resolution for macOS framework support
hjmjohnson Jun 26, 2025
0f1618c
BUG: Fix generated path handling in CMake to remove incorrect version…
hjmjohnson Jun 26, 2025
8ab4791
BUG: Unsupported combination headers caused build error
hjmjohnson Jun 26, 2025
da40492
REF: Replace `qAsConst` with `std::as_const` for C++17 compatibility
hjmjohnson Jun 27, 2025
e886394
REF: Replace `QVariant::Type` with `QMetaType::Type` for Qt compatibi…
hjmjohnson Jun 27, 2025
005d4b6
REF: Update verbose/optimize flag handling for Python 3.11 compatibility
hjmjohnson Jun 27, 2025
48dd28f
ENH: Disable deprecated Qt features prior to version 6 in generator
hjmjohnson Jun 27, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions .github/workflows/cmake.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
name: CMake CI

on:
push:
branches:
- master

jobs:
build-and-test:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
qt_version: [5, 6]
python_version: ["3.8", "3.9", "3.10"]
fail-fast: false

steps:
- name: Checkout code
uses: actions/checkout@v3

- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python_version }}

- name: Install dependencies (Linux)
if: runner.os == 'Linux'
run: |
sudo apt-get update
if [ "${{ matrix.qt_version }}" = "5" ]; then
sudo apt-get install -y qtbase5-dev qtbase5-private-dev qtchooser qt5-qmake qtbase5-dev-tools \
libqt5svg5-dev qttools5-dev libqt5xmlpatterns5-dev qtmultimedia5-dev qtdeclarative5-dev \
qtwebengine5-dev libqt5webkit5-dev
echo "QTDIR=/usr/lib/x86_64-linux-gnu/qt5" | tee -a $GITHUB_ENV
else
sudo apt-get install -y qt6-base-dev qt6-base-private-dev qt6-5compat-dev qt6-base-dev-tools \
libqt6svg6-dev qt6-multimedia-dev qt6-declarative-dev qt6-webengine-dev
echo "QTDIR=/usr/lib/x86_64-linux-gnu/qt6" | tee -a $GITHUB_ENV
fi

- name: Install dependencies (Windows)
if: runner.os == 'Windows'
run: |
if ("${{ matrix.qt_version }}" -eq "5") {
pip install aqtinstall
aqt install-qt windows desktop 5.15.2 win64_msvc2019 -m all
$Qt5Dir = "$env:USERPROFILE\Qt\5.15.2\msvc2019_64"
echo "Qt5Dir=$Qt5Dir" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
echo "$Qt5Dir\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
echo "QTDIR=$Qt5Dir" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
} else {
choco install -y qt6-base-dev qt6-base-private-dev qt6-5compat-dev qt6-base-dev-tools `
libqt6svg6-dev qt6-multimedia-dev qt6-declarative-dev qt6-webengine-dev `
--params "/InstallationFolder C:/Qt/${{ matrix.qt_version }}"
echo "C:/Qt/${{ matrix.qt_version }}/bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
echo "QTDIR=C:/Qt/${{ matrix.qt_version }}" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
}

- name: Configure CMake
run: |
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=./install

- name: Build project
run: |
cmake --build build --parallel --target all install

- name: Run tests
run: |
cd build
ctest --output-on-failure
87 changes: 87 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
cmake_minimum_required(VERSION 3.27)

project(PythonQt LANGUAGES CXX VERSION 3.5.6)

#-----------------------------------------------------------------------------
# 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 'Release' as none was specified.")
set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build." FORCE)
mark_as_advanced(CMAKE_BUILD_TYPE)
# Set the possible values of build type for cmake-gui
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "RelWithDebInfo")
endif()
#-----------------------------------------------------------------------------

enable_testing()

set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# Force errors for dangerous operations
add_compile_definitions( -DQT_NO_CAST_TO_ASCII )

option(PythonQt_NO_IMPLICIT_CASTING "Disable implicit casts related to dangerous implicit conversions" OFF)
if(PythonQt_NO_IMPLICIT_CASTING) # Definitions to force
# errors on dangerous implicit operations
# to be addressed at a later time
add_compile_definitions( -DQT_NO_CAST_FROM_ASCII
-DQT_NO_CAST_FROM_ASCII
-DQT_NO_CAST_TO_BYTEARRAY
-DQT_NO_CAST_FROM_BYTEARRAY
-DQT_NO_CAST_TO_QSTRING
-DQT_NO_CAST_FROM_QSTRING
-DQT_NO_CAST_TO_QSTRINGREF
-DQT_NO_CAST_FROM_QSTRINGREF
-DQT_NO_CAST_TO_QVARIANT
-DQT_NO_CAST_FROM_QVARIANT )
endif()

option(FORCE_BUILD_QT5 "Force Qt5 build instead of Qt6" OFF)

if(FORCE_BUILD_QT5)
find_package(Qt5 NAMES Qt5 REQUIRED COMPONENTS Core Widgets Xml)
string(REPLACE "." ";" qt_version_list ${Qt5_VERSION})
else()
find_package(Qt6 NAMES Qt6 REQUIRED COMPONENTS Core Widgets Xml Core5Compat)
string(REPLACE "." ";" qt_version_list ${Qt6_VERSION})
endif()
list(GET qt_version_list 0 QT_VERSION_MAJOR)
list(GET qt_version_list 1 QT_VERSION_MINOR)
list(GET qt_version_list 1 QT_VERSION_PATCH)
message(STATUS "QT_VERSION_MAJOR = ${QT_VERSION_MAJOR}")

find_package(Python3 COMPONENTS Development REQUIRED)

# Set the environment as defined in Readme.md
# Set environment variables for Python
if(Python3_FOUND)
set(PYTHON_VERSION "${Python3_VERSION_MAJOR}.${Python3_VERSION_MINOR}")
set(PYTHON_PATH "${Python3_PREFIX}")
set(PYTHON_LIB "${Python3_PREFIX}/libs")
set(PYTHON_DIR "${Python3_PREFIX}")
set(ENV{PYTHON_VERSION} "${PYTHON_VERSION}")
set(ENV{PYTHON_PATH} "${PYTHON_PATH}")
set(ENV{PYTHON_LIB} "${PYTHON_LIB}")
set(ENV{PYTHON_DIR} "${PYTHON_DIR}")
endif()

# Set QTDIR environment variable to Qt installation root
if(FORCE_BUILD_QT5)
if(DEFINED Qt5_DIR)
set(ENV{QTDIR} "${Qt5_DIR}")
endif()
else()
if(DEFINED Qt6_DIR)
set(ENV{QTDIR} "${Qt6_DIR}")
endif()
endif()


set(PYTHONQT_SUFFIX Qt${QT_VERSION_MAJOR}.${QT_VERSION_MINOR}-Python${Python3_VERSION_MAJOR}.${Python3_VERSION_MINOR})
include_directories(src)
add_subdirectory(generator)
add_subdirectory(src)
add_subdirectory(extensions)
add_subdirectory(tests)
# add_subdirectory(examples)
70 changes: 63 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,20 +36,22 @@ need to build and run the generator when you want to build additional wrappers
or you want to upgrade/downgrade to another Qt version, but this requires
updating the typesystems as well.

# Building
# Building

## General
## Using qmake and manual steps

### General

Building PythonQt requires a couple of steps.
Follow these instructions in order to get a correctly built PythonQt runtime and Qt bindings.

### Recommendations
#### Recommendations

It is recommended to build the Qt bindings yourself instead of using the pregenerated ones.
This ensures the bindings are compatible with your Qt version.
Do not build `PythonQt.pro` directly because it will only use the pregenerated bindings!

### Environment
#### Environment

First, you need to set a couple of environment variables, which depend on your Python and Qt installation.

Expand All @@ -74,7 +76,7 @@ First, you need to set a couple of environment variables, which depend on your P

The absolute path to the root directory of your Qt installation.

### Binding Generator
#### Binding Generator

1. cd into the `generator` directory
2. Run qmake on `generator.pro`
Expand All @@ -93,7 +95,7 @@ First, you need to set a couple of environment variables, which depend on your P

`<generator-executable> qtscript_masterinclude.h build_all.txt`

### PythonQt Runtime
#### PythonQt Runtime

Next, we need the PythonQt runtime.

Expand All @@ -106,7 +108,7 @@ Next, we need the PythonQt runtime.

Use `nmake` for MSVC (Visual Studio; make sure to have the environment variables set for Visual Studio beforehand). Otherwise, use `make`.

### Extensions
#### Extensions

As a last step, we need to build the extensions.

Expand All @@ -121,6 +123,60 @@ As a last step, we need to build the extensions.

After all these steps, you should now have a fully working PythonQt runtime and Qt bindings for your Python/Qt installation 🎉.


## The CMake Build System replaces the qmake manual proceedure defined above

Get the source code from the GitHub.
```bash
git clone [email protected]:MeVisLab/PythonQt.git
cd PythonQt
git checkout <desired_branch>
```

Build the PythonQt runtime and Qt bindings using CMake.
```bash
cmake -S $(pwd) -B ../PythonQt-build -GNinja -DCMAKE_EXPORT_COMPILE_COMMANDS:BOOL=ON -DCMAKE_BUILD_TYPE:STRING=Release
cmake --build ../PythonQt-build --config Release
```


# Platform Notes
## MinGW

It is possible to build PythonQt with MinGW on Windows instead of using MSVC.

## Ubuntu 24.04 apt-get install QT6
It is possible to build PythonQt on Ubuntu 24.04 using the `apt` package manager to install the required Qt6 packages.
``` bash
apt install qt6-base-dev qt6-tools-dev-tools libqt6* qt6-* qml6-module-qt*
```

## arm64 based MacOS
It is possible to build PythonQt on arm64 based MacOS systems.

Download and install qt6 from https://www.qt.io/download. Install the desired Qt6.

# Hints for developers

## Upgrading Qt Versions

### Use `clazy` for static analysis of Qt code
```bash
# NOTE: clazy uses the cmake output file generated when CMAKE_EXPORT_COMPILE_COMMANDS=ON
export CLAZY_TEST="use-static-qregularexpression"

clazy-standalone -p ../PythonQt-build \
--checks="${CLAZY_TEST}" \
--only-qt \
$(find . -name "*.cpp" |fgrep -v "/examples/") \
2>&1 \
| fgrep -v "pragma-messages" \
| tee ../logger.${CLAZY_TEST}
vim -q ../logger.${CLAZY_TEST}
```


# List clazy tests available
```bash
clazy-standalone -p ../PythonQt-build --only-qt generator/main.cpp --list-checks >> ../MIGRATE_QT5_to_QT6
```
2 changes: 1 addition & 1 deletion examples/PyLauncher/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ int main( int argc, char **argv )
}
PythonQtScriptingConsole console(NULL, mainContext);

Q_FOREACH(QString file, files) {
for( QString file : files ) {
mainContext.evalFile(file);
}
if (showConsole || console.hadError()) {
Expand Down
1 change: 1 addition & 0 deletions extensions/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
add_subdirectory(PythonQt_QtAll)
94 changes: 94 additions & 0 deletions extensions/PythonQt_QtAll/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
project(QtAll LANGUAGES CXX)

set(CMAKE_AUTOMOC ON)

file(GLOB SOURCES *.h *.cpp)

if(BUILD_SHARED_LIBS)
add_library(${PROJECT_NAME} SHARED)
target_compile_definitions(${PROJECT_NAME} PRIVATE PYTHONQT_QTALL_EXPORTS)
else()
add_library(${PROJECT_NAME} STATIC)
target_compile_definitions(${PROJECT_NAME} PUBLIC PYTHONQT_QTALL_STATIC)
endif()

target_sources(${PROJECT_NAME} PRIVATE ${SOURCES})

target_link_libraries(${PROJECT_NAME} PUBLIC Core)

target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_LIST_DIR})

list(APPEND QTMODULES Core Gui Svg Sql Network OpenGL Xml Multimedia Qml Quick UiTools WebEngineWidgets)
if(${QT_VERSION_MAJOR} EQUAL 5) #QT5
list(APPEND QTMODULES XmlPatterns WebKit)
endif()
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS ${QTMODULES})

foreach(QtModule IN LISTS QTMODULES)
if(NOT TARGET "Qt${QT_VERSION_MAJOR}::${QtModule}")
continue()
endif()

string(TOUPPER ${QtModule} QTMODULE)
target_sources(${PROJECT_NAME} PRIVATE ${PYTHONQT_WRAPPER_${QTMODULE}_SOURCES})
target_link_libraries(${PROJECT_NAME} PUBLIC Qt${QT_VERSION_MAJOR}::${QtModule})
target_compile_definitions(${PROJECT_NAME} PRIVATE PYTHONQT_WITH_${QTMODULE})
endforeach()

if(TARGET "Qt${QT_VERSION_MAJOR}::Gui")
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Widgets PrintSupport REQUIRED)
target_link_libraries(${PROJECT_NAME} PUBLIC
Qt${QT_VERSION_MAJOR}::Widgets
Qt${QT_VERSION_MAJOR}::PrintSupport
)
endif()

if(TARGET "Qt${QT_VERSION_MAJOR}::Svg" AND QT_VERSION_MAJOR VERSION_GREATER_EQUAL 6)
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS SvgWidgets REQUIRED)
target_link_libraries(${PROJECT_NAME} PUBLIC
Qt${QT_VERSION_MAJOR}::SvgWidgets
)
endif()

if(TARGET "Qt${QT_VERSION_MAJOR}::Multimedia")
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS MultimediaWidgets REQUIRED)
target_link_libraries(${PROJECT_NAME} PUBLIC
Qt${QT_VERSION_MAJOR}::MultimediaWidgets
)
endif()

if(TARGET "Qt${QT_VERSION_MAJOR}::Quick")
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS QuickWidgets REQUIRED)
target_link_libraries(${PROJECT_NAME} PUBLIC
Qt${QT_VERSION_MAJOR}::QuickWidgets
)
endif()

if(TARGET "Qt${QT_VERSION_MAJOR}::WebKit")
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS WebKitWidgets REQUIRED)
target_link_libraries(${PROJECT_NAME} PUBLIC
Qt${QT_VERSION_MAJOR}::WebKitWidgets
)
endif()

file(GLOB PUBLIC_HEADER *.h)

set_target_properties(${PROJECT_NAME} PROPERTIES
OUTPUT_NAME PythonQt-QtAll-${PYTHONQT_SUFFIX}
PUBLIC_HEADER "${PUBLIC_HEADER}"
)

if(MSVC)
target_compile_options(${PROJECT_NAME} PRIVATE "/bigobj")
elseif(MINGW)
target_compile_options(${PROJECT_NAME} PRIVATE "-Wa,-mbig-obj")
endif()

include(GNUInstallDirs)
install(TARGETS ${PROJECT_NAME}
BUNDLE DESTINATION .
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)
Loading
Loading