Table of Contents

Building PyQt5 in a Python Virtual Environment

By its construction, the PyQt5 library contains very little native Python code and consists primarily of compiled shared libraries that expose C++ Qt5 APIs via Python interfaces. Any pre-built binary copy of PyQt5 (e.g. a wheel) thus has quite a few dependencies that PyPI cannot track: what Qt5, libc, libxml, et al. libraries were used to build that wheel. Users of our clusters (built on CentOS/RedHat that tend to use older releases at the OS level) often find that using pip to add binary packages to a Python virtualenv results in compatibility issues.

One such instance came up today when a user complained that a virtualenv was crashing with the following error message:

This application failed to start because it could not find or load the Qt platform plugin "xcb"
in "".

Available platform plugins are: minimal, offscreen, xcb.

Reinstalling the application may fix this problem.
Aborted (core dumped)

Searching for this sort of error online yielded plenty of the typical responses:

  1. Recreate your virtualenv
  2. Reinstall everything in your virtualenv
  3. Use ldd to check that the shared libraries installed by pip don't have missing dependencies

None of this helped. In the end, asking the underlying Qt5 library to verbosely log its behavior was the key:

$ QT_DEBUG_PLUGINS=1 python qt-test.py
   :
Got keys from plugin meta data ("xcb")
QFactoryLoader::QFactoryLoader() checking directory path "/home/1001/qt-test/bin/platforms" ...
Cannot load library /home/1001/qt-test/lib/python3.6/site-packages/PyQt5/Qt/plugins/platforms/libqxcb.so: (/home/1001/qt-test/lib/python3.6/site-packages/PyQt5/Qt/lib/libQt5Core.so.5: version `Qt_5.9' not found (required by /opt/shared/anaconda/5.2.0-python3/lib/libQt5XcbQpa.so.5))
QLibraryPrivate::loadPlugin failed on "/home/1001/qt-test/lib/python3.6/site-packages/PyQt5/Qt/plugins/platforms/libqxcb.so" : "Cannot load library /home/1001/qt-test/lib/python3.6/site-packages/PyQt5/Qt/plugins/platforms/libqxcb.so: (/home/1001/qt-test/lib/python3.6/site-packages/PyQt5/Qt/lib/libQt5Core.so.5: version `Qt_5.9' not found (required by /opt/shared/anaconda/5.2.0-python3/lib/libQt5XcbQpa.so.5))"
This application failed to start because it could not find or load the Qt platform plugin "xcb"
in "".

Available platform plugins are: eglfs, linuxfb, minimal, minimalegl, offscreen, xcb.

Reinstalling the application may fix this problem.
Aborted (core dumped)

While PyQt5 happily loaded the native Qt5 libraries, when it attempted to initialize the runtime environment to a minimum support level of version 5.9 the library provided by CentOS (5.6) couldn't satisfy that requirement. In short, the PyPI wheel for PyQt5 was built against too new a Qt5 library. The only solution to this problem is to build and install your own copy of PyQt5 specifically tailored to the system on which you are running.

Setup a virtual environment

All code examples were tested on the Caviness cluster.

$ vpkg_require anaconda/5.2.0:python3
$ conda create -p ~/qt-test python=3 pip
   :
$ source activate ~/qt-test
(/home/1001/qt-test) $ cd ~/qt-test

If you already used pip to install PyQt5 into a virtualenv, remove it and the PyQt5-sip package prior to performing the rest of the actions in this recipe. The pip uninstall PyQt5 PyQt5-sip sip commmand will ask you to confirm removal of the components.

Dependencies

SIP

Building PyQt5 requires a tool called SIP that translates C/C++ APIs into low-level Python objects that can be compiled and linked into shared libraries. The SIP tool is maintained by the authors of PyQt5. For PyQt5 5.12.2, a minimum of SIP 4.9.14 is required. As of the time of this writing, PyPI provides SIP 4.9.8. So SIP must be built manually first.

PyQt5 expects SIP to be present as a module within itself (PyQt5.sip) so it is important to specify the module name using the –sip-module parameter to the configure.py script. Building and installing as a standalone module (sip) will not work.

(/home/1001/qt-test) $ mkdir attic src
(/home/1001/qt-test) $ cd attic
(/home/1001/qt-test) $ wget 'https://www.riverbankcomputing.com/static/Downloads/sip/4.19.17/sip-4.19.17.tar.gz'
(/home/1001/qt-test) $ cd ../src
(/home/1001/qt-test) $ tar -xf ../attic/sip-4.19.17.tar.gz
(/home/1001/qt-test) $ cd sip-4.19.17
(/home/1001/qt-test) $ python configure.py --sip-module=PyQt5.sip
    :
(/home/1001/qt-test) $ make
    :
(/home/1001/qt-test) $ make install
    :
(/home/1001/qt-test) $ pip show PyQt5-sip
Name: PyQt5-sip
Version: 4.19.17
Summary: None
Home-page: None
Author: None
Author-email: None
License: None
Location: /home/1001/qt-test/lib/python3.6/site-packages
Requires: 
Required-by: 

Build and install PyQt5

Once SIP is built and installed into the virtualenv, PyQt5 can be handled. By default the configuration script will enable all PyQt5 sub-modules for which necessary pre-requisite Qt5 libraries and headers are available. The –disable flag can be used to prevent any unnecessary sub-modules from being built.

(/home/1001/qt-test) $ cd ~/qt-test/attic
(/home/1001/qt-test) $ wget 'https://www.riverbankcomputing.com/static/Downloads/PyQt5/5.12.2/PyQt5_gpl-5.12.2.tar.gz'
(/home/1001/qt-test) $ cd ../src
(/home/1001/qt-test) $ tar -xf ../attic/PyQt5_gpl-5.12.2.tar.gz
(/home/1001/qt-test) $ cd PyQt5_gpl-5.12.2
(/home/1001/qt-test) $ python configure.py 
    :
(/home/1001/qt-test) $ make -j 10
    :
(/home/1001/qt-test) $ make install
    :

Test PyQt5

At this point you can test PyQt5 in your virtualenv:

(/home/1001/qt-test) $ cd ~/qt-test
(/home/1001/qt-test) $ cat >qt-test.py <<EOT
from PyQt5.QtWidgets import QApplication, QLabel
theApp = QApplication([])
theLabel = QLabel('Hello, world!')
theLabel.show()
theApp.exec_()

EOT
(/home/1001/qt-test) $ python qt-test.py

If you have an X11 display present (the DISPLAY environment variable defined accordingly) you should see a small window with the text Hello, world! displayed.