Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .github/workflows/build_deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ jobs:
test:
uses: ./.github/workflows/test_pyqt5.yml

test_pyqt6:
uses: ./.github/workflows/test_pyqt6.yml

build_wheels:
name: Build wheels on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
Expand Down Expand Up @@ -57,7 +60,7 @@ jobs:
path: dist/*.tar.gz

upload_pypi:
needs: [test, build_wheels, build_sdist]
needs: [test, test_pyqt6, build_wheels, build_sdist]
runs-on: ubuntu-latest
environment: pypi
permissions:
Expand Down
9 changes: 6 additions & 3 deletions .github/workflows/test_pyqt5.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ["3.10", "3.13", "3.14"]
python-version: ["3.10", "3.14"]

steps:
- uses: actions/checkout@v7
Expand All @@ -31,14 +31,17 @@ jobs:
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
env:
# Pull requests are routed by their target branch, pushes by their own branch
TARGET_BRANCH: ${{ github.base_ref || github.ref_name }}
run: |
sudo apt install libxkbcommon-x11-0 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-xinerama0 libxcb-xfixes0 x11-utils
/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -screen 0 1920x1200x24 -ac +extension GLX
python -m pip install --upgrade pip
python -m pip install ruff pytest
pip install PyQt5 setuptools numpy Cython
python setup.py build_ext --inplace
if [ "${{ github.ref_name }}" = "develop" ]; then
if [ "$TARGET_BRANCH" = "develop" ]; then
pip uninstall -y guidata PythonQwt
cd ..
git clone --depth 1 --branch develop https://github.com/PlotPyStack/guidata.git
Expand All @@ -53,7 +56,7 @@ jobs:
pip install -r deps.txt
# Install PlotPy without dependencies
pip install --no-deps .
elif [ "${{ github.ref_name }}" = "release" ]; then
elif [ "$TARGET_BRANCH" = "release" ]; then
pip uninstall -y guidata PythonQwt
cd ..
# Try cloning guidata from release, fallback to main or master
Expand Down
85 changes: 85 additions & 0 deletions .github/workflows/test_pyqt6.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# This workflow will install Python dependencies, run tests and lint with a variety of Python versions
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python

# Inspired from https://pytest-qt.readthedocs.io/en/latest/troubleshooting.html#github-actions

name: Build, install and test with PyQt6

on:
push:
branches: [ "master", "develop", "release" ]
pull_request:
branches: [ "master", "develop", "release" ]
workflow_call:

jobs:
build:

env:
DISPLAY: ':99.0'

runs-on: ubuntu-latest
timeout-minutes: 20
strategy:
fail-fast: false
matrix:
python-version: ["3.10", "3.14"]

steps:
- uses: actions/checkout@v7
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v7
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
env:
# Pull requests are routed by their target branch, pushes by their own branch
TARGET_BRANCH: ${{ github.base_ref || github.ref_name }}
run: |
sudo apt install libxkbcommon-x11-0 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-xinerama0 libxcb-xfixes0 x11-utils libegl1 libgl1 libopengl0 libxcb-cursor0
/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -screen 0 1920x1200x24 -ac +extension GLX
python -m pip install --upgrade pip
python -m pip install ruff pytest
pip install PyQt6 setuptools numpy Cython
python setup.py build_ext --inplace
if [ "$TARGET_BRANCH" = "develop" ]; then
pip uninstall -y guidata PythonQwt
cd ..
git clone --depth 1 --branch develop https://github.com/PlotPyStack/guidata.git
git clone --depth 1 https://github.com/PlotPyStack/PythonQwt.git
cd PlotPy
pip install -e ../guidata
pip install -e ../PythonQwt
# Install tomli for TOML parsing (safe if already present)
pip install tomli
# Extract dependencies and save to file, then install
python -c "import tomli; f=open('pyproject.toml','rb'); data=tomli.load(f); deps=[d for d in data['project']['dependencies'] if not any(p in d for p in ['guidata', 'PythonQwt'])]; open('deps.txt','w').write('\n'.join(deps))"
pip install -r deps.txt
# Install PlotPy without dependencies
pip install --no-deps .
elif [ "$TARGET_BRANCH" = "release" ]; then
pip uninstall -y guidata PythonQwt
cd ..
# Try cloning guidata from release, fallback to main or master
git clone --depth 1 --branch release https://github.com/PlotPyStack/guidata.git || git clone --depth 1 https://github.com/PlotPyStack/guidata.git || git clone --depth 1 --branch master https://github.com/PlotPyStack/guidata.git
# Try cloning PythonQwt from main or master
git clone --depth 1 https://github.com/PlotPyStack/PythonQwt.git || git clone --depth 1 --branch master https://github.com/PlotPyStack/PythonQwt.git
cd PlotPy
pip install -e ../guidata
pip install -e ../PythonQwt
# Install tomli for TOML parsing (safe if already present)
pip install tomli
# Extract dependencies and save to file, then install
python -c "import tomli; f=open('pyproject.toml','rb'); data=tomli.load(f); deps=[d for d in data['project']['dependencies'] if not any(p in d for p in ['guidata', 'PythonQwt'])]; open('deps.txt','w').write('\n'.join(deps))"
pip install -r deps.txt
# Install PlotPy without dependencies
pip install --no-deps .
else
# Install from PyPI normally for master branch
pip install .[test]
fi
- name: Lint with Ruff
run: ruff check --output-format=github plotpy
- name: Test with pytest
run: |
pytest
86 changes: 86 additions & 0 deletions .github/workflows/test_pyside6.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# This workflow will install Python dependencies, run tests and lint with a variety of Python versions
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python

# Inspired from https://pytest-qt.readthedocs.io/en/latest/troubleshooting.html#github-actions

name: Build, install and test with PySide6

on:
push:
branches: [ "master", "develop", "release" ]
pull_request:
branches: [ "master", "develop", "release" ]
workflow_call:

jobs:
build:

env:
DISPLAY: ':99.0'

runs-on: ubuntu-latest
timeout-minutes: 20
strategy:
fail-fast: false
matrix:
python-version: ["3.10", "3.14"]

steps:
- uses: actions/checkout@v7
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v7
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
env:
# Pull requests are routed by their target branch, pushes by their own branch
TARGET_BRANCH: ${{ github.base_ref || github.ref_name }}
run: |
sudo apt install libxkbcommon-x11-0 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-xinerama0 libxcb-xfixes0 x11-utils libegl1 libgl1 libopengl0 libxcb-cursor0
/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -screen 0 1920x1200x24 -ac +extension GLX
python -m pip install --upgrade pip
python -m pip install ruff pytest
# PySide6 6.12.0 drops a reference to None on each void call: fatal before Python 3.12
pip install "PySide6!=6.12.0; python_version < '3.12'" "PySide6; python_version >= '3.12'" setuptools numpy Cython
python setup.py build_ext --inplace
if [ "$TARGET_BRANCH" = "develop" ]; then
pip uninstall -y guidata PythonQwt
cd ..
git clone --depth 1 --branch develop https://github.com/PlotPyStack/guidata.git
git clone --depth 1 https://github.com/PlotPyStack/PythonQwt.git
cd PlotPy
pip install -e ../guidata
pip install -e ../PythonQwt
# Install tomli for TOML parsing (safe if already present)
pip install tomli
# Extract dependencies and save to file, then install
python -c "import tomli; f=open('pyproject.toml','rb'); data=tomli.load(f); deps=[d for d in data['project']['dependencies'] if not any(p in d for p in ['guidata', 'PythonQwt'])]; open('deps.txt','w').write('\n'.join(deps))"
pip install -r deps.txt
# Install PlotPy without dependencies
pip install --no-deps .
elif [ "$TARGET_BRANCH" = "release" ]; then
pip uninstall -y guidata PythonQwt
cd ..
# Try cloning guidata from release, fallback to main or master
git clone --depth 1 --branch release https://github.com/PlotPyStack/guidata.git || git clone --depth 1 https://github.com/PlotPyStack/guidata.git || git clone --depth 1 --branch master https://github.com/PlotPyStack/guidata.git
# Try cloning PythonQwt from main or master
git clone --depth 1 https://github.com/PlotPyStack/PythonQwt.git || git clone --depth 1 --branch master https://github.com/PlotPyStack/PythonQwt.git
cd PlotPy
pip install -e ../guidata
pip install -e ../PythonQwt
# Install tomli for TOML parsing (safe if already present)
pip install tomli
# Extract dependencies and save to file, then install
python -c "import tomli; f=open('pyproject.toml','rb'); data=tomli.load(f); deps=[d for d in data['project']['dependencies'] if not any(p in d for p in ['guidata', 'PythonQwt'])]; open('deps.txt','w').write('\n'.join(deps))"
pip install -r deps.txt
# Install PlotPy without dependencies
pip install --no-deps .
else
# Install from PyPI normally for master branch
pip install .[test]
fi
- name: Lint with Ruff
run: ruff check --output-format=github plotpy
- name: Test with pytest
run: |
pytest
5 changes: 5 additions & 0 deletions doc/release_notes/release_2.12.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,8 @@
⚙️ Dependencies:

* Python 3.10 or later is now required, and wheels are no longer built for Python 3.9. Python 3.9 users must keep an earlier compatible release (PlotPy 2.11.x) (closes [Issue #74](https://github.com/PlotPyStack/PlotPy/issues/74))

🔧 Other changes:

* **Continuous integration** — PlotPy is now also tested with PyQt6 and PySide6 (Python 3.10 and 3.14) on each push and pull request, and with PyQt6 before each PyPI release. Pull requests targeting the `develop` or `release` branch are now tested against the development versions of guidata and PythonQwt, as pushes to these branches already were (closes [Issue #69](https://github.com/PlotPyStack/PlotPy/issues/69))
* **Test suite** — The `SnapshotTool` annotation test no longer hangs when run on its own, on Windows or with Qt 6: it waited indefinitely for the snapshot dialog to be closed
17 changes: 17 additions & 0 deletions plotpy/tests/unit/test_annotation_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import numpy as np
import pytest
from guidata.qthelpers import exec_dialog, execenv, qt_app_context
from qtpy import QtWidgets as QW

from plotpy.interfaces.items import IBasePlotItem, IShapeItemType
from plotpy.tests import vistools as ptv
Expand Down Expand Up @@ -97,13 +98,29 @@ def _test_annotation_tools(tool_classes: tuple[type[InteractiveTool], ...]):
for tool_class in tool_classes:
tool = win.manager.get_tool(tool_class)
assert tool is not None
snapshot_calls: list[tuple] | None = None
if execenv.unattended and isinstance(tool, SnapshotTool):
# The snapshot action opens modal dialogs (destination size, options,
# file name) which are never closed in unattended mode
snapshot_calls = []
tool.action_func = lambda *args, calls=snapshot_calls: calls.append(
args
)
tool.activate()
x_path = np.linspace(0, 0.5, 100)
y_path = np.linspace(0, 0.5, 100)
with execenv.context(accept_dialogs=True):
drag_mouse(win, x_path, y_path)
# Run the actions deferred by a zero-delay timer (e.g. the snapshot
# action) within this test rather than in a later event loop
QW.QApplication.processEvents()
if hasattr(tool_class, "SWITCH_TO_DEFAULT_TOOL"):
assert win.manager.get_default_tool() == default_tool
if snapshot_calls is not None:
assert len(snapshot_calls) == 1
snap_plot, p0, p1 = snapshot_calls[0]
assert snap_plot is plot
assert p0.x() < p1.x() and p0.y() < p1.y()
plot.unselect_all()
shape_items: list[IBasePlotItem] = plot.get_items(item_type=IShapeItemType)
plot.select_some_items(shape_items)
Expand Down
Loading