Building from Source¶
Important
This page is focused on building Qt for Python from source. If you just want to install PySide6, you need to run: pip install pyside6.
For more details, refer to our Getting Started guide. Additionally, you can check the FAQ related to the project.
General Requirements¶
The following prerequisites must be installed before you build Qt for Python.
On Linux you might get them with your operating system package manager, on macOS
you might get them with brew
, and on Windows you can download the installer from each
website.
Python: 3.9+ [official Python website]
Qt: 6.8+ [online installer]
CMake: 3.18+ [official CMake website]
Git: 2.0+. [official Git website]
libclang: The libclang library, recommended: version 18+ for 6.8+. Prebuilt versions for each OS can be downloaded here.
Check the Supported Platforms of Qt
Guides per platform¶
You can refer to the following pages for platform specific instructions:
Important
Qt for Python does not yet support WebAssembly and the mobile operating systems (Android or iOS). Most Linux-based embedded OS provide PySide with their official package manager (for example, Raspbian and ArchlinuxARM).
A normal building command will look like this:
python setup.py install --qtpaths=/path/to/qtpaths \
--ignore-git \
--debug \
--build-tests \
--parallel=8 \
--verbose-build \
--module-subset=Core,Gui,Widgets
Which will build and install the project with debug symbols, including the tests,
using ninja (instead of make), and considering only the module subset of
QtCore
, QtGui
, and
QtWidgets
.
CMake Unity Build Mode is used by default for speed-up.
- Other important options to consider are:
--cmake
, to specify the path to the cmake binary,--reuse-build
, to rebuild only the modified files,--openssl=/path/to/openssl/bin
, to use a different path for OpenSSL,--standalone
, to copy over the Qt libraries into the final package to make it work on other machines,--build-docs
, to enable the documentation build.--doc-build-online
, to build documentation using the online template (by default it uses the offline)
Testing the installation¶
Once the installation finishes, you will be able to execute any of our examples:
python examples/widgets/widgets/tetrix/tetrix.py
Running Tests¶
Using the --build-tests
option will enable us to run all the auto tests inside the project:
python testrunner.py test > testlog.txt
Note
On Windows, don’t forget to have qtpaths in your path (set PATH=C:\Path\to\Qt\6.4.0\msvc2019_64\bin;%PATH%)
You can also run a specific test (for example qpainter_test
) by running:
ctest -R qpainter_test --verbose
Cross Compilation¶
Starting from 6.3, it is possible to cross-compile Shiboken (module), and PySide. This functionality is still in Technical Preview, which means it could change in the future releases.
Important
The only supported configuration is using a host Linux machine to cross-compile to a Linux target platform.
Cross compiling software is a valid use case that many projects rely on, however, it is a complicated process that might fail due to many reasons.
Before starting with the process, it is important to understand the details of the build system, and the goal of cross compilation.
In the build process, a Host
is the computer you are currently using to
compile, and a Target
is your embedded device that you are compiling for.
Qt for Python is being built using setuptools, and relies on a setup.py
file
that is called recursively to build Shiboken (module),
Shiboken (generator), and PySide. As the generator is creating
the wrappers for the bindings, it’s not cross compiled
for the target.
Only the Shiboken (module) and PySide are cross compiled.
The building process requires a Qt installation, and a Python interpreter on both the host, and the target. The used Qt versions on both platforms should have the same minor version. That is, Qt 6.3 (host) cannot be used with a Qt 6.2 (target), or the other way around.
Note
It is recommended to use the same version of Qt on both host and target to avoid possible unconventional errors.
Prerequisites¶
First and foremost, you need to have access to the target device because you need to copy several system files (sysroot). We recommend a Linux OS that has the latest Qt versions, like Manjaro ARM or Archlinux ARM.
(target) Install Qt 6.3+ on the system using the package manager.
(host) Install Qt 6.3+ on the system using the package manager or Qt Installer.
(target, host) Install the library and development packages that provide C++ headers, linkers, libraries, and compilers.
(target, host) Install Python interpreter v3.7 or later
(target, host) Install CMake 3.17+
After installing these prerequisites, copy the target
sysroot to your
host
computer. This process is tricky, because copying system files from
another computer might cause problems with the symbolic links. Here you
have two options to achieve that.
Option A: Copying the files¶
Create a directory to copy the sysroot of your target device,
for example rpi-sysroot
, and perform the copy on your host computer:
rsync -vR --progress -rl --delete-after --safe-links \
USERNAME@TARGET_IP:/{lib,usr,opt/vc/lib} rpi-sysroot/
Ensure to replace USERNAME
and TARGET_IP
with your system appropriate
values.
Option B: Packaging the file system¶
Create a package for your sysroot in your target:
tar cfJ ~/sysroot.tar.xz /lib /usr /opt/vc/lib
Copy the package from the target to your host:
rsync -vR --progress USERNAME@TARGET_IP:sysroot.tar.xz .
Once you have the tar file, unpack it inside a rpi-sysroot
directory.
Fix Symlinks¶
It is recommended to run the following script to fix most of the issues you would find with symbolic links:
import sys
from pathlib import Path
import os
# Take a sysroot directory and turn all the absolute symlinks and turn them into
# relative ones such that the sysroot is usable within another system.
if len(sys.argv) != 2:
print(f"Usage is {sys.argv[0]} <sysroot-directory>")
sys.exit(-1)
topdir = Path(sys.argv[1]).absolute()
def handlelink(filep, subdir):
link = filep.readlink()
if str(link)[0] != "/":
return
if link.startswith(topdir):
return
relpath = os.path.relpath((topdir / link).resolve(), subdir)
os.unlink(filep)
os.symlink(relpath, filep)
for f in topdir.glob("**/*"):
if f.is_file() and f.is_symlink():
handlelink(f, f.parent)
Setting up the toolchain¶
To perform the cross compilation, you need a special set of compilers, libraries, and headers, which runs on the host architecture, but generates (binaries/executables) for a target architecture. For example, from x86_64 to aarch64.
It is recommended to use the official 10.2 ARM Developer cross compilers,
which you can find on their official website. For this tutorial, we choose
aarch64
target architecture and we will assume that you downloaded the
gcc-arm-10.2-2020.11-x86_64-aarch64-none-linux-gnu.tar.xz file,
and unpacked it.
With those compilers, now you need a CMake toolchain file. This is a configuration file to set the compilers and sysroot information, together with extra options like compilation flags, and other details. You can use the following file as an example, but keep in mind they might vary:
# toolchain-aarch64.cmake
cmake_minimum_required(VERSION 3.18)
include_guard(GLOBAL)
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR aarch64)
set(TARGET_SYSROOT /path/to/your/target/sysroot)
set(CROSS_COMPILER /path/to/your/crosscompiling/compilers/)
set(CMAKE_SYSROOT ${TARGET_SYSROOT})
set(ENV{PKG_CONFIG_PATH} "")
set(ENV{PKG_CONFIG_LIBDIR} ${CMAKE_SYSROOT}/usr/lib/pkgconfig:${CMAKE_SYSROOT}/usr/share/pkgconfig)
set(ENV{PKG_CONFIG_SYSROOT_DIR} ${CMAKE_SYSROOT})
set(CMAKE_C_COMPILER ${CROSS_COMPILER}/aarch64-none-linux-gnu-gcc)
set(CMAKE_CXX_COMPILER ${CROSS_COMPILER}/aarch64-none-linux-gnu-g++)
set(QT_COMPILER_FLAGS "-march=armv8-a")
set(QT_COMPILER_FLAGS_RELEASE "-O2 -pipe")
set(QT_LINKER_FLAGS "-Wl,-O1 -Wl,--hash-style=gnu -Wl,--as-needed")
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
include(CMakeInitializeConfigs)
function(cmake_initialize_per_config_variable _PREFIX _DOCSTRING)
if (_PREFIX MATCHES "CMAKE_(C|CXX|ASM)_FLAGS")
set(CMAKE_${CMAKE_MATCH_1}_FLAGS_INIT "${QT_COMPILER_FLAGS}")
foreach (config DEBUG RELEASE MINSIZEREL RELWITHDEBINFO)
if (DEFINED QT_COMPILER_FLAGS_${config})
set(CMAKE_${CMAKE_MATCH_1}_FLAGS_${config}_INIT "${QT_COMPILER_FLAGS_${config}}")
endif()
endforeach()
endif()
if (_PREFIX MATCHES "CMAKE_(SHARED|MODULE|EXE)_LINKER_FLAGS")
foreach (config SHARED MODULE EXE)
set(CMAKE_${config}_LINKER_FLAGS_INIT "${QT_LINKER_FLAGS}")
endforeach()
endif()
_cmake_initialize_per_config_variable(${ARGV})
endfunction()
You need to adjust the paths in these two lines:
set(TARGET_SYSROOT /path/to/your/target/sysroot)
set(CROSS_COMPILER /path/to/your/crosscompiling/compilers/)
and replace them with the sysroot directory (the one we called rpi-sysroot
),
and the compilers (the gcc-arm-10.2-2020.11-x86_64-aarch64-none-linux-gnu/bin
directory).
Cross compiling PySide¶
After you have installed the prerequisites and copied the necessary files, you should have the following:
The compilers to cross compile (
gcc-argm-10.2-...
),The target sysroot (
rpi-sysroot
),The toolchain cmake file (
toolchain-aarch64.cmake
),The
pyside-setup
repository,
An example of the setup.py
invocation might look like the following:
python setup.py bdist_wheel \
--parallel=8 --ignore-git --reuse-build --standalone --limited-api=yes \
--cmake-toolchain-file=/opt/toolchain-aarch64.cmake \
--qt-host-path=/opt/Qt/6.3.0/gcc_64 \
--plat-name=linux_aarch64
Depending on the target platform, you could use linux_armv7
,
linux_aarch64
, etc.
For embedded platforms, which typically do not have Qt and its tools fully
built, the option --no-qt-tools
can be used to prevent the bundling of the
tools.
If the process succeeds, you will find the target wheels in your dist/
directory, for example:
PySide6-6.3.0-6.3.0-cp36-abi3-manylinux2014_aarch64.whl
shiboken6-6.3.0-6.3.0-cp36-abi3-manylinux2014_aarch64.whl
Troubleshooting¶
If the auto-detection mechanism fails to find the Python or Qt installations you have in your target device, you can use two additional options:
--python-target-path=...
and:
--qt-target-path=...
In case the automatic build of the host Shiboken (generator) fails, you can specify the custom path using:
--shiboken-host-path=...
Building the documentation¶
Starting from 6.3, the documentation is not being built by default.
When using the second option described in this section, make sure to use the
--build-docs
option.
Install the specific documentation requirements in your Python virtual environment:
pip install -r requirements-doc.txt
You can find the requirements-doc.txt
file in the root of the repository.
Starting from 5.15, there are two options to build the documentation:
1. Building the base documentation (no API)¶
The process of parsing Qt headers to generate the PySide API documentation can take several minutes, this means that modifying a specific section of the documentation we currently have, might become a hard task. You may only care about the base documentation, which comprises all the documentation except for the API documentation.
To generate this, execute the following command:
python setup.py build_base_docs
This will generate an html/
directory with the following structure:
html
└── pyside6
├── index.html
├── ...
└── shiboken6
├── index.html
└── ...
so you can open the main page html/pyside6/index.html
on your browser to check the generated
files.
This is useful when updating the general sections of the documentation, adding tutorials, modifying the build instructions, and more.
Note
In case you are interested in only generating the Example Gallery,
you would need to run python tools/example_gallery/main.py
to
generate the examples documentation
for the gallery. This will
also be used internally by the build_base_docs
target
2. Building the documentation (Base + API)¶
The documentation is being generated using qdoc to get the API information, and also sphinx for the local Python related notes.
The system requires the libxml2
and libxslt
libraries.
After installing graphviz
(included in the requirements-doc.txt
file),
the dot
command needs to be in PATH, otherwise,
the process will fail. Installing graphviz
system-wide is also an option.
Since the process relies on a Qt installation, you need to specify where the
qtbase
directory of a Qt source tree is located by passing it to
the command line option --qt-src-dir
.
Once the common setup.py
build process finishes (remember to use
--build-docs
to enable the documentation build, and --doc-build-online
to get the HTML files), you can go to the generated
build/<your_env_name>/build/pyside6
directory, and run:
ninja apidoc
You can add -j X
, to perform the build process in parallel with
X processes.
Note
The apidoc make target builds offline documentation in QCH
(Qt Compressed Help) format by default. You can switch to building for the
online use with the --doc-build-online
configure option.
The target executes several steps:
The
qdoc
tool is run over the Qt source code to produce documentation in WebXML format.shiboken6
is run to extract the functions for which bindings exist from WebXML and convert it into RST.sphinx
is run to produce the documentation in HTML format.
Re-running the command will not execute step 1 unless the file
qdoc-output/webxml/qtcore-index.webxml
is removed from the build tree.
Similarly, step 2 will not be executed unless the file base/PySide6/QtCore/index.rst
is removed.
Finally, you will get a html
directory containing all the generated documentation. The offline
help files, PySide.qch
and Shiboken.qch
, can be moved to any directory of your choice. You
can find Shiboken.qch
in the build directory, build/<your_env_name>/build/shiboken6/doc/html
.
If you want to temporarily change a .rst
file to examine the impact on
formatting, you can re-run sphinx
in the doc
directory:
sphinx-build base html
Viewing offline documentation¶
The offline documentation (QCH) can be viewed using the Qt Creator IDE or Qt Assistant, which is a standalone application for viewing QCH files.
To view the QCH using Qt Creator, following the instructions outlined in Using Qt Creator Help Mode. If you chose to use Qt Assistant instead, use the following command to register the QCH file before launching Qt Assistant:
assistant -register PySide.qch
Troubleshooting documentation¶
The documentation uses intersphinx to link from the PySide to the Shiboken documentation. This can fail if
the default
QCH
format is used; in which case the requiredobjects.inv
files are not generated. Use--doc-build-online
.base and full doc builds are mixed, resulting in wrong values for the intersphinx location in the CMake files. Re-run
cmake
to fix this.
Using the internal tools¶
A set of tools can be found under the tools/
directory inside the pyside-setup
repository.
checklibs.py
: Script to analyze dynamic library dependencies of Mach-O binaries. To use this utility, just run:python checklibs.py /path/to/some.app/Contents/MacOS/Some
This script was fetched from this repository.
create_changelog.py
: Script used to create the CHANGELOG that you can find in thedist/
directory. Usage:python create_changelog.py -r 6.0.1 -v v6.0.0..6.0 -t bug-fix
debug_windows.py
: This script can be used to find out why PySide modules fail to load with various DLL errors like Missing DLL or Missing symbol in DLL.You can think of it as a Windows version of ldd /
LD_DEBUG
.Underneath, it uses the cdb.exe command line debugger and the gflags.exe tool, which are installed with the latest Windows Kit.
The aim is to help developers debug issues that they may encounter using the PySide imports on Windows. The user should then provide the generated log file.
Incidentally it can also be used for any Windows executables, not just Python. To use it just run:
python debug_windows.py
missing_bindings.py
: This script is used to compare the state of PySide and PyQt regarding available modules and classses. This content is displayed in our wiki page, and can be used as follows:python missing_bindings.py --qt-version 6.0.1 -w all
Note
The script relies on BeautifulSoup to parse the content and generate a list of the missing bindings.