technical:recipes:pyqt5-in-virtualenv

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Next revision
Previous revision
technical:recipes:pyqt5-in-virtualenv [2019-05-13 16:01] – created freytechnical:recipes:pyqt5-in-virtualenv [2020-01-06 10:52] (current) – [Setup a virtual environment] anita
Line 1: Line 1:
 +====== 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:
 +<code>
 +$ 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)
 +</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.
 +</WRAP>
 +
 +<code>
 +(/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: 
 +</code>
 +
 +===== 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.
 +
 +<code>
 +(/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
 +    :
 +</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.