diff --git a/.github/workflows/README.md b/.github/workflows/README.md index 71a0d8f..a8ff02e 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -1,17 +1,42 @@ # GitHub Actions -## Building the Conda Package: [conda_build_and_publish.yml](https://github.com/paskino/qt-elements/blob/main/.github/workflows/conda_build_and_publish.yml) -This github action builds and tests the conda package, by using the [conda-package-publish-action](https://github.com/paskino/conda-package-publish-action) +Runs automatically on every commit via [test.yml](./test.yml). -When pushing to main *all* variants are built and tested. +## Testing -When making an [annotated](https://git-scm.com/book/en/v2/Git-Basics-Tagging) tag, *all* variants are built, tested and published to the [paskino conda channel for qt-elements](https://anaconda.org/paskino/eqt/files). This package is noarch. +Runs `pytest`. -When opening or modifying a pull request to main, a single variant is built and tested, but not published. This variant is `python=3.7` and `numpy=1.18`. +## Building -## Building the PyPi Package: [pypi_publish.yml](https://github.com/paskino/qt-elements/blob/main/.github/workflows/pypi_publish.yml) -This github action builds the pypi package, by using the [deploy-pypi action](https://github.com/casperdcl/deploy-pypi). +Runs automatically after tests (above) succeed. -When pushing to main it is built and checked. +Builds binary (`*.whl`) & source (`*.tar.gz`) distributions. -When making an [annotated](https://git-scm.com/book/en/v2/Git-Basics-Tagging) tag, it is built and published to the [PyPi](https://pypi.org/project/eqt/#description). +## Releasing + +Runs automatically -- when an annotated tag is pushed -- after builds (above) succeed. + +Publishes to [PyPI](https://pypi.org/project/eqt). + +:warning: The annotated tag's `title` must be `Version ` (separated by a blank line) and the `body` must contain release notes, e.g.: + +```bash +git tag v1.33.7 -a +``` + +```md +Version 1.33.7 + + +``` + +The `` will be used in the changelog (below). + +### Changelog + +See , or offline: + +```bash +git config --global alias.changelog 'for-each-ref --sort=-*authordate --format="# %(contents:subject)%0a%(contents:body)" refs/tags' +git changelog +``` diff --git a/.github/workflows/conda_build_and_publish.yml b/.github/workflows/conda_build_and_publish.yml deleted file mode 100644 index 8612ca3..0000000 --- a/.github/workflows/conda_build_and_publish.yml +++ /dev/null @@ -1,27 +0,0 @@ -name: publish_conda - -on: - release: - types: [published] - push: - branches: [ main ] - tags: - - '**' - pull_request: - branches: [ main ] - -jobs: - publish: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v1 - - name: publish-to-conda - uses: paskino/conda-package-publish-action@v1.4.3 - with: - subDir: 'conda' - channels: 'conda-forge' - AnacondaToken: ${{ secrets.ANACONDA_TOKEN }} - publish: ${{ github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags') }} - test_all: ${{(github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags')) || (github.ref == 'refs/heads/main')}} - convert_win: false - convert_osx: false diff --git a/.github/workflows/pypi_publish.yml b/.github/workflows/pypi_publish.yml deleted file mode 100644 index a0df473..0000000 --- a/.github/workflows/pypi_publish.yml +++ /dev/null @@ -1,35 +0,0 @@ -# This is a basic workflow to help you get started with Actions - -name: publish_pypi - -# Controls when the action will run. -on: - # Triggers the workflow on push or pull request events but only for the main branch - push: - branches: [ main ] - tags: - - '**' - pull_request: - branches: [ main ] - - # Allows you to run this workflow manually from the Actions tab - workflow_dispatch: - -# A workflow run is made up of one or more jobs that can run sequentially or in parallel -jobs: - # This workflow contains a single job called "build" - build: - # The type of runner that the job will run on - runs-on: ubuntu-latest - - # Steps represent a sequence of tasks that will be executed as part of the job - steps: - # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - - uses: actions/checkout@v1 - - uses: actions/setup-python@v2 - - uses: casperdcl/deploy-pypi@v2 - with: - password: ${{ secrets.EQT_SECRET_TOKEN }} - pip: wheel -w dist/ --no-deps . - # only upload if a tag is pushed (otherwise just build & check) - upload: ${{ github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags') }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..19ae71f --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,72 @@ +name: Test +on: + push: + pull_request: +jobs: + test: + if: github.event_name != 'pull_request' || !contains('OWNER,MEMBER,COLLABORATOR', github.event.pull_request.author_association) + name: py${{ matrix.python }} + runs-on: ubuntu-latest + strategy: + matrix: + python: [3.7, 3.11] + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + - uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python }} + - run: pip install -U .[dev] + - run: pytest + deploy: + needs: [test] + name: PyPI Deploy + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + - uses: actions/setup-python@v4 + with: + python-version: '3.x' + - if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') + id: changes + name: Check annotated tag + run: | + title=$(git for-each-ref --format="%(contents:subject)" ${GITHUB_REF}) + body=$(git for-each-ref --format="%(contents:body)" ${GITHUB_REF}) + tag="${GITHUB_REF#refs/tags/}" + if test "$title" = "Version ${tag#v}" -a -n "$body"; then + echo "title=$title" >> "$GITHUB_OUTPUT" + echo "body=$body" >> "$GITHUB_OUTPUT" + else + echo "::error title=Missing tag annotation::$tag" + changelog=$(git log --pretty='format:%d%n- %s%n%b---' $(git tag --sort=v:refname | tail -n2 | head -n1)..HEAD) + cat <> "$GITHUB_STEP_SUMMARY" + # Missing tag annotation + ## Fix + See . + ## Suggested body + $changelog + EOF + exit 1 + fi + - id: dist + uses: casperdcl/deploy-pypi@v2 + with: + build: true + password: ${{ secrets.EQT_SECRET_TOKEN }} + upload: ${{ github.event_name == 'push' && startsWith(github.ref, 'refs/tags') }} + skip_existing: true # allow for annotated tag amendments + - if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') + name: Release + run: > + gh release create + --title "${{ steps.changes.outputs.title }}" + --notes "${{ steps.changes.outputs.body }}" + "${GITHUB_REF#refs/tags/}" + dist/${{ steps.dist.outputs.whl }} + dist/${{ steps.dist.outputs.targz }} + env: + GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }} diff --git a/.gitignore b/.gitignore index 4fe082b..091f8a9 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,9 @@ -eqt/version.py +*.py[cod] +__pycache__/ +/eqt/_dist_ver.py +/*.egg*/ +/build/ +/dist/ +/.coverage* +/coverage.xml +/.pytest_cache/ diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index ee2eb77..0000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,66 +0,0 @@ - -## v0.7.0 -- Adds `MainWindowWithProgressDialogs` a base class for a main window, with a menu bar, and ability to create ProgressTimerDialogs. -- Renames `SessionMainWindow` to `MainWindowWithSessionManagement` -- Removed addToMenu method from `MainWindowWithProgressDialogs` (and therefore `MainWindowWithSessionManagement` which inherits from it) as it didn't do anything, and the user can add their own method and call it if needed. -- Check if session folder loaded from QSettings exists before writing to it. - -## v0.6.0 -- Use pip install in the conda recipe, instead of setup.py install -- Adds the following new methods to UIFormWidget, FormWidget, FormDialog and FormDockWidget: - - `saveAllWidgetStates` - Saves the state of all widgets in the form. This can be used to restore the state of the widgets using the restoreAllSavedWidgetStates method. - - - `restoreAllSavedWidgetStates` - Restores the state of all widgets in the form to the state saved by the saveAllWidgetStates method. If the saveAllWidgetStates method has not been called, this method will do nothing. - - - `getAllWidgetStates` - Returns a dictionary of the state of all widgets in the form. - - - `getWidgetState` - Returns the state of the widget. - - - `applyWidgetState` - Applies the given state to the widget with the given name. - - - `applyWidgetStates` - Applies the given state to the widgets in the form given by the keys of the state dictionary. - -- Adds an example of a FormDialog: `dialog_save_state_example.py` where all of the widgets are saved and restored if you press "Ok", whereas the previous values of the dialog are restored if you press "Cancel". -- Adds unit tests to cover: `saveAllWidgetStates`, `restoreAllSavedWidgetStates`, `getAllWidgetStates`, `getWidgetState`, `applyWidgetState`, `applyWidgetStates` -- setup.py: - - Always normalise the version from git describe to pep440 -- Adds `SessionsMainWindow.py` - which is a base class for our apps which create a session folder where any files generated in the app are saved, and provides the ability to permanently save and reload sessions. -- Adds `SessionsMainWindow_example.py` - an example of using the SessionsMainWindow - you can run this example, change the state of widgets in the form, save the session, reload the session and see the state of the widgets be restored. -- Adds `SessionsDialogs.py` - the dialogs used by the SessionsMainWindow.py -- Adds `io.py` - contains method for zipping a directory, used by SessionsMainWindow.py -- Adds unit tests to cover `SessionsDialogs.py`, `io.py`, and a large proportion of `SessionsMainWindow.py` - -## v0.5.0 -* Add getWidgets method to FormWidget, FormDockWidget and FormDialog -* Add setWidgetVisibility method to FormWidget, FormDockWidget and FormDialog - -## v0.4.0 -* Add ReOrderableListWidget and ReOrderableListDockWidget -* Add example of using the ReOrderableListWidget -* Add getWidget method to FormWidget, FormDockWidget and FormDialog - -## v0.3.0 -* Add ProgressTimerDialog and example. -* Delete ErrorObserver, as this is relevant to VTK, not Qt, so it has been moved to the CILViewer repo. - -## v0.2.2 -* By default, automatically number the tab titles in a StackedWidget - -## v0.2.1 -* Adds methods to add titles and separators to the FormWidget -* Adds UIMultiStepWidget -* Adds version string from git describe -* Added unit tests and a way to disable interactive tests if run on conda build - -## v0.2.0 -* Adds methods to add titles and separators to the FormWidget -* Adds version string from git describe -* Added unit tests and a way to disable interactive tests if run on conda build -* Adds UIMultiStepWidget - -## v0.1.0 -* Fixes FormDockWidget by setting a FormWidget as the DockWidget's widget -* Allows a widget to be added to a FormWidget, which spans the width of the form -* Adds a factory for creating a StackedWidget -* Adds UISliderWidget - diff --git a/LICENSE b/LICENSE index 261eeb9..9e3bf15 100644 --- a/LICENSE +++ b/LICENSE @@ -1,201 +1,14 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ +Copyright 2020 United Kingdom Research and Innovation (https://stfc.ukri.org) +Authors: https://github.com/TomographicImaging/eqt/graphs/contributors - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at - 1. Definitions. + http://www.apache.org/licenses/LICENSE-2.0 - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/README.md b/README.md index 06f5dbc..27a7a94 100644 --- a/README.md +++ b/README.md @@ -8,10 +8,10 @@ running some task asynchronously so that the interface is still responsive. This little package tries to address both recurring requirements. -1. `UIFormWidget`, a class to help creating Qt forms - programmatically, useable in `QDockWidgets` and `QWidget` +1. `UIFormWidget`, a class to help creating Qt forms + programmatically, useable in `QDockWidgets` and `QWidget` 1. `FormDialog`, a `QDialog` with a form inside with OK and Cancel button -1. `Worker`, a class that defines a `QRunnable` to +1. `Worker`, a class that defines a `QRunnable` to handle worker thread setup, signals and wrap up ### Installation @@ -19,59 +19,50 @@ This little package tries to address both recurring requirements. You may install via `pip` or `conda` ```bash - - python -m pip install eqt - # or - conda install eqt -c paskino +python -m pip install eqt +# or +conda install eqt -c paskino ``` ### Example +In the `example` directory there is an example on how to launch a `QDialog` with a form inside using `eqt`'s [`QWidget`](https://github.com/TomographicImaging/eqt/blob/main/examples/dialog_example.py) or [`FormDialog`](https://github.com/TomographicImaging/eqt/blob/main/examples/dialog_example_2.py). -In the `example` directory there is an example on how to launch a -`QDialog` with a form inside using `eqt`'s - [`QWidget`](https://github.com/paskino/qt-elements/blob/main/examples/dialog_example.py) or [`FormDialog`](https://github.com/paskino/qt-elements/blob/main/examples/dialog_example_2.py). - ### Running asynchronous tasks -To run a function in a separate thread we use a `Worker` which is a subclass of a `QRunnable`. +To run a function in a separate thread we use a `Worker` which is a subclass of a `QRunnable`. For the `Worker` to work one needs to define: 1. the function that does what you need 2. Optional callback methods to get the status of the thread by means of `QtCore.QSignal`s - - - -On [initialisation](https://github.com/paskino/qt-elements/blob/535e487d09d928713d7d6aa1123657597627c4b0/eqt/threading/QtThreading.py#L32-L38) of the `Worker` the user needs to pass the function that has to run in the thread, i.e. `fn` below, and additional optional positional and keyword arguments, which will be passed on to the actual function that is run in the `QRunnable`. - +On [initialisation](https://github.com/TomographicImaging/eqt/blob/535e487d09d928713d7d6aa1123657597627c4b0/eqt/threading/QtThreading.py#L32-L38) of the `Worker` the user needs to pass the function that has to run in the thread, i.e. `fn` below, and additional optional positional and keyword arguments, which will be passed on to the actual function that is run in the `QRunnable`. ```python class Worker(QtCore.QRunnable): - def __init__(self, fn, *args, **kwargs): self.fn = fn self.args = args self.kwargs = kwargs self.signals = WorkerSignals() ``` - In practice the user will need to pass to the `Worker` as many parameters as there are listed in the [function](https://github.com/paskino/qt-elements/blob/535e487d09d928713d7d6aa1123657597627c4b0/eqt/threading/QtThreading.py#L56 -) to be run. + +In practice the user will need to pass to the `Worker` as many parameters as there are listed in the [function](https://github.com/TomographicImaging/eqt/blob/535e487d09d928713d7d6aa1123657597627c4b0/eqt/threading/QtThreading.py#L56) to be run. ```python result = self.fn(*self.args, **self.kwargs) ``` -But `Worker` will [add](https://github.com/paskino/qt-elements/blob/535e487d09d928713d7d6aa1123657597627c4b0/eqt/threading/QtThreading.py#L41-L43 -) to the `**kwargs` the following `QSignal`. +But `Worker` will [add](https://github.com/TomographicImaging/eqt/blob/535e487d09d928713d7d6aa1123657597627c4b0/eqt/threading/QtThreading.py#L41-L43) to the `**kwargs` the following `QSignal`. -```python +```python # Add progress callback to kwargs self.kwargs['progress_callback'] = self.signals.progress self.kwargs['message_callback'] = self.signals.message self.kwargs['status_callback'] = self.signals.status ``` -Therefore it is advisable to always have `**kwargs` in the function `fn` signature so that you can access the `QSignal` and emit the signal required. For instance one could emit a progress by +Therefore it is advisable to always have `**kwargs` in the function `fn` signature so that you can access the `QSignal` and emit the signal required. For instance one could emit a progress by: + ```python def fn(num_iter, **kwargs): progress_callback = kwargs.get('progress_callback', None) @@ -85,30 +76,26 @@ def fn(num_iter, **kwargs): This is done just after one has defined the `Worker`: ```python - def handle_progress(num_iter): # do something with the progress print ("Current progress is ", num_iter) worker = Worker(fn, 10) worker.signals.progress.connect(handle_progress) - ``` So, each time `fn` comes to `progress_callback.emit( i )` the function `handle_progress` will be called with the parameter `i` of its `for` loop. #### What are the available signals? - -The signals that are available in the `Worker` class are defined in [`WorkerSignal`](https://github.com/paskino/qt-elements/blob/535e487d09d928713d7d6aa1123657597627c4b0/eqt/threading/QtThreading.py#L66) and are the following. Below you can also see the type of data that each signal can emit. +The signals that are available in the `Worker` class are defined in [`WorkerSignal`](https://github.com/TomographicImaging/eqt/blob/535e487d09d928713d7d6aa1123657597627c4b0/eqt/threading/QtThreading.py#L66) and are the following. Below you can also see the type of data that each signal can emit. ```python - finished = QtCore.Signal() error = QtCore.Signal(tuple) result = QtCore.Signal(object) - + progress = QtCore.Signal(int) message = QtCore.Signal(str) status = QtCore.Signal(tuple) ``` -Read more on [Qt signals and slots](https://doc.qt.io/qt-5/signalsandslots.html) and on how to use them in [pyside2](https://wiki.qt.io/Qt_for_Python_Signals_and_Slots) +Read more on [Qt signals and slots](https://doc.qt.io/qt-5/signalsandslots.html) and on how to use them in [pyside2](https://wiki.qt.io/Qt_for_Python_Signals_and_Slots). diff --git a/conda/bld.bat b/conda/bld.bat deleted file mode 100644 index 5950f51..0000000 --- a/conda/bld.bat +++ /dev/null @@ -1,4 +0,0 @@ - -cd %RECIPE_DIR%\.. - -pip install . diff --git a/conda/build.sh b/conda/build.sh deleted file mode 100644 index e68ca1a..0000000 --- a/conda/build.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash - -# install using pip from the whl file -# pip install https://files.pythonhosted.org/packages/98/7b/98fcb18998153977ed3cb41f9e231ae7a8ceaa1fbda74a4c8e642b69e676/qtelements-1.0.1-py3-none-any.whl - -#$PYTHON -m pip install eqt --no-deps -cd $RECIPE_DIR/.. -pip install . diff --git a/conda/conda_build_config.yaml b/conda/conda_build_config.yaml deleted file mode 100644 index d06cf07..0000000 --- a/conda/conda_build_config.yaml +++ /dev/null @@ -1,3 +0,0 @@ -python: - - 3.6 - - 3.7 diff --git a/conda/meta.yaml b/conda/meta.yaml deleted file mode 100644 index 7c5fa90..0000000 --- a/conda/meta.yaml +++ /dev/null @@ -1,36 +0,0 @@ -package: - name: eqt - version: {{ environ.get('GIT_DESCRIBE_TAG','v')[1:] }} - -source: - path: .. - -build: - skip: True # [py==38 and np==115] - preserve_egg_dir: False - number: {{ environ.get('GIT_DESCRIBE_NUMBER', 0)}} - noarch: python - -test: - source_files: - - ./test - - commands: - - python -c "import os; print ('TESTING IN THIS DIRECTORY' , os.getcwd())" - - python -m unittest discover -s test -v - - -requirements: - build: - - python - - pip - - sphinx - run: - - python - - pyside2 - - qdarkstyle - -about: - home: https://github.com/paskino/qt-elements - license: Apache 2.0 - summary: A number of templates and tools to develop Qt GUI's with Python effectively. diff --git a/eqt/__init__.py b/eqt/__init__.py index d33d233..812265a 100644 --- a/eqt/__init__.py +++ b/eqt/__init__.py @@ -1,3 +1,10 @@ -from eqt import version as dversion +# version detector. Precedence: installed dist, git, 'UNKNOWN' +try: + from ._dist_ver import __version__ +except ImportError: + try: + from setuptools_scm import get_version -__version__ = dversion.version + __version__ = get_version(root="..", relative_to=__file__) + except (ImportError, LookupError): + __version__ = "UNKNOWN" diff --git a/eqt/io.py b/eqt/io.py index cc369be..957e5c8 100644 --- a/eqt/io.py +++ b/eqt/io.py @@ -2,29 +2,21 @@ import zipfile +def zip_directory(directory: str, compress: bool=True): + """ + Zips a directory, optionally compressing it. -def zip_directory(directory, compress=True, **kwargs): - ''' - Zips a directory, optionally compressing it. - - Parameters - ---------- - directory : str - The directory to be zipped. - compress : bool - Whether to compress the directory. - ''' - - zipper = zipfile.ZipFile(directory + '.zip', 'a') - - if compress: - compress_type = zipfile.ZIP_DEFLATED - else: - compress_type = zipfile.ZIP_STORED - + Parameters + ---------- + directory + The directory to be zipped. + compress + Whether to compress the directory. + """ + compress_type = zipfile.ZIP_DEFLATED if compress else zipfile.ZIP_STORED + with zipfile.ZipFile(f'{directory}.zip', 'a') as zipper: for r, _, f in os.walk(directory): for _file in f: filepath = os.path.join(r, _file) arcname = os.path.relpath(filepath, directory) zipper.write(filepath, arcname, compress_type=compress_type) - zipper.close() \ No newline at end of file diff --git a/examples/MainWindowWithSessionManagement_example.py b/examples/MainWindowWithSessionManagement_example.py index 2c22c17..fe9e322 100644 --- a/examples/MainWindowWithSessionManagement_example.py +++ b/examples/MainWindowWithSessionManagement_example.py @@ -6,17 +6,14 @@ from eqt.ui.MainWindowWithSessionManagement import MainWindowWithSessionManagement from eqt.ui.UIFormWidget import FormWidget from eqt.ui.UISliderWidget import UISliderWidget - -from eqt.version import version - -__version__ = version +from eqt import __version__ class ExampleMainWindowWithSessionManagement(MainWindowWithSessionManagement): ''' This is an example of a MainWindowWithSessionManagement. It is used to show how to use the MainWindowWithSessionManagement class. - + To test out this example, change the state of the widgets and save the session. Then load the session again and check if the state of the widgets is the same as before. ''' @@ -37,7 +34,7 @@ def getSessionConfig(self): ''' This function is called when the user wants to save the current session. We need to add the state of the widgets to the config. - + Returns ------- config : dict @@ -87,8 +84,8 @@ def add_every_widget_to_form(form): def create_main_window(): window = ExampleMainWindowWithSessionManagement( - "Example Session Main Window{}".format(__version__), "Example0", settings_name="Example0") - + f"Example Session Main Window{__version__}", "Example0", settings_name="Example0") + return window diff --git a/examples/reorderable_list_widget_example.py b/examples/reorderable_list_widget_example.py index c9f3e5a..4050771 100644 --- a/examples/reorderable_list_widget_example.py +++ b/examples/reorderable_list_widget_example.py @@ -1,12 +1,8 @@ from eqt.ui.ReOrderableListWidget import ReOrderableListWidget from PySide2 import QtWidgets import sys -try: - import qdarkstyle - from qdarkstyle.dark.palette import DarkPalette - HAS_QDARKSTYLE = True -except: - HAS_QDARKSTYLE = False +import qdarkstyle +from qdarkstyle.dark.palette import DarkPalette class MainUI(QtWidgets.QMainWindow): @@ -14,23 +10,22 @@ def __init__(self, parent = None, title=None): QtWidgets.QMainWindow.__init__(self, parent) self.setWindowTitle(title) - + qlist = ReOrderableListWidget() qlist.addItems(["Transmission to Absorption", "Centre of Rotation Correction"]) qlist.addItem("FBP") - + layout = QtWidgets.QHBoxLayout() layout.addWidget(qlist) layout.setSizeConstraint(QtWidgets.QLayout.SetMinimumSize) - + widg = QtWidgets.QWidget() widg.setLayout(layout) self.setCentralWidget(widg) - if HAS_QDARKSTYLE: - style = qdarkstyle.load_stylesheet(palette=DarkPalette) - self.setStyleSheet(style) + style = qdarkstyle.load_stylesheet(palette=DarkPalette) + self.setStyleSheet(style) self.show() @@ -39,8 +34,8 @@ def __init__(self, parent = None, title=None): if __name__ == "__main__": app = QtWidgets.QApplication(sys.argv) - + window = MainUI(title="ReOrderableListWidget") window.resize(400, 150) - - sys.exit(app.exec_()) \ No newline at end of file + + sys.exit(app.exec_()) diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..5f16a8e --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,71 @@ +[build-system] +requires = ["setuptools>=42", "setuptools_scm[toml]>=3.4"] +build-backend = "setuptools.build_meta" + +[tool.setuptools_scm] +write_to = "eqt/_dist_ver.py" +write_to_template = "__version__ = '{version}'\n" + +[tool.setuptools.packages.find] +exclude = ["test"] + +[project.urls] +documentation = "https://github.com/TomographicImaging/eqt#readme" +repository = "https://github.com/TomographicImaging/eqt" +changelog = "https://github.com/TomographicImaging/eqt/releases" + +[project] +name = "eqt" +dynamic = ["version"] +authors = [ + {name = "Edoardo Pasca", email = "edoardo.pasca@stfc.ac.uk"}, + {name = "Laura Murgatroyd", email = "laura.murgatroyd@stfc.ac.uk"}] +maintainers = [{name = "Casper da Costa-Luis", email = "casper.dcl@physics.org"}] +description = "A number of templates and tools to develop Qt GUIs with Python effectively" +readme = "README.md" +requires-python = ">=3.7" +license = {text = "Apache-2.0"} +classifiers = [ + "Development Status :: 4 - Beta", + "License :: OSI Approved :: Apache Software License", + "Operating System :: OS Independent", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3 :: Only"] +dependencies = ["pyside2", "qdarkstyle"] + +[project.optional-dependencies] +dev = ["pytest>=6", "pytest-cov", "pytest-timeout"] + +[tool.flake8] +max_line_length = 99 +exclude = [".git", "__pycache__", "build", "dist", ".eggs"] + +[tool.yapf] +spaces_before_comment = [15, 20] +arithmetic_precedence_indication = true +allow_split_before_dict_value = false +coalesce_brackets = true +column_limit = 99 +each_dict_entry_on_separate_line = false +space_between_ending_comma_and_closing_bracket = false +split_before_named_assigns = false +split_before_closing_bracket = false +blank_line_before_nested_class_or_def = false + +[tool.isort] +profile = "black" +line_length = 99 +multi_line_output = 4 +known_first_party = ["eqt", "test"] + +[tool.pytest.ini_options] +minversion = "6.0" +timeout = 30 +log_level = "INFO" +testpaths = ["test"] +addopts = "-v --tb=short -rxs -W=error --durations=0 --durations-min=1 --cov=eqt --cov-report=term-missing --cov-report=xml" diff --git a/setup.py b/setup.py deleted file mode 100644 index 37d6a08..0000000 --- a/setup.py +++ /dev/null @@ -1,88 +0,0 @@ -#!/usr/bin/env python3 - -from setuptools import setup -import os -import subprocess - -try: - from sphinx.setup_command import BuildDoc - sphinx_available = False - cmdclass = {'build_sphinx': BuildDoc} - -except ImportError as ie: - sphinx_available = False - cmdclass = {} - -def version2pep440(version): - '''normalises the version from git describe to pep440 - - https://www.python.org/dev/peps/pep-0440/#id29 - ''' - if version[0] == 'v': - version = version[1:] - - if u'-' in version: - v = version.split('-') - v_pep440 = "{}.dev{}".format(v[0], v[1]) - else: - v_pep440 = version - - return v_pep440 - - -git_version_string = subprocess.check_output('git describe', shell=True).decode("utf-8").rstrip()[1:] - - -#with open("README.rst", "r") as fh: -# long_description = fh.read() -long_description = 'A number of templates and tools to develop Qt GUIs with Python effectively.' - -if os.environ.get('CONDA_BUILD', 0) == '1': - # if it is a conda build requirements are going to be satisfied by conda - install_requires = [] - cwd = os.path.join(os.environ.get('RECIPE_DIR'),'..') -else: - install_requires = [ - - 'sphinx', - 'pyside2' - - ] - cwd = os.getcwd() - -version = version2pep440(git_version_string) - - -print ('version {}'.format(version)) -print(cwd) -fname = os.path.join(cwd, 'eqt', 'version.py') -print("write version at: ", fname) - -if os.path.exists(fname): - os.remove(fname) -with open(fname, 'w') as f: - f.write("version = \"{}\"".format(version)) -name = "eqt" - -setup(name=name, - version = version, - description = 'A number of templates and tools to develop Qt GUIs with Python effectively', - long_description = long_description, - author = 'Edoardo Pasca', - author_email = 'edoardo.pasca@stfc.ac.uk', - url = '', - packages = ['eqt', 'eqt.threading', 'eqt.ui'], - license = 'Apache v2.0', - install_requires=install_requires, - classifiers = [ - "Development Status :: 4 - Beta", - "Programming Language :: Python :: 3", - "License :: OSI Approved :: Apache Software License", - "Operating System :: OS Independent", - ], - command_options={ - 'build_sphinx': { - 'project': ('setup.py', name), - 'version': ('setup.py', version), - 'source_dir': ('setup.py', 'doc')}}, - ) diff --git a/test/__init__.py b/test/__init__.py index e69de29..9e48711 100644 --- a/test/__init__.py +++ b/test/__init__.py @@ -0,0 +1,81 @@ +import os + +from pytest import skip + +from eqt.ui import UIFormFactory, FormDialog +from PySide2 import QtWidgets + +if any(os.getenv(var, '0').lower() in ('1', 'true') for var in ('CONDA_BUILD', 'CI')): + def skip_ci(_): + def inner(*_, **__): + skip("Running in CI (no GUI)") + return inner +else: + def skip_ci(fn): + return fn + +@skip_ci +def test_import(): + from dialog_example_2_test import MainUI, DialogTest + + +class MainUI(QtWidgets.QMainWindow): + + def __init__(self, parent=None): + QtWidgets.QMainWindow.__init__(self, parent) + + pb = QtWidgets.QPushButton(self) + pb.setText("Open Dialog with form layout") + pb.clicked.connect(lambda: self.openFormDialog()) + + layout = QtWidgets.QHBoxLayout() + layout.addWidget(pb) + widg = QtWidgets.QWidget() + widg.setLayout(layout) + self.push_button = pb + + self.setCentralWidget(widg) + + self.show() + + def openFormDialog(self): + + dialog = FormDialog(parent=self, title='Example') + dialog.Ok.clicked.connect(lambda: self.accepted()) + dialog.Cancel.clicked.connect(lambda: self.rejected()) + + # Example on how to add elements to the + # add input 1 as QLineEdit + qlabel = QtWidgets.QLabel(dialog.groupBox) + qlabel.setText("Input 1: ") + qwidget = QtWidgets.QLineEdit(dialog.groupBox) + qwidget.setClearButtonEnabled(True) + # finally add to the form widget + dialog.addWidget(qwidget, qlabel, 'input1', layout='form') + + # add input 2 as QComboBox + qlabel = QtWidgets.QLabel(dialog.groupBox) + qlabel.setText("Input 2: ") + qwidget = QtWidgets.QComboBox(dialog.groupBox) + qwidget.addItem("option 1") + qwidget.addItem("option 2") + qwidget.setCurrentIndex(0) + qwidget.setEnabled(True) + # finally add to the form widget + dialog.addWidget(qwidget, qlabel, 'input2') + + # store a reference + self.dialog = dialog + + dialog.exec() + + def accepted(self): + print("accepted") + print(self.dialog.widgets['input1_field'].text()) + print(self.dialog.widgets['input2_field'].currentText()) + + self.dialog.close() + + def rejected(self): + print("rejected") + self.dialog.close() diff --git a/test/common.py b/test/common.py deleted file mode 100644 index 6142dfc..0000000 --- a/test/common.py +++ /dev/null @@ -1,63 +0,0 @@ -from eqt.ui import UIFormFactory, FormDialog -from PySide2 import QtWidgets - -class MainUI(QtWidgets.QMainWindow): - - def __init__(self, parent=None): - QtWidgets.QMainWindow.__init__(self, parent) - - pb = QtWidgets.QPushButton(self) - pb.setText("Open Dialog with form layout") - pb.clicked.connect(lambda: self.openFormDialog()) - - layout = QtWidgets.QHBoxLayout() - layout.addWidget(pb) - widg = QtWidgets.QWidget() - widg.setLayout(layout) - self.push_button = pb - - self.setCentralWidget(widg) - - self.show() - - def openFormDialog(self): - - dialog = FormDialog(parent=self, title='Example') - dialog.Ok.clicked.connect(lambda: self.accepted()) - dialog.Cancel.clicked.connect(lambda: self.rejected()) - - # Example on how to add elements to the - # add input 1 as QLineEdit - qlabel = QtWidgets.QLabel(dialog.groupBox) - qlabel.setText("Input 1: ") - qwidget = QtWidgets.QLineEdit(dialog.groupBox) - qwidget.setClearButtonEnabled(True) - # finally add to the form widget - dialog.addWidget(qwidget, qlabel, 'input1', layout='form') - - # add input 2 as QComboBox - qlabel = QtWidgets.QLabel(dialog.groupBox) - qlabel.setText("Input 2: ") - qwidget = QtWidgets.QComboBox(dialog.groupBox) - qwidget.addItem("option 1") - qwidget.addItem("option 2") - qwidget.setCurrentIndex(0) - qwidget.setEnabled(True) - # finally add to the form widget - dialog.addWidget(qwidget, qlabel, 'input2') - - # store a reference - self.dialog = dialog - - dialog.exec() - - def accepted(self): - print("accepted") - print(self.dialog.widgets['input1_field'].text()) - print(self.dialog.widgets['input2_field'].currentText()) - - self.dialog.close() - - def rejected(self): - print("rejected") - self.dialog.close() \ No newline at end of file diff --git a/test/dialog_example_2_test.py b/test/dialog_example_2_test.py index 09cb43d..b6431e8 100644 --- a/test/dialog_example_2_test.py +++ b/test/dialog_example_2_test.py @@ -1,22 +1,13 @@ from PySide2 import QtCore, QtWidgets, QtTest from PySide2.QtTest import QTest from PySide2.QtCore import Qt -import glob import sys -import os -from eqt.ui import UIFormFactory, FormDialog +from eqt.ui import FormDialog import unittest +from . import skip_ci -# skip the tests on GitHub actions -if os.environ.get('CONDA_BUILD', '0') == '1': - skip_as_conda_build = True -else: - skip_as_conda_build = False - -print ("skip_as_conda_build is set to ", skip_as_conda_build) class MainUI(QtWidgets.QMainWindow): - def __init__(self, parent=None): QtWidgets.QMainWindow.__init__(self, parent) @@ -35,7 +26,6 @@ def __init__(self, parent=None): self.show() def openFormDialog(self): - dialog = FormDialog(parent=self, title='Example') dialog.Ok.clicked.connect(lambda: self.accepted()) dialog.Cancel.clicked.connect(lambda: self.rejected()) @@ -82,8 +72,7 @@ def rejected(self): class DialogTest(unittest.TestCase): '''Test the margarita mixer GUI''' - - @unittest.skipIf(skip_as_conda_build, "On conda builds do not do any test with interfaces") + @skip_ci def setUp(self): '''Create the GUI''' super(DialogTest, self).setUp() @@ -99,27 +88,25 @@ def setUp(self): # QTest.mouseClick(self.window.push_button, Qt.LeftButton) # self.dialog = window.dialog - @unittest.skipIf(skip_as_conda_build, "On conda builds do not do any test with interfaces") + @skip_ci def tearDown(self): del self.app super(DialogTest, self).tearDown() - @unittest.skipIf(skip_as_conda_build, "On conda builds do not do any test with interfaces") + @skip_ci def test_close(self): self.window.close() self.assertTrue(True) - @unittest.skipIf(skip_as_conda_build, "On conda builds do not do any test with interfaces") + @skip_ci def test_openclose_dialog(self): QTest.mouseClick(self.window.push_button, Qt.LeftButton) dialog = self.window.dialog print(dialog) dialog.close() - @unittest.skipUnless(skip_as_conda_build, "On conda builds do not do any test with interfaces") def stest_defaults(self): '''Test the GUI in its default state''' - self.dialog = self.window.dialog print("test1") self.assertEqual(self.window.dialog.widgets['input1_field'].text(), '') diff --git a/test/test_MainWindowWithSessionManagement.py b/test/test_MainWindowWithSessionManagement.py index 7726675..304e8aa 100644 --- a/test/test_MainWindowWithSessionManagement.py +++ b/test/test_MainWindowWithSessionManagement.py @@ -1,37 +1,22 @@ import json import os import shutil -import sys import unittest from datetime import datetime from unittest import mock from unittest.mock import patch from PySide2.QtCore import QSettings, QThreadPool -from PySide2.QtWidgets import QApplication, QMenu, QMenuBar +from PySide2.QtWidgets import QMenu, QMenuBar import eqt from eqt.io import zip_directory from eqt.ui.MainWindowWithSessionManagement import MainWindowWithSessionManagement -# skip the tests on GitHub actions -if os.environ.get('CONDA_BUILD', '0') == '1': - skip_as_conda_build = True -else: - skip_as_conda_build = False +from . import skip_ci -print("skip_as_conda_build is set to ", skip_as_conda_build) -if not skip_as_conda_build: - if not QApplication.instance(): - app = QApplication(sys.argv) - else: - app = QApplication.instance() -else: - skip_test = True - - -@unittest.skipIf(skip_as_conda_build, "On conda builds do not do any test with interfaces") +@skip_ci class TestMainWindowWithSessionManagementInit(unittest.TestCase): ''' Tests the init method of the MainWindowWithSessionManagement class @@ -94,7 +79,7 @@ def test_init_calls_setUpSession(self, mock_setupSession): smw.setupSession.assert_called_once() -@unittest.skipIf(skip_as_conda_build, "On conda builds do not do any test with interfaces") +@skip_ci class TestMainWindowWithSessionManagementMenuBar(unittest.TestCase): ''' Tests the expected menu bar is created @@ -140,7 +125,7 @@ def test_settings_menu_has_expected_actions(self): 1].text(), "Set Session Directory") -@unittest.skipIf(skip_as_conda_build, "On conda builds do not do any test with interfaces") +@skip_ci class TestMainWindowWithSessionManagementSetupSession(unittest.TestCase): ''' Tests the setupSession method of the MainWindowWithSessionManagement class @@ -201,7 +186,7 @@ def test_setupSession_when_sessions_folder_setting_is_not_None_and_session_folde self.smw.createSessionSelector.assert_not_called() -@unittest.skipIf(skip_as_conda_build, "On conda builds do not do any test with interfaces") +@skip_ci class TestMainWindowWithSessionManagementCreateSessionSelector(unittest.TestCase): ''' Tests the createSessionSelector method of the MainWindowWithSessionManagement class @@ -253,7 +238,7 @@ def tearDown(self): shutil.rmtree("Test Folder") -@unittest.skipIf(skip_as_conda_build, "On conda builds do not do any test with interfaces") +@skip_ci class TestMainWindowWithSessionManagementCreateLoadSessionDialog(unittest.TestCase): ''' Tests the createLoadSessionDialog method of the MainWindowWithSessionManagement class @@ -299,7 +284,7 @@ def test_createLoadSessionDialog_connections(self): self.smw.loadSessionNew.assert_called_once() -@unittest.skipIf(skip_as_conda_build, "On conda builds do not do any test with interfaces") +@skip_ci class TestSelectLoadSessionsDirectorySelectedInSessionSelector(unittest.TestCase): ''' Tests the selectLoadSessionsDirectorySelectedInSessionSelector method of the MainWindowWithSessionManagement class @@ -324,7 +309,7 @@ def test_selectLoadSessionsDirectorySelectedInSessionSelector(self): new_session=True) -@unittest.skipIf(skip_as_conda_build, "On conda builds do not do any test with interfaces") +@skip_ci class TestCreateSessionFolder(unittest.TestCase): ''' Tests the createSessionFolder method of the MainWindowWithSessionManagement class @@ -355,7 +340,7 @@ def tearDown(self): shutil.rmtree("Test Folder") -@unittest.skipIf(skip_as_conda_build, "On conda builds do not do any test with interfaces") +@skip_ci class TestLoadSessionConfig(unittest.TestCase): ''' Tests the loadSessionConfig method of the MainWindowWithSessionManagement class @@ -402,7 +387,7 @@ def tearDown(self): shutil.rmtree("Test Folder") -@unittest.skipIf(skip_as_conda_build, "On conda builds do not do any test with interfaces") +@skip_ci class TestSaveSession(unittest.TestCase): ''' Tests: @@ -493,7 +478,7 @@ def test_saveSessionConfigToJson(self): shutil.rmtree("Test Folder") -@unittest.skipIf(skip_as_conda_build, "On conda builds do not do any test with interfaces") +@skip_ci class TestRemoveTempMethods(unittest.TestCase): ''' Tests: @@ -534,7 +519,3 @@ def test_removeTempAndClose(self): self.smw.removeTemp.assert_called_once() self.smw.finishProcess.assert_called_once_with(process_name) self.smw.close.assert_called_once() - - -if __name__ == "__main__": - unittest.main() diff --git a/test/test_SessionDialogs.py b/test/test_SessionDialogs.py index 1a2505b..1ecbac6 100644 --- a/test/test_SessionDialogs.py +++ b/test/test_SessionDialogs.py @@ -1,28 +1,14 @@ from eqt.ui.SessionDialogs import WarningDialog, ErrorDialog, SaveSessionDialog, SessionDirectorySelectionDialog, LoadSessionDialog, AppSettingsDialog import os import unittest -from PySide2.QtWidgets import QApplication, QFileDialog -import sys +from pathlib import Path +from PySide2.QtWidgets import QFileDialog from unittest.mock import patch -# skip the tests on GitHub actions -if os.environ.get('CONDA_BUILD', '0') == '1': - skip_as_conda_build = True -else: - skip_as_conda_build = False +from . import skip_ci -print("skip_as_conda_build is set to ", skip_as_conda_build) -if not skip_as_conda_build: - if not QApplication.instance(): - app = QApplication(sys.argv) - else: - app = QApplication.instance() -else: - skip_test = True - - -@unittest.skipIf(skip_as_conda_build, "On conda builds do not do any test with interfaces") +@skip_ci class TestWarningDialog(unittest.TestCase): def test_init(self): wd = WarningDialog() @@ -40,7 +26,7 @@ def test_init_with_params(self): self.assertEqual(wd.windowTitle(), window_title) -@unittest.skipIf(skip_as_conda_build, "On conda builds do not do any test with interfaces") +@skip_ci class TestErrorDialog(unittest.TestCase): def test_init(self): ed = ErrorDialog() @@ -58,7 +44,7 @@ def test_init_with_params(self): self.assertEqual(ed.windowTitle(), window_title) -@unittest.skipIf(skip_as_conda_build, "On conda builds do not do any test with interfaces") +@skip_ci class TestSaveSessionDialog(unittest.TestCase): def test_init(self): ssd = SaveSessionDialog() @@ -70,7 +56,7 @@ def test_init_with_title_param(self): self.assertEqual(ssd.windowTitle(), title) -@unittest.skipIf(skip_as_conda_build, "On conda builds do not do any test with interfaces") +@skip_ci class TestSessionDirectorySelectionDialog(unittest.TestCase): def test_init(self): sdsd = SessionDirectorySelectionDialog() @@ -120,7 +106,7 @@ def test_browse_dialog_updates_selected_dir_attribute(self): self.assertEqual(sdsd.selected_dir, example_dir) -@unittest.skipIf(skip_as_conda_build, "On conda builds do not do any test with interfaces") +@skip_ci class TestLoadSessionDialog(unittest.TestCase): def test_init(self): lsd = LoadSessionDialog() @@ -132,20 +118,14 @@ def test_init_with_title_param(self): self.assertEqual(lsd.windowTitle(), title) def test_init_with_location_of_session_files_param(self): - location_of_session_files = "C:\\Users\\test_user\\Documents\\test_dir" - lsd = LoadSessionDialog( - location_of_session_files=location_of_session_files) + location_of_session_files = Path("~").expanduser() / "some" / "test_dir" + lsd = LoadSessionDialog(location_of_session_files=location_of_session_files) self.assertEqual(lsd.getWidget("sessions_directory").text( - ), "Currently loading sessions from: C:\\Users\\test_user\\Documents\\test_dir") + ), f"Currently loading sessions from: {location_of_session_files}") -@unittest.skipIf(skip_as_conda_build, "On conda builds do not do any test with interfaces") +@skip_ci class TestAppSettingsDialog(unittest.TestCase): - def test_init(self): asd = AppSettingsDialog() assert asd is not None - - -if __name__ == "__main__": - unittest.main() diff --git a/test/test__formUI_status_test.py b/test/test__formUI_status_test.py index 5e4235e..09eb817 100644 --- a/test/test__formUI_status_test.py +++ b/test/test__formUI_status_test.py @@ -1,90 +1,56 @@ -import os -import sys +import abc import unittest from unittest import mock from eqt.ui.FormDialog import FormDialog from eqt.ui.UIFormWidget import FormWidget, FormDockWidget from PySide2 import QtWidgets -from PySide2.QtWidgets import QApplication from eqt.ui.UISliderWidget import UISliderWidget +from . import skip_ci -# skip the tests on GitHub actions -if os.environ.get('CONDA_BUILD', '0') == '1': - skip_test = True -else: - skip_test = False - -print("skip_test is set to ", skip_test) - - -if not skip_test: - if not QApplication.instance(): - app = QApplication(sys.argv) - else: - app = QApplication.instance() -else: - skip_test = True - - -def add_every_widget_to_form(form): - ''' - Generate every widget and add it to the form - - Parameters - ---------- - form : FormWidget, FormDialog or FormDockWidget - The form to add the widgets to - ''' - form.addWidget(QtWidgets.QLabel('test label'), 'Label: ', 'label') - form.addWidget(QtWidgets.QCheckBox( - 'test checkbox'), 'CheckBox: ', 'checkBox') - form.addWidget(QtWidgets.QComboBox(), 'ComboBox: ', 'comboBox') - form.addWidget(QtWidgets.QDoubleSpinBox(), - 'DoubleSpinBox: ', 'doubleSpinBox') - form.addWidget(QtWidgets.QSpinBox(), 'SpinBox: ', 'spinBox') - form.addWidget(QtWidgets.QSlider(), 'Slider: ', 'slider') - form.addWidget(UISliderWidget(QtWidgets.QLabel()), - 'UISliderWidget: ', 'uiSliderWidget') - form.addWidget(QtWidgets.QRadioButton('test'), - 'RadioButton: ', 'radioButton') - form.addWidget(QtWidgets.QTextEdit('test'), 'TextEdit: ', 'textEdit') - form.addWidget(QtWidgets.QPlainTextEdit('test'), - 'PlainTextEdit: ', 'plainTextEdit') - form.addWidget(QtWidgets.QLineEdit('test'), 'LineEdit: ', 'lineEdit') - form.addWidget(QtWidgets.QPushButton('test'), 'Button: ', 'button') - - -def add_two_widgets_to_form(form): - ''' - Generate two widgets and add them to the form - - Parameters - ---------- - form : FormWidget, FormDialog or FormDockWidget - The form to add the widgets to - ''' - form.addWidget(QtWidgets.QLabel('test label'), 'Label: ', 'label') - form.addWidget(QtWidgets.QCheckBox( - 'test checkbox'), 'CheckBox: ', 'checkBox') - - -@unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") -class FormDialogStatusTest(unittest.TestCase): - - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") + +class FormsCommonTests(metaclass=abc.ABCMeta): + """Common tests for all Form types""" + @abc.abstractmethod def setUp(self): - self.form = FormDialog() - add_every_widget_to_form(self.form) - self.simple_form = FormDialog() - add_two_widgets_to_form(self.simple_form) + raise NotImplementedError + + def add_every_widget(self): + """Generate every widget and add it to `self.form`""" + form = self.form + form.addWidget(QtWidgets.QLabel('test label'), 'Label: ', 'label') + form.addWidget(QtWidgets.QCheckBox( + 'test checkbox'), 'CheckBox: ', 'checkBox') + form.addWidget(QtWidgets.QComboBox(), 'ComboBox: ', 'comboBox') + form.addWidget(QtWidgets.QDoubleSpinBox(), + 'DoubleSpinBox: ', 'doubleSpinBox') + form.addWidget(QtWidgets.QSpinBox(), 'SpinBox: ', 'spinBox') + form.addWidget(QtWidgets.QSlider(), 'Slider: ', 'slider') + form.addWidget(UISliderWidget(QtWidgets.QLabel()), + 'UISliderWidget: ', 'uiSliderWidget') + form.addWidget(QtWidgets.QRadioButton('test'), + 'RadioButton: ', 'radioButton') + form.addWidget(QtWidgets.QTextEdit('test'), 'TextEdit: ', 'textEdit') + form.addWidget(QtWidgets.QPlainTextEdit('test'), + 'PlainTextEdit: ', 'plainTextEdit') + form.addWidget(QtWidgets.QLineEdit('test'), 'LineEdit: ', 'lineEdit') + form.addWidget(QtWidgets.QPushButton('test'), 'Button: ', 'button') + + + def add_two_widgets(self): + """Generate two widgets and add them to `self.simple_form`""" + form = self.simple_form + form.addWidget(QtWidgets.QLabel('test label'), 'Label: ', 'label') + form.addWidget(QtWidgets.QCheckBox( + 'test checkbox'), 'CheckBox: ', 'checkBox') - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") def test_getWidgetState_returns_visibility(self): - # Check that the visibility of the widget is saved to the state - # Have to use magic mock as we can't set the visibility of the QLabel - # to be True, because the FormDialog is not visible + """ + Check that the visibility of the widget is saved to the state + Have to use magic mock as we can't set the visibility of the QLabel + to be True, because the FormDialog is not visible + """ initial_label_visibility = True self.form.getWidget('label').isVisible = mock.MagicMock() self.form.getWidget( @@ -99,9 +65,8 @@ def test_getWidgetState_returns_visibility(self): self.assertEqual(self.form.getWidgetState('label_field')[ 'visible'], final_label_visibility) - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") def test_getWidgetState_returns_enabled_state(self): - # Check that the enabled state of the widget is saved to the state + """Check that the enabled state of the widget is saved to the state""" initial_label_enabled_state = True @@ -114,27 +79,8 @@ def test_getWidgetState_returns_enabled_state(self): self.assertEqual(self.form.getWidgetState('label_field')[ 'enabled'], final_label_enabled_state) - # Test value is saved for all widget types ---------------------------------------------------------------- - - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") - def test_getWidgetState_returns_QLabel_value(self): - # Check that the value of the QLabel is saved to the state - - initial_label_value = 'Label: ' - - self.assertEqual(self.form.getWidgetState( - 'label_label')['value'], initial_label_value) - - final_label_value = 'final test label' - self.form.getWidget('label', 'label').setText(final_label_value) - - self.assertEqual(self.form.getWidgetState( - 'label_label')['value'], final_label_value) - - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") def test_getWidgetState_returns_value_using_role_parameter_field(self): - # Check that the value of the QLabel is saved to the state - + """Check that the value of the QLabel is saved to the state""" initial_label_value = 'test label' self.assertEqual(self.form.getWidgetState( @@ -146,10 +92,8 @@ def test_getWidgetState_returns_value_using_role_parameter_field(self): self.assertEqual(self.form.getWidgetState( 'label', 'field')['value'], final_label_value) - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") def test_getWidgetState_returns_value_using_role_parameter_label(self): - # Check that the value of the QLabel is saved to the state - + """Check that the value of the QLabel is saved to the state""" initial_label_value = 'test label' self.assertEqual(self.form.getWidgetState( @@ -161,9 +105,8 @@ def test_getWidgetState_returns_value_using_role_parameter_label(self): self.assertEqual(self.form.getWidgetState( 'label', 'field')['value'], final_label_value) - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") def test_getWidgetState_returns_value_using_default_role_parameter(self): - # Check that the value of the QLabel is saved to the state + """Check that the value of the QLabel is saved to the state""" initial_label_value = 'test label' # In getWidgetState we do not specify if we want the 'field' or 'label' role, so it should default to 'field': @@ -176,10 +119,8 @@ def test_getWidgetState_returns_value_using_default_role_parameter(self): self.assertEqual(self.form.getWidgetState( 'label')['value'], final_label_value) - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") def test_getWidgetState_returns_QCheckBox_value(self): - # Check that the value of the QCheckBox is saved to the state - + """Check that the value of the QCheckBox is saved to the state""" initial_checkbox_value = False self.assertEqual(self.form.getWidgetState('checkBox_field')[ @@ -191,10 +132,8 @@ def test_getWidgetState_returns_QCheckBox_value(self): self.assertEqual(self.form.getWidgetState( 'checkBox_field')['value'], final_checkbox_value) - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") def test_getWidgetState_returns_QComboBox_value(self): - # Check that the value of the QComboBox is saved to the state - + """Check that the value of the QComboBox is saved to the state""" combobox_list = ['test', 'test2'] self.form.getWidget('comboBox').addItems(combobox_list) @@ -209,10 +148,8 @@ def test_getWidgetState_returns_QComboBox_value(self): self.assertEqual(self.form.getWidgetState( 'comboBox_field')['value'], final_combobox_value) - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") def test_getWidgetState_returns_QDoubleSpinBox_value(self): - # Check that the value of the QDoubleSpinBox is saved to the state - + """Check that the value of the QDoubleSpinBox is saved to the state""" initial_doubleSpinBox_value = 0.0 self.assertEqual(self.form.getWidgetState('doubleSpinBox_field')[ @@ -225,10 +162,8 @@ def test_getWidgetState_returns_QDoubleSpinBox_value(self): self.assertEqual(self.form.getWidgetState('doubleSpinBox_field')[ 'value'], final_doubleSpinBox_value) - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") def test_getWidgetState_returns_QSpinBox_value(self): - # Check that the value of the QSpinBox is saved to the state - + """Check that the value of the QSpinBox is saved to the state""" initial_spinBox_value = 0 self.assertEqual(self.form.getWidgetState('spinBox_field')[ @@ -240,10 +175,8 @@ def test_getWidgetState_returns_QSpinBox_value(self): self.assertEqual(self.form.getWidgetState( 'spinBox_field')['value'], final_spinBox_value) - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") def test_getWidgetState_returns_QSlider_value(self): - # Check that the value of the QSlider is saved to the state - + """Check that the value of the QSlider is saved to the state""" initial_slider_value = 0 self.assertEqual(self.form.getWidgetState( @@ -255,10 +188,8 @@ def test_getWidgetState_returns_QSlider_value(self): self.assertEqual(self.form.getWidgetState( 'slider_field')['value'], final_slider_value) - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") def test_getWidgetState_returns_UISliderWidget_value(self): - # Check that the value of the UISliderWidget is returned in the state - + """Check that the value of the UISliderWidget is returned in the state""" initial_slider_value = 0 self.assertEqual(self.form.getWidgetState( @@ -270,10 +201,8 @@ def test_getWidgetState_returns_UISliderWidget_value(self): self.assertEqual(self.form.getWidgetState( 'uiSliderWidget_field')['value'], final_slider_value) - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") def test_getWidgetState_returns_QLineEdit_value(self): - # Check that the value of the QLineEdit is saved to the state - + """Check that the value of the QLineEdit is saved to the state""" initial_lineEdit_value = '' self.form.getWidget('lineEdit').setText(initial_lineEdit_value) @@ -286,10 +215,8 @@ def test_getWidgetState_returns_QLineEdit_value(self): self.assertEqual(self.form.getWidgetState( 'lineEdit_field')['value'], final_lineEdit_value) - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") def test_getWidgetState_returns_QTextEdit_value(self): - # Check that the value of the QTextEdit is saved to the state - + """Check that the value of the QTextEdit is saved to the state""" initial_textEdit_value = '' self.form.getWidget('textEdit').setText(initial_textEdit_value) @@ -302,10 +229,8 @@ def test_getWidgetState_returns_QTextEdit_value(self): self.assertEqual(self.form.getWidgetState( 'textEdit_field')['value'], final_textEdit_value) - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") def test_getWidgetState_returns_QPlainTextEdit_value(self): - # Check that the value of the QPlainTextEdit is saved to the state - + """Check that the value of the QPlainTextEdit is saved to the state""" initial_plainTextEdit_value = '' self.form.getWidget('plainTextEdit').setPlainText( initial_plainTextEdit_value) @@ -320,10 +245,8 @@ def test_getWidgetState_returns_QPlainTextEdit_value(self): self.assertEqual(self.form.getWidgetState('plainTextEdit_field')[ 'value'], final_plainTextEdit_value) - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") def test_getWidgetState_returns_QPushButton_value(self): - # Check that the value of the QPushButton is saved to the state - + """Check that the value of the QPushButton is saved to the state""" initial_button_value = False self.assertEqual(self.form.getWidgetState( @@ -336,10 +259,8 @@ def test_getWidgetState_returns_QPushButton_value(self): self.assertEqual(self.form.getWidgetState( 'button_field')['value'], final_button_value) - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") def test_getWidgetState_returns_QRadioButton_value(self): - # Check that the value of the QRadioButton is saved to the state - + """Check that the value of the QRadioButton is saved to the state""" initial_radio_value = False self.assertEqual(self.form.getWidgetState( @@ -351,7 +272,6 @@ def test_getWidgetState_returns_QRadioButton_value(self): self.assertEqual(self.form.getWidgetState( 'radioButton_field')['value'], final_radio_value) - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") def test_applyWidgetStates(self): state_to_set = { 'checkBox_field': {'value': True, 'enabled': False, 'visible': False}, @@ -365,14 +285,12 @@ def test_applyWidgetStates(self): self.assertEqual(self.simple_form.getWidgetState( 'label_field'), state_to_set['label_field']) - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") def test_applyWidgetState(self): state_to_set = {'value': True, 'enabled': False, 'visible': False} self.simple_form.applyWidgetState('checkBox_field', state_to_set) self.assertEqual(self.simple_form.getWidgetState( 'checkBox_field'), state_to_set) - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") def test_applyWidgetState_using_role_parameter_field(self): state_to_set = {'value': True, 'enabled': False, 'visible': False} self.simple_form.applyWidgetState( @@ -380,7 +298,6 @@ def test_applyWidgetState_using_role_parameter_field(self): self.assertEqual(self.simple_form.getWidgetState( 'checkBox', 'field'), state_to_set) - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") def test_applyWidgetState_using_role_parameter_label(self): state_to_set = {'value': 'test the checkbox:', 'enabled': False, 'visible': False} @@ -389,17 +306,14 @@ def test_applyWidgetState_using_role_parameter_label(self): self.assertEqual(self.simple_form.getWidgetState( 'checkBox', 'label'), state_to_set) - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") def test_applyWidgetState_using_role_parameter_default(self): state_to_set = {'value': True, 'enabled': False, 'visible': False} self.simple_form.applyWidgetState('checkBox', state_to_set) self.assertEqual(self.simple_form.getWidgetState( 'checkBox'), state_to_set) - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") def test_getAllWidgetStates(self): - # Check that the state of all widgets is returned - + """Check that the state of all widgets is returned""" expected_state = { 'checkBox_field': {'value': False, 'enabled': True, 'visible': False}, 'label_field': {'value': 'test label', 'enabled': True, 'visible': False}, @@ -409,35 +323,47 @@ def test_getAllWidgetStates(self): self.assertEqual(self.simple_form.getAllWidgetStates(), expected_state) - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") - def test_saveAllWidgetStates(self): - # Check that the state of all widgets is saved to the state variable +@skip_ci +class FormDialogStatusTest(FormsCommonTests, unittest.TestCase): + def setUp(self): + self.form = FormDialog() + self.add_every_widget() + self.simple_form = FormDialog() + self.add_two_widgets() + + def test_getWidgetState_returns_QLabel_value(self): + """Check that the value of the QLabel is saved to the state""" + initial_label_value = 'Label: ' + self.assertEqual(self.form.getWidgetState( + 'label_label')['value'], initial_label_value) + + final_label_value = 'final test label' + self.form.getWidget('label', 'label').setText(final_label_value) + self.assertEqual(self.form.getWidgetState( + 'label_label')['value'], final_label_value) + + def test_saveAllWidgetStates(self): + """Check that the state of all widgets is saved to the state variable""" expected_state = { 'checkBox_field': {'value': False, 'enabled': True, 'visible': False}, 'label_field': {'value': 'test label', 'enabled': True, 'visible': False}, 'checkBox_label': {'value': 'CheckBox: ', 'enabled': True, 'visible': False}, 'label_label': {'value': 'Label: ', 'enabled': True, 'visible': False} } - self.simple_form.saveAllWidgetStates() - self.assertEqual( self.simple_form.formWidget.widget_states, expected_state) - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") def test_restoreAllSavedWidgetStates(self): - # Check that the state of all widgets is restored from the state variable - + """Check that the state of all widgets is restored from the state variable""" state_to_restore = { 'checkBox_field': {'value': True, 'enabled': False, 'visible': False}, 'label_field': {'value': 'applyWidgetStates Test', 'enabled': True, 'visible': False}, 'checkBox_label': {'value': 'CheckBox Test: ', 'enabled': True, 'visible': False}, 'label_label': {'value': 'Label Test: ', 'enabled': True, 'visible': False} } - self.simple_form.formWidget.widget_states = state_to_restore - self.simple_form.restoreAllSavedWidgetStates() self.assertEqual(self.simple_form.getWidget( @@ -466,770 +392,114 @@ def test_restoreAllSavedWidgetStates(self): 'label', 'label').isVisible(), state_to_restore['label_label']['visible']) -@unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") -class FormWidgetStateTest(unittest.TestCase): - - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") +@skip_ci +class FormWidgetStateTest(FormsCommonTests, unittest.TestCase): def setUp(self): self.form = FormWidget() - add_every_widget_to_form(self.form) + self.add_every_widget() self.simple_form = FormWidget() - add_two_widgets_to_form(self.simple_form) - - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") - def test_getWidgetState_returns_visibility(self): - # Check that the visibility of the widget is saved to the state - # Have to use magic mock as we can't set the visibility of the QLabel - # to be True, because the FormDialog is not visible - initial_label_visibility = True - self.form.getWidget('label').isVisible = mock.MagicMock() - self.form.getWidget( - 'label').isVisible.return_value = initial_label_visibility - - self.assertEqual(self.form.getWidgetState('label_field')[ - 'visible'], initial_label_visibility) - - final_label_visibility = False - self.form.getWidget('label').isVisible.return_value = False - - self.assertEqual(self.form.getWidgetState('label_field')[ - 'visible'], final_label_visibility) - - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") - def test_getWidgetState_returns_enabled_state(self): - # Check that the enabled state of the widget is saved to the state - - initial_label_enabled_state = True - - self.assertEqual(self.form.getWidgetState('label_field')[ - 'enabled'], initial_label_enabled_state) - - self.form.getWidget('label').setEnabled(False) - final_label_enabled_state = False - - self.assertEqual(self.form.getWidgetState('label_field')[ - 'enabled'], final_label_enabled_state) - - # Test value is saved for all widget types ---------------------------------------------------------------- - - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") - def test_getWidgetState_returns_value_using_role_parameter_field(self): - # Check that the value of the QLabel is saved to the state + self.add_two_widgets() + def test_getWidgetState_returns_QLabel_value(self): + """Check that the value of the QLabel is saved to the state""" initial_label_value = 'test label' - self.assertEqual(self.form.getWidgetState( - 'label', 'field')['value'], initial_label_value) + 'label_field')['value'], initial_label_value) final_label_value = 'final test label' self.form.getWidget('label').setText(final_label_value) - - self.assertEqual(self.form.getWidgetState( - 'label', 'field')['value'], final_label_value) - - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") - def test_getWidgetState_returns_value_using_role_parameter_label(self): - # Check that the value of the QLabel is saved to the state - - initial_label_value = 'test label' - self.assertEqual(self.form.getWidgetState( - 'label', 'field')['value'], initial_label_value) - - final_label_value = 'final test label' - self.form.getWidget('label').setText(final_label_value) + 'label_field')['value'], final_label_value) - self.assertEqual(self.form.getWidgetState( - 'label', 'field')['value'], final_label_value) + def test_saveAllWidgetStates(self): + """Check that the state of all widgets is saved to the state variable""" + expected_state = { + 'checkBox_field': {'value': False, 'enabled': True, 'visible': False}, + 'label_field': {'value': 'test label', 'enabled': True, 'visible': False}, + 'checkBox_label': {'value': 'CheckBox: ', 'enabled': True, 'visible': False}, + 'label_label': {'value': 'Label: ', 'enabled': True, 'visible': False} + } + self.simple_form.saveAllWidgetStates() + self.assertEqual(self.simple_form.widget_states, expected_state) - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") - def test_getWidgetState_returns_value_using_default_role_parameter(self): - # Check that the value of the QLabel is saved to the state - initial_label_value = 'test label' + def test_restoreAllSavedWidgetStates(self): + """Check that the state of all widgets is restored from the state variable""" + state_to_restore = { + 'checkBox_field': {'value': True, 'enabled': False, 'visible': False}, + 'label_field': {'value': 'applyWidgetStates Test', 'enabled': True, 'visible': False}, + 'checkBox_label': {'value': 'CheckBox Test: ', 'enabled': True, 'visible': False}, + 'label_label': {'value': 'Label Test: ', 'enabled': True, 'visible': False} + } + self.simple_form.widget_states = state_to_restore + self.simple_form.restoreAllSavedWidgetStates() - # In getWidgetState we do not specify if we want the 'field' or 'label' role, so it should default to 'field': - self.assertEqual(self.form.getWidgetState( - 'label')['value'], initial_label_value) + self.assertEqual(self.simple_form.getWidget( + 'checkBox').isChecked(), state_to_restore['checkBox_field']['value']) + self.assertEqual(self.simple_form.getWidget( + 'checkBox').isEnabled(), state_to_restore['checkBox_field']['enabled']) + self.assertEqual(self.simple_form.getWidget( + 'checkBox').isVisible(), state_to_restore['checkBox_field']['visible']) + self.assertEqual(self.simple_form.getWidget( + 'checkBox', 'label').text(), state_to_restore['checkBox_label']['value']) + self.assertEqual(self.simple_form.getWidget( + 'checkBox', 'label').isEnabled(), state_to_restore['checkBox_label']['enabled']) + self.assertEqual(self.simple_form.getWidget( + 'checkBox', 'label').isVisible(), state_to_restore['checkBox_label']['visible']) + self.assertEqual(self.simple_form.getWidget( + 'label').text(), state_to_restore['label_field']['value']) + self.assertEqual(self.simple_form.getWidget( + 'label').isEnabled(), state_to_restore['label_field']['enabled']) + self.assertEqual(self.simple_form.getWidget( + 'label').isVisible(), state_to_restore['label_field']['visible']) + self.assertEqual(self.simple_form.getWidget( + 'label', 'label').text(), state_to_restore['label_label']['value']) + self.assertEqual(self.simple_form.getWidget( + 'label', 'label').isEnabled(), state_to_restore['label_label']['enabled']) + self.assertEqual(self.simple_form.getWidget( + 'label', 'label').isVisible(), state_to_restore['label_label']['visible']) - final_label_value = 'final test label' - self.form.getWidget('label').setText(final_label_value) - self.assertEqual(self.form.getWidgetState( - 'label')['value'], final_label_value) +@skip_ci +class FormDockWidgetStateTest(FormsCommonTests, unittest.TestCase): + def setUp(self): + self.form = FormDockWidget() + self.add_every_widget() + self.simple_form = FormDockWidget() + self.add_two_widgets() - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") def test_getWidgetState_returns_QLabel_value(self): - # Check that the value of the QLabel is saved to the state - + """Check that the value of the QLabel is saved to the state""" initial_label_value = 'test label' - self.assertEqual(self.form.getWidgetState( 'label_field')['value'], initial_label_value) final_label_value = 'final test label' self.form.getWidget('label').setText(final_label_value) - self.assertEqual(self.form.getWidgetState( 'label_field')['value'], final_label_value) - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") - def test_getWidgetState_returns_QCheckBox_value(self): - # Check that the value of the QCheckBox is saved to the state - - initial_checkbox_value = False - - self.assertEqual(self.form.getWidgetState('checkBox_field')[ - 'value'], initial_checkbox_value) - - final_checkbox_value = True - self.form.getWidget('checkBox').setChecked(final_checkbox_value) - - self.assertEqual(self.form.getWidgetState( - 'checkBox_field')['value'], final_checkbox_value) - - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") - def test_getWidgetState_returns_QComboBox_value(self): - # Check that the value of the QComboBox is saved to the state - - combobox_list = ['test', 'test2'] - self.form.getWidget('comboBox').addItems(combobox_list) - - initial_combobox_value = 0 - - self.assertEqual(self.form.getWidgetState('comboBox_field')[ - 'value'], initial_combobox_value) - - final_combobox_value = 1 - self.form.getWidget('comboBox').setCurrentIndex(final_combobox_value) - - self.assertEqual(self.form.getWidgetState( - 'comboBox_field')['value'], final_combobox_value) - - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") - def test_getWidgetState_returns_QDoubleSpinBox_value(self): - # Check that the value of the QDoubleSpinBox is saved to the state - - initial_doubleSpinBox_value = 0.0 - - self.assertEqual(self.form.getWidgetState('doubleSpinBox_field')[ - 'value'], initial_doubleSpinBox_value) - - final_doubleSpinBox_value = 1.0 - self.form.getWidget('doubleSpinBox').setValue( - final_doubleSpinBox_value) - - self.assertEqual(self.form.getWidgetState('doubleSpinBox_field')[ - 'value'], final_doubleSpinBox_value) - - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") - def test_getWidgetState_returns_QSpinBox_value(self): - # Check that the value of the QSpinBox is saved to the state - - initial_spinBox_value = 0 - - self.assertEqual(self.form.getWidgetState('spinBox_field')[ - 'value'], initial_spinBox_value) - - final_spinBox_value = 1 - self.form.getWidget('spinBox').setValue(final_spinBox_value) - - self.assertEqual(self.form.getWidgetState( - 'spinBox_field')['value'], final_spinBox_value) - - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") - def test_getWidgetState_returns_QSlider_value(self): - # Check that the value of the QSlider is saved to the state - - initial_slider_value = 0 - - self.assertEqual(self.form.getWidgetState( - 'slider_field')['value'], initial_slider_value) - - final_slider_value = 1 - self.form.getWidget('slider').setValue(final_slider_value) - - self.assertEqual(self.form.getWidgetState( - 'slider_field')['value'], final_slider_value) - - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") - def test_getWidgetState_returns_UISliderWidget_value(self): - # Check that the value of the UISliderWidget is returned in the state - - initial_slider_value = 0 - - self.assertEqual(self.form.getWidgetState( - 'uiSliderWidget_field')['value'], initial_slider_value) + def test_saveAllWidgetStates(self): + """Check that the state of all widgets is saved to the state variable""" + expected_state = { + 'checkBox_field': {'value': False, 'enabled': True, 'visible': False}, + 'label_field': {'value': 'test label', 'enabled': True, 'visible': False}, + 'checkBox_label': {'value': 'CheckBox: ', 'enabled': True, 'visible': False}, + 'label_label': {'value': 'Label: ', 'enabled': True, 'visible': False} + } + self.simple_form.saveAllWidgetStates() + self.assertEqual( + self.simple_form.widget().widget_states, expected_state) - final_slider_value = 1 - self.form.getWidget('uiSliderWidget').setValue(final_slider_value) - - self.assertEqual(self.form.getWidgetState( - 'uiSliderWidget_field')['value'], final_slider_value) - - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") - def test_getWidgetState_returns_QLineEdit_value(self): - # Check that the value of the QLineEdit is saved to the state - - initial_lineEdit_value = '' - self.form.getWidget('lineEdit').setText(initial_lineEdit_value) - - self.assertEqual(self.form.getWidgetState('lineEdit_field')[ - 'value'], initial_lineEdit_value) - - final_lineEdit_value = 'test' - self.form.getWidget('lineEdit').setText(final_lineEdit_value) - - self.assertEqual(self.form.getWidgetState( - 'lineEdit_field')['value'], final_lineEdit_value) - - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") - def test_getWidgetState_returns_QTextEdit_value(self): - # Check that the value of the QTextEdit is saved to the state - - initial_textEdit_value = '' - self.form.getWidget('textEdit').setText(initial_textEdit_value) - - self.assertEqual(self.form.getWidgetState('textEdit_field')[ - 'value'], initial_textEdit_value) - - final_textEdit_value = 'test' - self.form.getWidget('textEdit').setText(final_textEdit_value) - - self.assertEqual(self.form.getWidgetState( - 'textEdit_field')['value'], final_textEdit_value) - - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") - def test_getWidgetState_returns_QPlainTextEdit_value(self): - # Check that the value of the QPlainTextEdit is saved to the state - - initial_plainTextEdit_value = '' - self.form.getWidget('plainTextEdit').setPlainText( - initial_plainTextEdit_value) - - self.assertEqual(self.form.getWidgetState('plainTextEdit_field')[ - 'value'], initial_plainTextEdit_value) - - final_plainTextEdit_value = 'test' - self.form.getWidget('plainTextEdit').setPlainText( - final_plainTextEdit_value) - - self.assertEqual(self.form.getWidgetState('plainTextEdit_field')[ - 'value'], final_plainTextEdit_value) - - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") - def test_getWidgetState_returns_QPushButton_value(self): - # Check that the value of the QPushButton is saved to the state - - initial_button_value = False - - self.assertEqual(self.form.getWidgetState( - 'button_field')['value'], initial_button_value) - - final_button_value = True - self.form.getWidget('button').setCheckable(True) - self.form.getWidget('button').setChecked(final_button_value) - - self.assertEqual(self.form.getWidgetState( - 'button_field')['value'], final_button_value) - - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") - def test_getWidgetState_returns_QRadioButton_value(self): - # Check that the value of the QRadioButton is saved to the state - - initial_radio_value = False - - self.assertEqual(self.form.getWidgetState( - 'radioButton_field')['value'], initial_radio_value) - - final_radio_value = True - self.form.getWidget('radioButton').setChecked(final_radio_value) - - self.assertEqual(self.form.getWidgetState( - 'radioButton_field')['value'], final_radio_value) - - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") - def test_applyWidgetStates(self): - state_to_set = { - 'checkBox_field': {'value': True, 'enabled': False, 'visible': False}, - 'label_field': {'value': 'applyWidgetStates Test', 'enabled': True, 'visible': False} - } - - self.simple_form.applyWidgetStates(state_to_set) - - self.assertEqual(self.simple_form.getWidgetState( - 'checkBox_field'), state_to_set['checkBox_field']) - self.assertEqual(self.simple_form.getWidgetState( - 'label_field'), state_to_set['label_field']) - - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") - def test_applyWidgetState(self): - state_to_set = {'value': True, 'enabled': False, 'visible': False} - self.simple_form.applyWidgetState('checkBox_field', state_to_set) - self.assertEqual(self.simple_form.getWidgetState( - 'checkBox_field'), state_to_set) - - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") - def test_applyWidgetState_using_role_parameter_field(self): - state_to_set = {'value': True, 'enabled': False, 'visible': False} - self.simple_form.applyWidgetState( - 'checkBox', state_to_set, role='field') - self.assertEqual(self.simple_form.getWidgetState( - 'checkBox', 'field'), state_to_set) - - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") - def test_applyWidgetState_using_role_parameter_label(self): - state_to_set = {'value': 'test the checkbox:', - 'enabled': False, 'visible': False} - self.simple_form.applyWidgetState( - 'checkBox', state_to_set, role='label') - self.assertEqual(self.simple_form.getWidgetState( - 'checkBox', 'label'), state_to_set) - - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") - def test_applyWidgetState_using_role_parameter_default(self): - state_to_set = {'value': True, 'enabled': False, 'visible': False} - self.simple_form.applyWidgetState('checkBox', state_to_set) - self.assertEqual(self.simple_form.getWidgetState( - 'checkBox'), state_to_set) - - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") - def test_getAllWidgetStates(self): - # Check that the state of all widgets is returned - - expected_state = { - 'checkBox_field': {'value': False, 'enabled': True, 'visible': False}, - 'label_field': {'value': 'test label', 'enabled': True, 'visible': False}, - 'checkBox_label': {'value': 'CheckBox: ', 'enabled': True, 'visible': False}, - 'label_label': {'value': 'Label: ', 'enabled': True, 'visible': False} - } - - self.assertEqual(self.simple_form.getAllWidgetStates(), expected_state) - - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") - def test_saveAllWidgetStates(self): - # Check that the state of all widgets is saved to the state variable - - expected_state = { - 'checkBox_field': {'value': False, 'enabled': True, 'visible': False}, - 'label_field': {'value': 'test label', 'enabled': True, 'visible': False}, - 'checkBox_label': {'value': 'CheckBox: ', 'enabled': True, 'visible': False}, - 'label_label': {'value': 'Label: ', 'enabled': True, 'visible': False} - } - - self.simple_form.saveAllWidgetStates() - - self.assertEqual(self.simple_form.widget_states, expected_state) - - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") - def test_restoreAllSavedWidgetStates(self): - # Check that the state of all widgets is restored from the state variable - - state_to_restore = { - 'checkBox_field': {'value': True, 'enabled': False, 'visible': False}, - 'label_field': {'value': 'applyWidgetStates Test', 'enabled': True, 'visible': False}, - 'checkBox_label': {'value': 'CheckBox Test: ', 'enabled': True, 'visible': False}, - 'label_label': {'value': 'Label Test: ', 'enabled': True, 'visible': False} - } - - self.simple_form.widget_states = state_to_restore - - self.simple_form.restoreAllSavedWidgetStates() - - self.assertEqual(self.simple_form.getWidget( - 'checkBox').isChecked(), state_to_restore['checkBox_field']['value']) - self.assertEqual(self.simple_form.getWidget( - 'checkBox').isEnabled(), state_to_restore['checkBox_field']['enabled']) - self.assertEqual(self.simple_form.getWidget( - 'checkBox').isVisible(), state_to_restore['checkBox_field']['visible']) - self.assertEqual(self.simple_form.getWidget( - 'checkBox', 'label').text(), state_to_restore['checkBox_label']['value']) - self.assertEqual(self.simple_form.getWidget( - 'checkBox', 'label').isEnabled(), state_to_restore['checkBox_label']['enabled']) - self.assertEqual(self.simple_form.getWidget( - 'checkBox', 'label').isVisible(), state_to_restore['checkBox_label']['visible']) - self.assertEqual(self.simple_form.getWidget( - 'label').text(), state_to_restore['label_field']['value']) - self.assertEqual(self.simple_form.getWidget( - 'label').isEnabled(), state_to_restore['label_field']['enabled']) - self.assertEqual(self.simple_form.getWidget( - 'label').isVisible(), state_to_restore['label_field']['visible']) - self.assertEqual(self.simple_form.getWidget( - 'label', 'label').text(), state_to_restore['label_label']['value']) - self.assertEqual(self.simple_form.getWidget( - 'label', 'label').isEnabled(), state_to_restore['label_label']['enabled']) - self.assertEqual(self.simple_form.getWidget( - 'label', 'label').isVisible(), state_to_restore['label_label']['visible']) - - -@unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") -class FormDockWidgetStateTest(unittest.TestCase): - - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") - def setUp(self): - self.form = FormDockWidget() - add_every_widget_to_form(self.form) - self.simple_form = FormDockWidget() - add_two_widgets_to_form(self.simple_form) - - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") - def test_getWidgetState_returns_visibility(self): - # Check that the visibility of the widget is saved to the state - # Have to use magic mock as we can't set the visibility of the QLabel - # to be True, because the FormDialog is not visible - initial_label_visibility = True - self.form.getWidget('label').isVisible = mock.MagicMock() - self.form.getWidget( - 'label').isVisible.return_value = initial_label_visibility - - self.assertEqual(self.form.getWidgetState('label_field')[ - 'visible'], initial_label_visibility) - - final_label_visibility = False - self.form.getWidget('label').isVisible.return_value = False - - self.assertEqual(self.form.getWidgetState('label_field')[ - 'visible'], final_label_visibility) - - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") - def test_getWidgetState_returns_enabled_state(self): - # Check that the enabled state of the widget is saved to the state - - initial_label_enabled_state = True - - self.assertEqual(self.form.getWidgetState('label_field')[ - 'enabled'], initial_label_enabled_state) - - self.form.getWidget('label').setEnabled(False) - final_label_enabled_state = False - - self.assertEqual(self.form.getWidgetState('label_field')[ - 'enabled'], final_label_enabled_state) - - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") - def test_getWidgetState_returns_value_using_role_parameter_field(self): - # Check that the value of the QLabel is saved to the state - - initial_label_value = 'test label' - - self.assertEqual(self.form.getWidgetState( - 'label', 'field')['value'], initial_label_value) - - final_label_value = 'final test label' - self.form.getWidget('label').setText(final_label_value) - - self.assertEqual(self.form.getWidgetState( - 'label', 'field')['value'], final_label_value) - - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") - def test_getWidgetState_returns_value_using_role_parameter_label(self): - # Check that the value of the QLabel is saved to the state - - initial_label_value = 'test label' - - self.assertEqual(self.form.getWidgetState( - 'label', 'field')['value'], initial_label_value) - - final_label_value = 'final test label' - self.form.getWidget('label').setText(final_label_value) - - self.assertEqual(self.form.getWidgetState( - 'label', 'field')['value'], final_label_value) - - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") - def test_getWidgetState_returns_value_using_default_role_parameter(self): - # Check that the value of the QLabel is saved to the state - initial_label_value = 'test label' - - # In getWidgetState we do not specify if we want the 'field' or 'label' role, so it should default to 'field': - self.assertEqual(self.form.getWidgetState( - 'label')['value'], initial_label_value) - - final_label_value = 'final test label' - self.form.getWidget('label').setText(final_label_value) - - self.assertEqual(self.form.getWidgetState( - 'label')['value'], final_label_value) - - # Test value is saved for all widget types ---------------------------------------------------------------- - - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") - def test_getWidgetState_returns_QLabel_value(self): - # Check that the value of the QLabel is saved to the state - - initial_label_value = 'test label' - - self.assertEqual(self.form.getWidgetState( - 'label_field')['value'], initial_label_value) - - final_label_value = 'final test label' - self.form.getWidget('label').setText(final_label_value) - - self.assertEqual(self.form.getWidgetState( - 'label_field')['value'], final_label_value) - - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") - def test_getWidgetState_returns_QCheckBox_value(self): - # Check that the value of the QCheckBox is saved to the state - - initial_checkbox_value = False - - self.assertEqual(self.form.getWidgetState('checkBox_field')[ - 'value'], initial_checkbox_value) - - final_checkbox_value = True - self.form.getWidget('checkBox').setChecked(final_checkbox_value) - - self.assertEqual(self.form.getWidgetState( - 'checkBox_field')['value'], final_checkbox_value) - - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") - def test_getWidgetState_returns_QComboBox_value(self): - # Check that the value of the QComboBox is saved to the state - - combobox_list = ['test', 'test2'] - self.form.getWidget('comboBox').addItems(combobox_list) - - initial_combobox_value = 0 - - self.assertEqual(self.form.getWidgetState('comboBox_field')[ - 'value'], initial_combobox_value) - - final_combobox_value = 1 - self.form.getWidget('comboBox').setCurrentIndex(final_combobox_value) - - self.assertEqual(self.form.getWidgetState( - 'comboBox_field')['value'], final_combobox_value) - - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") - def test_getWidgetState_returns_QDoubleSpinBox_value(self): - # Check that the value of the QDoubleSpinBox is saved to the state - - initial_doubleSpinBox_value = 0.0 - - self.assertEqual(self.form.getWidgetState('doubleSpinBox_field')[ - 'value'], initial_doubleSpinBox_value) - - final_doubleSpinBox_value = 1.0 - self.form.getWidget('doubleSpinBox').setValue( - final_doubleSpinBox_value) - - self.assertEqual(self.form.getWidgetState('doubleSpinBox_field')[ - 'value'], final_doubleSpinBox_value) - - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") - def test_getWidgetState_returns_QSpinBox_value(self): - # Check that the value of the QSpinBox is saved to the state - - initial_spinBox_value = 0 - - self.assertEqual(self.form.getWidgetState('spinBox_field')[ - 'value'], initial_spinBox_value) - - final_spinBox_value = 1 - self.form.getWidget('spinBox').setValue(final_spinBox_value) - - self.assertEqual(self.form.getWidgetState( - 'spinBox_field')['value'], final_spinBox_value) - - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") - def test_getWidgetState_returns_QSlider_value(self): - # Check that the value of the QSlider is saved to the state - - initial_slider_value = 0 - - self.assertEqual(self.form.getWidgetState( - 'slider_field')['value'], initial_slider_value) - - final_slider_value = 1 - self.form.getWidget('slider').setValue(final_slider_value) - - self.assertEqual(self.form.getWidgetState( - 'slider_field')['value'], final_slider_value) - - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") - def test_getWidgetState_returns_UISliderWidget_value(self): - # Check that the value of the UISliderWidget is returned in the state - - initial_slider_value = 0 - - self.assertEqual(self.form.getWidgetState( - 'uiSliderWidget_field')['value'], initial_slider_value) - - final_slider_value = 1 - self.form.getWidget('uiSliderWidget').setValue(final_slider_value) - - self.assertEqual(self.form.getWidgetState( - 'uiSliderWidget_field')['value'], final_slider_value) - - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") - def test_getWidgetState_returns_QLineEdit_value(self): - # Check that the value of the QLineEdit is saved to the state - - initial_lineEdit_value = '' - self.form.getWidget('lineEdit').setText(initial_lineEdit_value) - - self.assertEqual(self.form.getWidgetState('lineEdit_field')[ - 'value'], initial_lineEdit_value) - - final_lineEdit_value = 'test' - self.form.getWidget('lineEdit').setText(final_lineEdit_value) - - self.assertEqual(self.form.getWidgetState( - 'lineEdit_field')['value'], final_lineEdit_value) - - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") - def test_getWidgetState_returns_QTextEdit_value(self): - # Check that the value of the QTextEdit is saved to the state - - initial_textEdit_value = '' - self.form.getWidget('textEdit').setText(initial_textEdit_value) - - self.assertEqual(self.form.getWidgetState('textEdit_field')[ - 'value'], initial_textEdit_value) - - final_textEdit_value = 'test' - self.form.getWidget('textEdit').setText(final_textEdit_value) - - self.assertEqual(self.form.getWidgetState( - 'textEdit_field')['value'], final_textEdit_value) - - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") - def test_getWidgetState_returns_QPlainTextEdit_value(self): - # Check that the value of the QPlainTextEdit is saved to the state - - initial_plainTextEdit_value = '' - self.form.getWidget('plainTextEdit').setPlainText( - initial_plainTextEdit_value) - - self.assertEqual(self.form.getWidgetState('plainTextEdit_field')[ - 'value'], initial_plainTextEdit_value) - - final_plainTextEdit_value = 'test' - self.form.getWidget('plainTextEdit').setPlainText( - final_plainTextEdit_value) - - self.assertEqual(self.form.getWidgetState('plainTextEdit_field')[ - 'value'], final_plainTextEdit_value) - - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") - def test_getWidgetState_returns_QPushButton_value(self): - # Check that the value of the QPushButton is saved to the state - - initial_button_value = False - - self.assertEqual(self.form.getWidgetState( - 'button_field')['value'], initial_button_value) - - final_button_value = True - self.form.getWidget('button').setCheckable(True) - self.form.getWidget('button').setChecked(final_button_value) - - self.assertEqual(self.form.getWidgetState( - 'button_field')['value'], final_button_value) - - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") - def test_getWidgetState_returns_QRadioButton_value(self): - # Check that the value of the QRadioButton is saved to the state - - initial_radio_value = False - - self.assertEqual(self.form.getWidgetState( - 'radioButton_field')['value'], initial_radio_value) - - final_radio_value = True - self.form.getWidget('radioButton').setChecked(final_radio_value) - - self.assertEqual(self.form.getWidgetState( - 'radioButton_field')['value'], final_radio_value) - - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") - def test_applyWidgetStates(self): - state_to_set = { - 'checkBox_field': {'value': True, 'enabled': False, 'visible': False}, - 'label_field': {'value': 'applyWidgetStates Test', 'enabled': True, 'visible': False} - } - - self.simple_form.applyWidgetStates(state_to_set) - - self.assertEqual(self.simple_form.getWidgetState( - 'checkBox_field'), state_to_set['checkBox_field']) - self.assertEqual(self.simple_form.getWidgetState( - 'label_field'), state_to_set['label_field']) - - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") - def test_applyWidgetState(self): - state_to_set = {'value': True, 'enabled': False, 'visible': False} - self.simple_form.applyWidgetState('checkBox_field', state_to_set) - self.assertEqual(self.simple_form.getWidgetState( - 'checkBox_field'), state_to_set) - - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") - def test_applyWidgetState_using_role_parameter_field(self): - state_to_set = {'value': True, 'enabled': False, 'visible': False} - self.simple_form.applyWidgetState( - 'checkBox', state_to_set, role='field') - self.assertEqual(self.simple_form.getWidgetState( - 'checkBox', 'field'), state_to_set) - - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") - def test_applyWidgetState_using_role_parameter_label(self): - state_to_set = {'value': 'test the checkbox:', - 'enabled': False, 'visible': False} - self.simple_form.applyWidgetState( - 'checkBox', state_to_set, role='label') - self.assertEqual(self.simple_form.getWidgetState( - 'checkBox', 'label'), state_to_set) - - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") - def test_applyWidgetState_using_role_parameter_default(self): - state_to_set = {'value': True, 'enabled': False, 'visible': False} - self.simple_form.applyWidgetState('checkBox', state_to_set) - self.assertEqual(self.simple_form.getWidgetState( - 'checkBox'), state_to_set) - - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") - def test_getAllWidgetStates(self): - # Check that the state of all widgets is returned - - expected_state = { - 'checkBox_field': {'value': False, 'enabled': True, 'visible': False}, - 'label_field': {'value': 'test label', 'enabled': True, 'visible': False}, - 'checkBox_label': {'value': 'CheckBox: ', 'enabled': True, 'visible': False}, - 'label_label': {'value': 'Label: ', 'enabled': True, 'visible': False} - } - - self.assertEqual(self.simple_form.getAllWidgetStates(), expected_state) - - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") - def test_saveAllWidgetStates(self): - # Check that the state of all widgets is saved to the state variable - - expected_state = { - 'checkBox_field': {'value': False, 'enabled': True, 'visible': False}, - 'label_field': {'value': 'test label', 'enabled': True, 'visible': False}, - 'checkBox_label': {'value': 'CheckBox: ', 'enabled': True, 'visible': False}, - 'label_label': {'value': 'Label: ', 'enabled': True, 'visible': False} - } - - self.simple_form.saveAllWidgetStates() - - self.assertEqual( - self.simple_form.widget().widget_states, expected_state) - - @unittest.skipIf(skip_test, "Can't test interfaces if we can't connect to the display") - def test_restoreAllSavedWidgetStates(self): - # Check that the state of all widgets is restored from the state variable - - state_to_restore = { - 'checkBox_field': {'value': True, 'enabled': False, 'visible': False}, - 'label_field': {'value': 'applyWidgetStates Test', 'enabled': True, 'visible': False}, - 'checkBox_label': {'value': 'CheckBox Test: ', 'enabled': True, 'visible': False}, - 'label_label': {'value': 'Label Test: ', 'enabled': True, 'visible': False} - } - - self.simple_form.widget().widget_states = state_to_restore - - self.simple_form.restoreAllSavedWidgetStates() + def test_restoreAllSavedWidgetStates(self): + """Check that the state of all widgets is restored from the state variable""" + state_to_restore = { + 'checkBox_field': {'value': True, 'enabled': False, 'visible': False}, + 'label_field': {'value': 'applyWidgetStates Test', 'enabled': True, 'visible': False}, + 'checkBox_label': {'value': 'CheckBox Test: ', 'enabled': True, 'visible': False}, + 'label_label': {'value': 'Label Test: ', 'enabled': True, 'visible': False} + } + self.simple_form.widget().widget_states = state_to_restore + self.simple_form.restoreAllSavedWidgetStates() self.assertEqual(self.simple_form.getWidget( 'checkBox').isChecked(), state_to_restore['checkBox_field']['value']) @@ -1255,7 +525,3 @@ def test_restoreAllSavedWidgetStates(self): 'label', 'label').isEnabled(), state_to_restore['label_label']['enabled']) self.assertEqual(self.simple_form.getWidget( 'label', 'label').isVisible(), state_to_restore['label_label']['visible']) - - -if __name__ == '__main__': - unittest.main() diff --git a/test/test_basic.py b/test/test_basic.py deleted file mode 100644 index 418fc31..0000000 --- a/test/test_basic.py +++ /dev/null @@ -1,16 +0,0 @@ -import unittest -import os - -# skip test of UI imports on GitHub actions -if os.environ.get('CONDA_BUILD', '0') != '1': - from dialog_example_2_test import MainUI, DialogTest - -class TestModuleBase(unittest.TestCase): - def test_version(self): - try: - from eqt import version as dversion - a = dversion.version - print ("version", a) - self.assertTrue(isinstance(a, str)) - except ImportError as ie: - self.assertFalse(True, str(ie)) diff --git a/test/test_io.py b/test/test_io.py index 398b9ef..101f9e9 100644 --- a/test/test_io.py +++ b/test/test_io.py @@ -1,47 +1,23 @@ from eqt.io import zip_directory -import unittest -import os import shutil - - -class TestZipDirectory(unittest.TestCase): - - def setUp(self): - ''' - Create a session zip file, which contains a session.json file - ''' - self.title = "title" - self.app_name = "app_name" - - self.folder = "Test Folder" - self.subfolder = os.path.join(self.folder, "Test Subfolder") - self.subfile = os.path.join(self.subfolder, "test_file.txt") - - os.mkdir(self.folder) - os.mkdir(self.subfolder) - - with open(self.subfile, "w+") as f: - f.write("test") - - def _test_zip_directory(self): - # Check the zip file exists: - assert os.path.exists(self.folder + ".zip") - # extract the zipfile and check the subfile exists: - shutil.unpack_archive(self.folder + ".zip", "extracted") - assert os.path.exists(os.path.join( - "extracted", "Test Subfolder", "test_file.txt")) - - def test_zip_directory_compress_True(self): - zip_directory(self.folder) - self._test_zip_directory() - - def test_zip_directory_compress_False(self): - zip_directory(self.folder, compress=False) - self._test_zip_directory() - - def tearDown(self): - shutil.rmtree(self.folder) - - -if __name__ == "__main__": - unittest.main() +from pytest import fixture +from pytest import mark + +@fixture +def test_file(tmp_path): + """Create a zip file, which contains a session.json file""" + subfile = tmp_path / "Test Subfolder" / "test_file.txt" + subfolder = subfile.parent + subfolder.mkdir(parents=True, exist_ok=True) + subfile.write_text("test") + return subfile + + +@mark.parametrize('compress', (True, False)) +def test_zip_directory_compress_False(test_file, compress): + folder = test_file.parent.parent + zip_directory(folder, compress=compress) + zipname = folder.with_suffix(".zip") + assert zipname.is_file() + shutil.unpack_archive(zipname, folder / "extracted") + assert (folder / "extracted" / test_file.parts[-2] / test_file.parts[-1]).is_file()