diff --git a/CAVEATS.md b/CAVEATS.md index 4ebdc2a8..3c4ec0e6 100644 --- a/CAVEATS.md +++ b/CAVEATS.md @@ -216,3 +216,49 @@ Traceback (most recent call last): ... TypeError: ... ``` + + +#### QtWidgets.QHeaderView.setResizeMode + +`setResizeMode` was [renamed](http://doc.qt.io/qt-5/qheaderview.html#setSectionResizeMode) `setSectionResizeMode` in Qt 5. + +```python +# PySide2 +>>> from Qt import QtWidgets +>>> app = QtWidgets.QApplication(sys.argv) +>>> view = QtWidgets.QTreeWidget() +>>> header = view.header() +>>> header.setResizeMode(QtWidgets.QHeaderView.Fixed) +Traceback (most recent call last): +... +AttributeError: 'PySide2.QtWidgets.QHeaderView' object has no attribute 'setResizeMode' +``` + +```python +# PySide +>>> from Qt import QtWidgets +>>> app = QtWidgets.QApplication(sys.argv) +>>> view = QtWidgets.QTreeWidget() +>>> header = view.header() +>>> header.setSectionResizeMode(QtWidgets.QHeaderView.Fixed) +Traceback (most recent call last): +... +AttributeError: 'PySide.QtGui.QHeaderView' object has no attribute 'setSectionResizeMode' +``` + +##### Workaround + +Use a conditional. + +```python +# PySide2 +>>> from Qt import QtWidgets, __binding__ +>>> app = QtWidgets.QApplication(sys.argv) +>>> view = QtWidgets.QTreeWidget() +>>> header = view.header() +>>> if __binding__ in ("PyQt4", "PySide"): +... header.setResizeMode(QtWidgets.QHeaderView.Fixed) +... else: +... header.setSectionResizeMode(QtWidgets.QHeaderView.Fixed) +``` + diff --git a/Dockerfile-py2.7 b/Dockerfile-py2.7 index 28f242a6..a8e59fda 100644 --- a/Dockerfile-py2.7 +++ b/Dockerfile-py2.7 @@ -11,7 +11,8 @@ RUN apt-get update && \ python-qt4 \ python-pyqt5 \ python-pyside \ - python-pyside2 + python-pyside2 \ + xvfb # Nose is the Python test-runner RUN pip install nose nosepipe @@ -19,9 +20,14 @@ RUN pip install nose nosepipe # Enable additional output from Qt.py ENV QT_VERBOSE true +# Xvfb +ENV DISPLAY :99 + WORKDIR /workspace/Qt.py ENTRYPOINT cp -r /Qt.py /workspace && \ python build_caveats_tests.py && \ + Xvfb :99 -screen 0 1024x768x16 2>/dev/null & \ + sleep 3 && \ nosetests \ --verbose \ --with-process-isolation \ diff --git a/Dockerfile-py3.5 b/Dockerfile-py3.5 index e064e349..89bedfa4 100644 --- a/Dockerfile-py3.5 +++ b/Dockerfile-py3.5 @@ -11,7 +11,8 @@ RUN apt-get update && \ python3-pyqt4 \ python3-pyqt5 \ python3-pyside \ - python3-pyside2 + python3-pyside2 \ + xvfb # Nose is the Python test-runner RUN pip3 install nose nosepipe @@ -19,9 +20,14 @@ RUN pip3 install nose nosepipe # Enable additional output from Qt.py ENV QT_VERBOSE true +# Xvfb +ENV DISPLAY :99 + WORKDIR /workspace/Qt.py ENTRYPOINT cp -r /Qt.py /workspace && \ python3 build_caveats_tests.py && \ + Xvfb :99 -screen 0 1024x768x16 2>/dev/null & \ + sleep 3 && \ nosetests \ --verbose \ --with-process-isolation \ diff --git a/README.md b/README.md index d6312612..d541defd 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,30 @@ Qt.py enables you to write software that dynamically chooses the most desireable bindings based on what's available, including PySide2, PyQt5, PySide and PyQt4; in that (configurable) order (see below). +
+ +**Table of contents** + +- [Install](#install) +- [Usage](#usage) +- [Documentation](#documentation) +- [Rules](#rules) +- [How it works](#how-it-works) +- [Known problems](#known-problems) +- [Who's using Qt.py?](#whos-using-qtpy) +- [Projects using Qt.py](#projects-using-qtpy) +- [Projects similar to Qt.py](#projects-similar-to-qtpy) +- [Developer guide](#developer-guide) + +
+
+
+ +### Development goals + +- Simplicity. Simple to read, simple to grok, simple to maintain. +- No bugs. What you get is what each binding provides equally and documentation of inequalities. +


@@ -45,29 +69,86 @@ app.exec_()

-### How it works +### Documentation -Once you import Qt.py, Qt.py replaces itself with the most desirable binding on your platform, or throws an `ImportError` if none are available. +All members of `Qt` stem directly from those available via PySide2, along with these additional members. + +| Attribute | Type | Value +|:------------------------|:-------|:------------ +| `__binding__` | `str` | A string reference to binding currently in use +| `__qt_version__` | `str` | Reference to version of Qt, such as Qt 5.6.1 +| `__binding_version__` | `str` | Reference to version of binding, such as PySide 1.2.6 +| `__wrapper_version__` | `str` | Version of this project +| `load_ui()` | `func` | Minimal wrapper of PyQt4.loadUi and PySide equivalent + +
+ +##### Branch binding-specific code + +Some bindings offer features not available in others, you can use `__binding__` to capture those. ```python ->>> import Qt ->>> print(Qt) - +if "PySide" in Qt.__binding__: + do_pyside_stuff() ``` -Here's an example of how this works. +
-**Qt.py** +##### Override preferred choice + +If your system has multiple choices where one or more is preferred, you can override the preference and order in which they are tried with this environment variable. + +```bash +# Windows +$ set QT_PREFERRED_BINDING=PyQt5 +$ python -c "import Qt;print(Qt.__binding__)" +PyQt5 + +# Unix/OSX +$ export QT_PREFERRED_BINDING=PyQt5 +$ python -c "import Qt;print(Qt.__binding__)" +PyQt5 +``` + +Constrain available choices and order of discovery by supplying multiple values. + +```bash +# Try PyQt first and then PySide, but nothing else. +$ export QT_PREFERRED_BINDING=PyQt:PySide +``` + +Using the OS path separator (`os.pathsep`) which is `:` on Unix systems and `;` on Windows. + +
+ +##### Load Qt Designer .ui files + +The `uic.loadUi` function of PyQt4 and PyQt5 as well as the `QtUiTools.QUiLoader().load` function of PySide/PySide2 are mapped to a convenience function `load_ui`. ```python import sys -import PyQt5 +import Qt -# Replace myself PyQt5 -sys.modules["Qt"] = PyQt5 +app = QtWidgets.QApplication(sys.argv) +ui = Qt.load_ui("my.ui") +ui.show() +app.exec_() ``` -Once imported, it is as though your application was importing whichever binding was chosen and Qt.py never existed. +Please note, for maximum compatibility, only pass the argument of the filename to the `load_ui` function. + +
+ +##### sip API v2 + +If you're using PyQt4, `sip` attempts to set its API to version 2 for the following: +- `QString` +- `QVariant` +- `QDate` +- `QDateTime` +- `QTextStream` +- `QTime` +- `QUrl`

@@ -119,86 +200,29 @@ There are cases where Qt.py is not handling incompatibility issues. Please see [

-### Documentation - -All members of `Qt` stem directly from those available via PySide2, along with these additional members. - -```python -import Qt - -# A string reference to binding currently in use -Qt.__binding__ == 'PyQt5' - -# Reference to version of Qt, such as Qt 5.6.1 -Qt.__qt_version__ == '5.6.1' - -# Reference to version of binding, such as PySide 1.2.6 -Qt.__binding_version__ == '1.2.6' - -# Version of this project -Qt.__wrapper_version__ == '1.0.0' -``` - -##### Branch binding-specific code +### How it works -Some bindings offer features not available in others, you can use `__binding__` to capture those. +Once you import Qt.py, Qt.py replaces itself with the most desirable binding on your platform, or throws an `ImportError` if none are available. ```python -if "PySide" in Qt.__binding__: - do_pyside_stuff() -``` - -##### Override preferred choice - -If your system has multiple choices where one or more is preferred, you can override the preference and order in which they are tried with this environment variable. - -```bash -# Windows -$ set QT_PREFERRED_BINDING=PyQt5 -$ python -c "import Qt;print(Qt.__binding__)" -PyQt5 - -# Unix/OSX -$ export QT_PREFERRED_BINDING=PyQt5 -$ python -c "import Qt;print(Qt.__binding__)" -PyQt5 -``` - -Constrain available choices and order of discovery by supplying multiple values. - -```bash -# Try PyQt first and then PySide, but nothing else. -$ export QT_PREFERRED_BINDING=PyQt:PySide +>>> import Qt +>>> print(Qt) + ``` -Using the OS path separator (`os.pathsep`) which is `:` on Unix systems and `;` on Windows. - -##### Load Qt Designer .ui files +Here's an example of how this works. -The `uic.loadUi` function of PyQt4 and PyQt5 as well as the `QtUiTools.QUiLoader().load` function of PySide/PySide2 are mapped to a convenience function `load_ui`. +**Qt.py** ```python import sys -import Qt +import PyQt5 -app = QtWidgets.QApplication(sys.argv) -ui = Qt.load_ui("my.ui") -ui.show() -app.exec_() +# Replace myself PyQt5 +sys.modules["Qt"] = PyQt5 ``` -Please note, for maximum compatibility, only pass the argument of the filename to the `load_ui` function. - -##### sip API v2 - -If you're using PyQt4, `sip` attempts to set its API to version 2 for the following: -- `QString` -- `QVariant` -- `QDate` -- `QDateTime` -- `QTextStream` -- `QTime` -- `QUrl` +Once imported, it is as though your application was importing whichever binding was chosen and Qt.py never existed.

diff --git a/tests.py b/tests.py index 887f1346..5aa45a1d 100644 --- a/tests.py +++ b/tests.py @@ -92,7 +92,7 @@ def test_preferred_pyside(): def test_preferred_pyside2(): - """Setting QT_PREFERRED_BINDING to PyQt5 properly forces the binding""" + """Setting QT_PREFERRED_BINDING to PySide2 properly forces the binding""" with pyside2(): import Qt assert Qt.__name__ == "PySide2", ("PySide2 should have been picked, "