Both sides previous revision Previous revision | |
technical:recipes:pyqt5-in-virtualenv [2019-05-13 16:02] – frey | technical:recipes:pyqt5-in-virtualenv [2020-01-06 10:52] (current) – [Setup a virtual environment] anita |
---|
| ====== 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. |