====== 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:
+
<code>
+
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)
+
</code>
+
Searching for this sort of error online yielded plenty of the typical responses:
+
- Recreate your virtualenv
+
- Reinstall everything in your virtualenv
+
- 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:
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)
+
</code>
+
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.
+
+
<code>
+
$ 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
+
</code>
+
+
<WRAP center round tip 60%>
+
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.
+
</WRAP>
+
+
===== 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.
+
+
<WRAP center round important 60%>
+
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.
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) $ 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
+
:
+
</code>
+
+
===== Test PyQt5 =====
+
+
At this point you can test PyQt5 in your virtualenv:
+
+
<code>
+
(/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
+
</code>
+
+
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.