diff --git a/.github/workflows/base_test_workflow.yml b/.github/workflows/base_test_workflow.yml index ffd24d1de..43e5ec2df 100644 --- a/.github/workflows/base_test_workflow.yml +++ b/.github/workflows/base_test_workflow.yml @@ -52,6 +52,12 @@ jobs: test: name: ${{ inputs.os }} py ${{ inputs.python_version }} ${{ inputs.napari }} ${{ inputs.qt_backend }} ${{ inputs.pydantic }} runs-on: ${{ inputs.os }} + env: + NAPARI: ${{ inputs.napari }} + BACKEND: ${{ inputs.qt_backend }} + PYVISTA_OFF_SCREEN: True # required for opengl on windows + TOX_ARGS: ${{ inputs.tox_args }} + PYTEST_ARGS: ${{ inputs.pytest_args }} steps: - uses: actions/checkout@v6 - uses: actions/setup-python@v6 @@ -61,20 +67,11 @@ jobs: cache: 'pip' cache-dependency-path: 'pyproject.toml' -# - name: Install ubuntu libraries -# if: runner.os == 'Linux' -# run: | -# sudo apt update -# sudo apt-get install -y libegl1 libdbus-1-3 libxkbcommon-x11-0 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-xinerama0 libxcb-xinput0 libxcb-xfixes0 x11-utils libxcb-cursor0 - - - uses: awalsh128/cache-apt-pkgs-action@latest - if: runner.os == 'Linux' + - name: Setup headless display + uses: pyvista/setup-headless-display-action@7d84ae825e6d9297a8e99bdbbae20d1b919a0b19 # v4.2 with: - packages: libegl1 libdbus-1-3 libxkbcommon-x11-0 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-xinerama0 libxcb-xinput0 libxcb-xfixes0 x11-utils libxcb-cursor0 libhdf5-dev - version: 1.0 - - - name: Install Windows OpenGL - uses: pyvista/setup-headless-display-action@v4 + qt: true + wm: herbstluftwm - name: Download test data if: ${{ inputs.test_data }} @@ -90,47 +87,43 @@ jobs: - name: Test with tox PartSegImage if: ${{ inputs.napari == 'latest' }} - run: python -m tox ${{ inputs.tox_args }} -- package/tests/test_PartSegImage ${{ inputs.pytest_args }} + shell: bash + run: | + # shellcheck disable=SC2086 + python -m tox $TOX_ARGS -- package/tests/test_PartSegImage $PYTEST_ARGS env: - PYVISTA_OFF_SCREEN: True # required for opengl on windows - NAPARI: ${{ inputs.napari }} - BACKEND: ${{ inputs.qt_backend }} PIP_CONSTRAINT: ${{ inputs.napari == 'latest' && format('requirements/constraints_py{0}{1}.txt', inputs.python_version, inputs.pydantic ) || 'requirements/version_denylist.txt' }} UV_CONSTRAINT: ${{ inputs.napari == 'latest' && format('requirements/constraints_py{0}{1}.txt', inputs.python_version, inputs.pydantic ) || 'requirements/version_denylist.txt' }} - name: Test with tox PartSegCore if: ${{ inputs.napari == 'latest' }} - run: python -m tox ${{ inputs.tox_args }} -- package/tests/test_PartSegCore ${{ inputs.pytest_args }} + shell: bash + run: | + # shellcheck disable=SC2086 + python -m tox $TOX_ARGS -- package/tests/test_PartSegCore $PYTEST_ARGS env: - PYVISTA_OFF_SCREEN: True # required for opengl on windows - NAPARI: ${{ inputs.napari }} - BACKEND: ${{ inputs.qt_backend }} PIP_CONSTRAINT: ${{ inputs.napari == 'latest' && format('requirements/constraints_py{0}{1}.txt', inputs.python_version, inputs.pydantic ) || 'requirements/version_denylist.txt' }} UV_CONSTRAINT: ${{ inputs.napari == 'latest' && format('requirements/constraints_py{0}{1}.txt', inputs.python_version, inputs.pydantic ) || 'requirements/version_denylist.txt' }} - name: Test with tox PartSeg if: ${{ inputs.napari == 'latest' }} - uses: aganders3/headless-gui@v2 timeout-minutes: ${{ inputs.timeout }} - with: - run: python -m tox ${{ inputs.tox_args }} -- package/tests/test_PartSeg ${{ inputs.pytest_args }} + shell: bash + run: | + # shellcheck disable=SC2086 + python -m tox $TOX_ARGS -- package/tests/test_PartSeg $PYTEST_ARGS env: - PYVISTA_OFF_SCREEN: True # required for opengl on windows - NAPARI: ${{ inputs.napari }} - BACKEND: ${{ inputs.qt_backend }} PIP_CONSTRAINT: ${{ inputs.napari == 'latest' && format('requirements/constraints_py{0}{1}.txt', inputs.python_version, inputs.pydantic ) || 'requirements/version_denylist.txt' }} UV_CONSTRAINT: ${{ inputs.napari == 'latest' && format('requirements/constraints_py{0}{1}.txt', inputs.python_version, inputs.pydantic ) || 'requirements/version_denylist.txt' }} - name: Test with tox all if: ${{ inputs.napari != 'latest' }} - uses: aganders3/headless-gui@v2 timeout-minutes: ${{ inputs.timeout }} - with: - run: python -m tox ${{ inputs.tox_args }} -- ${{ inputs.pytest_args }} + shell: bash + run: | + # shellcheck disable=SC2086 + python -m tox $TOX_ARGS ${PYTEST_ARGS:+-- $PYTEST_ARGS} env: - PYVISTA_OFF_SCREEN: True # required for opengl on windows - NAPARI: ${{ inputs.napari }} - BACKEND: ${{ inputs.qt_backend }} PIP_CONSTRAINT: ${{ inputs.napari == 'latest' && format('requirements/constraints_py{0}{1}.txt', inputs.python_version, inputs.pydantic ) || 'requirements/version_denylist.txt' }} UV_CONSTRAINT: ${{ inputs.napari == 'latest' && format('requirements/constraints_py{0}{1}.txt', inputs.python_version, inputs.pydantic ) || 'requirements/version_denylist.txt' }} diff --git a/.github/workflows/test_napari_repo.yml b/.github/workflows/test_napari_repo.yml index 18a3e62e3..1270132d2 100644 --- a/.github/workflows/test_napari_repo.yml +++ b/.github/workflows/test_napari_repo.yml @@ -34,7 +34,7 @@ jobs: fail-fast: false matrix: platform: [ ubuntu-24.04 ] - python: ['3.10', '3.11', '3.12'] + python: ['3.10', '3.11', '3.12', '3.13'] napari_version: ['repo'] steps: - uses: actions/checkout@v6 diff --git a/package/PartSeg/common_gui/main_window.py b/package/PartSeg/common_gui/main_window.py index 6f5ba861b..e4dc5daea 100644 --- a/package/PartSeg/common_gui/main_window.py +++ b/package/PartSeg/common_gui/main_window.py @@ -227,10 +227,13 @@ def get_colormaps(self) -> list[Optional[colormap.Colormap]]: return [self.settings.colormap_dict[name][0] for name in colormaps_name] def napari_viewer_show(self): - viewer = Viewer(title="Additional output", settings=self.settings, partseg_viewer_name=self.channel_info) + viewer = Viewer( + title="Additional output", settings=self.settings, partseg_viewer_name=self.channel_info, show=False + ) viewer.scale_bar.unit = "nm" viewer.theme = self.settings.theme_name viewer.create_initial_layers(image=True, roi=True, additional_layers=False, points=True) + viewer.show() self.viewer_list.append(viewer) viewer.window.qt_viewer.destroyed.connect(lambda _x: self.close_viewer(viewer)) @@ -238,9 +241,12 @@ def additional_layers_show(self, with_channels=False): if not self.settings.additional_layers: QMessageBox().information(self, "No data", "Last executed algorithm does not provide additional data") return - viewer = Viewer(title="Additional output", settings=self.settings, partseg_viewer_name=self.channel_info) + viewer = Viewer( + title="Additional output", settings=self.settings, partseg_viewer_name=self.channel_info, show=False + ) viewer.theme = self.settings.theme_name viewer.create_initial_layers(image=with_channels, roi=False, additional_layers=True, points=False) + viewer.show() self.viewer_list.append(viewer) viewer.window.qt_viewer.destroyed.connect(lambda _x: self.close_viewer(viewer)) diff --git a/package/tests/test_PartSeg/test_viewer.py b/package/tests/test_PartSeg/test_viewer.py index 3284e7261..97461edee 100644 --- a/package/tests/test_PartSeg/test_viewer.py +++ b/package/tests/test_PartSeg/test_viewer.py @@ -73,7 +73,7 @@ class TestNapariViewer: def test_base(self, image, analysis_segmentation2, tmp_path): settings = BaseSettings(tmp_path) settings.image = image - viewer = Viewer(settings, "") + viewer = Viewer(settings, "", show=False) viewer.create_initial_layers(True, True, True, True) assert len(viewer.layers) == 2 viewer.create_initial_layers(True, True, True, True) @@ -92,7 +92,7 @@ def test_base(self, image, analysis_segmentation2, tmp_path): def test_points(self, image, tmp_path, qtbot): settings = BaseSettings(tmp_path) settings.image = image - viewer = Viewer(settings, "") + viewer = Viewer(settings, "", show=False) viewer.create_initial_layers(True, True, True, True) assert len(viewer.layers) == 2 points = np.array([[0, 1, 1, 1], [0, 7, 10, 10]]) @@ -113,7 +113,7 @@ def test_points(self, image, tmp_path, qtbot): def test_image(self, image, image2, tmp_path, qtbot): settings = BaseSettings(tmp_path) settings.image = image - viewer = Viewer(settings, "test") + viewer = Viewer(settings, "test", show=False) with qtbot.waitSignal(viewer._sync_widget.sync_image_chk.stateChanged): viewer._sync_widget.sync_image_chk.setChecked(True) assert len(viewer.layers) == 2 @@ -125,7 +125,7 @@ def test_image(self, image, image2, tmp_path, qtbot): def test_roi(self, image, tmp_path, qtbot): settings = BaseSettings(tmp_path) settings.image = image - viewer = Viewer(settings, "test") + viewer = Viewer(settings, "test", show=False) viewer._sync_widget.sync_image() assert len(viewer.layers) == 2 viewer._sync_widget.sync_ROI_chk.setChecked(True) @@ -135,10 +135,11 @@ def test_roi(self, image, tmp_path, qtbot): assert len(viewer.layers) == 4 viewer.close() - def test_additional(self, image, tmp_path, qtbot): + @pytest.mark.usefixtures("qtbot") + def test_additional(self, image, tmp_path): settings = BaseSettings(tmp_path) settings.image = image - viewer = Viewer(settings, "test") + viewer = Viewer(settings, "test", show=False) viewer._sync_widget.sync_image() assert len(viewer.layers) == 2 settings._additional_layers = { diff --git a/tox.ini b/tox.ini index b8651056d..b5acaa3b5 100644 --- a/tox.ini +++ b/tox.ini @@ -4,7 +4,7 @@ # and then run "tox" from this directory. [tox] -envlist = py{39,310,311,312,313}-{PyQt5,PySide2,PyQt6,PySide6}-all, py{39,310,311,312,313}-{PyQt5,PyQt6}-napari_{419,54,repo}, py{39,310}-PySide2-napari_{419,54,repo} +envlist = py{39,310,311,312,313}-{PyQt5,PySide2,PyQt6,PySide6}-all, py{310,311,312,313}-{PyQt5,PyQt6}-napari_repo,py{39,310,311,312,313}-{PyQt5,PyQt6,PySide2}-napari_{419,54}, py{312,313}-PySide6-napari_{419,54,repo} toxworkdir=/tmp/tox [gh-actions] @@ -71,15 +71,23 @@ deps= pytest-json-report lxml_html_clean -[testenv:py{39,310,311,312,313}-{PyQt5,PySide2,PyQt6,PySide6}-napari_{419,54,repo}] + +[testenv:py{39,310,311,312,313}-{PyQt5,PySide2,PyQt6,PySide6}-napari_{419,54}] deps = {[testenv]deps} napari_419: napari==0.4.19.post1 napari_54: napari==0.5.4 - napari_repo: git+https://github.com/napari/napari.git commands = - !napari_repo: python -m pytest -v package/tests/test_PartSeg/test_napari_widgets.py --json-report --json-report-file={toxinidir}/report-{envname}-{sys_platform}.json {posargs} - napari_repo: python -m pytest package/tests --json-report --json-report-file={toxinidir}/report-{envname}-{sys_platform}.json {posargs} + python -m pytest -v --json-report --json-report-file={toxinidir}/report-{envname}-{sys_platform}.json {posargs:package/tests/test_PartSeg/test_napari_widgets.py} + + +[testenv:py{310,311,312,313}-{PyQt5,PyQt6,PySide6}-napari_repo] +deps = + {[testenv]deps} + git+https://github.com/napari/napari.git +commands = + python -m pytest --json-report --json-report-file={toxinidir}/report-{envname}-{sys_platform}.json {posargs:package/tests} + [testenv:py{39,310,311,312,313}-PyQt5-coverage] deps =