├── .coveragerc
├── .env.template
├── .github
└── workflows
│ ├── build_deploy.yml
│ └── test.yml
├── .gitignore
├── .pylintrc
├── .readthedocs.yaml
├── .vscode
├── launch.json
├── settings.json
└── tasks.json
├── CHANGELOG.md
├── LICENSE
├── MANIFEST.in
├── README.md
├── doc
├── _static
│ └── favicon.ico
├── autodoc
│ ├── autodoc_example.py
│ └── index.rst
├── basic_example.py
├── changelog.rst
├── conf.py
├── dev
│ ├── contribute.rst
│ ├── howto.rst
│ ├── index.rst
│ ├── platform.rst
│ ├── v2_to_v3.csv
│ └── v2_to_v3.rst
├── examples.rst
├── images
│ ├── basic_example.png
│ ├── guidata-banner.png
│ ├── guidata-vertical.png
│ ├── layout_example.png
│ └── screenshots
│ │ ├── __init__.png
│ │ ├── activable_dataset.png
│ │ ├── all_features.png
│ │ ├── all_items.png
│ │ ├── arrayeditor.png
│ │ ├── bool_selector.png
│ │ ├── codeeditor.png
│ │ ├── collectioneditor.png
│ │ ├── console.png
│ │ ├── dataframeeditor.png
│ │ ├── datasetgroup.png
│ │ ├── editgroupbox.png
│ │ └── importwizard.png
├── index.rst
├── installation.rst
├── overview.rst
├── reference
│ ├── dataset
│ │ ├── conv.rst
│ │ ├── dataitems.rst
│ │ ├── datatypes.rst
│ │ ├── index.rst
│ │ ├── io.rst
│ │ └── qtwidgets.rst
│ ├── guitest.rst
│ ├── index.rst
│ ├── userconfig.rst
│ ├── utils.rst
│ └── widgets.rst
├── requirements.rst
├── update_requirements.py
└── widgets.rst
├── guidata-tests.desktop
├── guidata
├── __init__.py
├── config.py
├── configtools.py
├── data
│ └── icons
│ │ ├── apply.png
│ │ ├── arredit.png
│ │ ├── busy.png
│ │ ├── cell_edit.png
│ │ ├── copy.png
│ │ ├── copy_all.svg
│ │ ├── delete.png
│ │ ├── dictedit.png
│ │ ├── dtype.png
│ │ ├── edit.png
│ │ ├── editors
│ │ ├── copywop.png
│ │ ├── edit.png
│ │ ├── edit_add.png
│ │ ├── editclear.png
│ │ ├── editcopy.png
│ │ ├── editcut.png
│ │ ├── editdelete.png
│ │ ├── editpaste.png
│ │ ├── fileimport.png
│ │ ├── filesave.png
│ │ ├── imshow.png
│ │ ├── insert.png
│ │ ├── plot.png
│ │ ├── rename.png
│ │ └── selectall.png
│ │ ├── exit.png
│ │ ├── expander_down.png
│ │ ├── expander_right.png
│ │ ├── export.svg
│ │ ├── file.png
│ │ ├── fileclose.png
│ │ ├── fileimport.png
│ │ ├── filenew.png
│ │ ├── fileopen.png
│ │ ├── filesave.png
│ │ ├── filesaveas.png
│ │ ├── filetypes
│ │ ├── doc.png
│ │ ├── gif.png
│ │ ├── html.png
│ │ ├── jpg.png
│ │ ├── pdf.png
│ │ ├── png.png
│ │ ├── pps.png
│ │ ├── ps.png
│ │ ├── tar.png
│ │ ├── tgz.png
│ │ ├── tif.png
│ │ ├── txt.png
│ │ ├── xls.png
│ │ └── zip.png
│ │ ├── format.svg
│ │ ├── guidata-banner.svg
│ │ ├── guidata-vertical.svg
│ │ ├── guidata.svg
│ │ ├── hist.png
│ │ ├── max.png
│ │ ├── min.png
│ │ ├── none.png
│ │ ├── not_found.png
│ │ ├── python.png
│ │ ├── quickview.png
│ │ ├── resize.svg
│ │ ├── save_all.png
│ │ ├── selection.png
│ │ ├── settings.png
│ │ ├── shape.png
│ │ ├── xmax.png
│ │ └── xmin.png
├── dataset
│ ├── __init__.py
│ ├── autodoc.py
│ ├── conv.py
│ ├── dataitems.py
│ ├── datatypes.py
│ ├── io.py
│ ├── note_directive.py
│ ├── qtitemwidgets.py
│ ├── qtwidgets.py
│ └── textedit.py
├── env.py
├── external
│ ├── __init__.py
│ └── darkdetect
│ │ ├── __init__.py
│ │ ├── _dummy.py
│ │ ├── _linux_detect.py
│ │ ├── _mac_detect.py
│ │ └── _windows_detect.py
├── guitest.py
├── io
│ ├── __init__.py
│ ├── base.py
│ ├── h5fmt.py
│ ├── inifmt.py
│ └── jsonfmt.py
├── locale
│ └── fr
│ │ └── LC_MESSAGES
│ │ └── guidata.po
├── qthelpers.py
├── tests
│ ├── __init__.py
│ ├── conftest.py
│ ├── data
│ │ └── genreqs
│ │ │ ├── pyproject.toml
│ │ │ ├── requirements.rst
│ │ │ └── setup.cfg
│ ├── dataset
│ │ ├── __init__.py
│ │ ├── test_activable_dataset.py
│ │ ├── test_activable_items.py
│ │ ├── test_all_features.py
│ │ ├── test_all_items.py
│ │ ├── test_all_items_readonly.py
│ │ ├── test_bool_selector.py
│ │ ├── test_callbacks.py
│ │ ├── test_datasetgroup.py
│ │ ├── test_editgroupbox.py
│ │ ├── test_inheritance.py
│ │ ├── test_item_order.py
│ │ ├── test_loadsave_hdf5.py
│ │ ├── test_loadsave_json.py
│ │ └── test_rotatedlabel.py
│ ├── unit
│ │ ├── __init__.py
│ │ ├── test_config.py
│ │ ├── test_data.py
│ │ ├── test_dataset_from_dict.py
│ │ ├── test_dataset_from_func.py
│ │ ├── test_genreqs.py
│ │ ├── test_h5fmt.py
│ │ ├── test_jsonfmt.py
│ │ ├── test_no_qt.py
│ │ ├── test_text.py
│ │ ├── test_translations.py
│ │ ├── test_updaterestoredataset.py
│ │ └── test_userconfig_app.py
│ └── widgets
│ │ ├── __init__.py
│ │ ├── test_arrayeditor.py
│ │ ├── test_arrayeditor_unit.py
│ │ ├── test_codeeditor.py
│ │ ├── test_collectionseditor.py
│ │ ├── test_console.py
│ │ ├── test_dataframeeditor.py
│ │ ├── test_importwizard.py
│ │ ├── test_objecteditor.py
│ │ └── test_theme.py
├── userconfig.py
├── utils
│ ├── __init__.py
│ ├── encoding.py
│ ├── genreqs.py
│ ├── gettext_helpers.py
│ └── misc.py
└── widgets
│ ├── __init__.py
│ ├── about.py
│ ├── arrayeditor
│ ├── __init__.py
│ ├── arrayeditor.py
│ ├── arrayhandler.py
│ ├── datamodel.py
│ ├── editorwidget.py
│ └── utils.py
│ ├── codeeditor.py
│ ├── collectionseditor.py
│ ├── console
│ ├── __init__.py
│ ├── base.py
│ ├── calltip.py
│ ├── dochelpers.py
│ ├── internalshell.py
│ ├── interpreter.py
│ ├── mixins.py
│ ├── shell.py
│ └── terminal.py
│ ├── dataframeeditor.py
│ ├── dockable.py
│ ├── importwizard.py
│ ├── nsview.py
│ ├── objecteditor.py
│ ├── rotatedlabel.py
│ ├── syntaxhighlighters.py
│ └── texteditor.py
├── pyproject.toml
├── requirements.txt
└── scripts
├── build_dist.bat
├── build_doc.bat
├── clean_up.bat
├── gettext.bat
├── run_coverage.bat
├── run_pylint.bat
├── run_pytest.bat
├── run_ruff.bat
├── run_test_launcher.bat
├── upgrade_env.bat
└── utils.bat
/.coveragerc:
--------------------------------------------------------------------------------
1 | [run]
2 | parallel = True
3 | concurrency = multiprocessing,thread
4 | omit =
5 | */guidata/tests/*
6 | */guidata/external/*
7 | */guidata/widgets/*
8 |
9 | [report]
10 | exclude_lines =
11 | if __name__ == .__main__.:
12 | if TYPE_CHECKING:
--------------------------------------------------------------------------------
/.env.template:
--------------------------------------------------------------------------------
1 | PYTHONPATH=.
--------------------------------------------------------------------------------
/.github/workflows/build_deploy.yml:
--------------------------------------------------------------------------------
1 | name: Build and upload to PyPI
2 |
3 | on:
4 | release:
5 | types: [published]
6 |
7 | permissions:
8 | contents: read
9 |
10 | jobs:
11 | deploy:
12 |
13 | runs-on: ubuntu-latest
14 |
15 | steps:
16 | - uses: actions/checkout@v4
17 | - name: Set up Python
18 | uses: actions/setup-python@v5
19 | with:
20 | python-version: '3.x'
21 | - name: Install dependencies
22 | run: |
23 | python -m pip install --upgrade pip
24 | pip install build
25 | - name: Build package
26 | run: python -m build
27 | - name: Publish package
28 | uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29
29 | with:
30 | user: __token__
31 | password: ${{ secrets.PYPI_API_TOKEN }}
32 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | # This workflow will install Python dependencies, run tests and lint with a variety of Python versions
2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python
3 |
4 | # Inspired from https://pytest-qt.readthedocs.io/en/latest/troubleshooting.html#github-actions
5 |
6 | name: Build, install and test
7 |
8 | on:
9 | push:
10 | branches: [ "master" ]
11 | pull_request:
12 | branches: [ "master" ]
13 |
14 | jobs:
15 | build:
16 |
17 | env:
18 | DISPLAY: ':99.0'
19 |
20 | runs-on: ubuntu-latest
21 | strategy:
22 | fail-fast: false
23 | matrix:
24 | python-version: ["3.9", "3.11", "3.13"]
25 |
26 | steps:
27 | - uses: actions/checkout@v4
28 | - name: Set up Python ${{ matrix.python-version }}
29 | uses: actions/setup-python@v5
30 | with:
31 | python-version: ${{ matrix.python-version }}
32 | - name: Install dependencies
33 | run: |
34 | sudo apt install libxkbcommon-x11-0 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-xinerama0 libxcb-xfixes0 x11-utils
35 | /sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -screen 0 1920x1200x24 -ac +extension GLX
36 | python -m pip install --upgrade pip
37 | python -m pip install ruff pytest
38 | pip install PyQt5
39 | pip install .
40 | - name: Lint with Ruff
41 | run: ruff check --output-format=github guidata
42 | - name: Test with pytest
43 | run: |
44 | pytest
45 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .spyderproject
2 | .spyproject
3 | doc.zip
4 | Thumbs.db
5 | doctmp/
6 | *.h5
7 | *.stats
8 | test.json
9 | doc/install_requires.txt
10 | doc/extras_require-dev.txt
11 | doc/extras_require-doc.txt
12 | *.bak
13 |
14 | # Visual Studio Code
15 | .env
16 | .venv
17 |
18 | # Created by https://www.gitignore.io/api/python
19 |
20 | ### Python ###
21 | # Byte-compiled / optimized / DLL files
22 | __pycache__/
23 | *.py[cod]
24 | *$py.class
25 |
26 | # C extensions
27 | *.so
28 |
29 | # Distribution / packaging
30 | .Python
31 | env/
32 | build/
33 | develop-eggs/
34 | dist/
35 | downloads/
36 | eggs/
37 | .eggs/
38 | lib/
39 | lib64/
40 | parts/
41 | sdist/
42 | var/
43 | *.egg-info/
44 | .installed.cfg
45 | *.egg
46 |
47 | # PyInstaller
48 | # Usually these files are written by a python script from a template
49 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
50 | *.manifest
51 | *.spec
52 |
53 | # Installer logs
54 | pip-log.txt
55 | pip-delete-this-directory.txt
56 |
57 | # Unit test / coverage reports
58 | .hypothesis/
59 | htmlcov/
60 | .tox/
61 | .coverage
62 | .coverage.*
63 | .cache
64 | nosetests.xml
65 | coverage.xml
66 | *,cover
67 |
68 | # Translations
69 | *.mo
70 | *.pot
71 |
72 | # Django stuff:
73 | *.log
74 |
75 | # Sphinx documentation
76 | docs/_build/
77 | *.chm
78 |
79 | # PyBuilder
80 | target/
81 |
--------------------------------------------------------------------------------
/.pylintrc:
--------------------------------------------------------------------------------
1 | [FORMAT]
2 | # Essential to be able to compare code side-by-side (`black` default setting)
3 | # and best compromise to minimize file size
4 | max-line-length=88
5 |
6 | [TYPECHECK]
7 | ignored-modules=qtpy.QtWidgets,qtpy.QtCore,qtpy.QtGui
8 |
9 | [MESSAGES CONTROL]
10 | disable=wrong-import-order
11 |
12 | [DESIGN]
13 | max-args=8 # default: 5
14 | max-attributes=12 # default: 7
15 | max-branches=17 # default: 12
16 | max-locals=20 # default: 15
17 | min-public-methods=0 # default: 2
18 | max-public-methods=25 # default: 20
--------------------------------------------------------------------------------
/.readthedocs.yaml:
--------------------------------------------------------------------------------
1 | # Read the Docs configuration file for Sphinx projects
2 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
3 |
4 | version: 2
5 | build:
6 | os: ubuntu-22.04
7 | tools:
8 | python: "3.11"
9 | sphinx:
10 | configuration: doc/conf.py
11 | formats:
12 | - pdf
13 | python:
14 | install:
15 | - method: pip
16 | path: .
17 | extra_requirements:
18 | - doc
19 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "name": "Run Test Launcher",
9 | "type": "debugpy",
10 | "request": "launch",
11 | "program": "${workspaceFolder}/guidata/tests/__init__.py",
12 | "console": "integratedTerminal",
13 | "envFile": "${workspaceFolder}/.env",
14 | "python": "${config:python.defaultInterpreterPath}",
15 | "justMyCode": true,
16 | "env": {
17 | "QT_COLOR_MODE": "light",
18 | }
19 | },
20 | {
21 | "name": "Run current file",
22 | "type": "debugpy",
23 | "request": "launch",
24 | "program": "${file}",
25 | "console": "integratedTerminal",
26 | "envFile": "${workspaceFolder}/.env",
27 | "python": "${config:python.defaultInterpreterPath}",
28 | "pythonArgs": [
29 | "-W error::DeprecationWarning",
30 | "-W error::RuntimeWarning",
31 | ],
32 | "justMyCode": false,
33 | "args": [],
34 | "env": {}
35 | },
36 | {
37 | "name": "Run current file (unattended)",
38 | "type": "debugpy",
39 | "request": "launch",
40 | "program": "${file}",
41 | "console": "integratedTerminal",
42 | "envFile": "${workspaceFolder}/.env",
43 | "python": "${config:python.defaultInterpreterPath}",
44 | "justMyCode": false,
45 | "args": [
46 | "--unattended"
47 | ],
48 | "env": {
49 | "GUIDATA_PARSE_ARGS": "1",
50 | }
51 | },
52 | ]
53 | }
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "[bat]": {
3 | "files.encoding": "cp850",
4 | },
5 | "editor.rulers": [
6 | 88
7 | ],
8 | "files.exclude": {
9 | "**/__pycache__": true,
10 | "**/.pytest_cache": true,
11 | "**/.hypothesis": true,
12 | "**/*.pyc": true,
13 | "**/*.pyo": true,
14 | "**/*.pyd": true,
15 | ".venv": true
16 | },
17 | "files.trimFinalNewlines": true,
18 | "files.trimTrailingWhitespace": true,
19 | "python.defaultInterpreterPath": "${env:PPSTACK_PYTHONEXE}",
20 | "editor.formatOnSave": true,
21 | "python.analysis.autoFormatStrings": true,
22 | "python.testing.unittestEnabled": false,
23 | "python.testing.pytestEnabled": true,
24 | "python.testing.pytestPath": "pytest",
25 | "python.testing.pytestArgs": [],
26 | "[python]": {
27 | "editor.defaultFormatter": "charliermarsh.ruff"
28 | },
29 | "editor.codeActionsOnSave": {
30 | "source.organizeImports": "explicit",
31 | },
32 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | BSD 3-Clause License
2 |
3 | Copyright (c) 2023, CEA-Codra, Pierre Raybaut.
4 | All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 |
12 | 2. Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | 3. Neither the name of the copyright holder nor the names of its
17 | contributors may be used to endorse or promote products derived from
18 | this software without specific prior written permission.
19 |
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | graft doc
2 | include *.desktop
3 | include CHANGELOG.md
4 | include requirements.txt
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # guidata: Automatic GUI generation for easy dataset editing and display with Python
2 |
3 | [](https://pypi.org/project/guidata/)
4 | [](https://github.com/PlotPyStack/guidata/)
5 | [](https://pypi.python.org/pypi/guidata/)
6 | [](https://www.anaconda.com/download/)
7 |
8 | ℹ️ Created in 2009 by [Pierre Raybaut](https://github.com/PierreRaybaut) and maintained by the [PlotPyStack](https://github.com/PlotPyStack) organization.
9 |
10 | ## Overview
11 |
12 | The `guidata` package is a Python library generating Qt graphical user interfaces.
13 | It is part of the [PlotPyStack](https://github.com/PlotPyStack) project, aiming at
14 | providing a unified framework for creating scientific GUIs with Python and Qt.
15 |
16 | Simple example of `guidata` datasets embedded in an application window:
17 |
18 | 
19 |
20 | See [documentation](https://guidata.readthedocs.io/en/latest/) for more details on
21 | the library and [changelog](https://github.com/PlotPyStack/guidata/blob/master/CHANGELOG.md) for recent history of changes.
22 |
23 | Copyrights and licensing:
24 |
25 | * Copyright © 2023 [CEA](https://www.cea.fr), [Codra](https://codra.net/), [Pierre Raybaut](https://github.com/PierreRaybaut).
26 | * Licensed under the terms of the BSD 3-Clause (see [LICENSE](https://github.com/PlotPyStack/guidata/blob/master/LICENSE)).
27 |
28 | ## Features
29 |
30 | Based on the Qt library, `guidata` is a Python library generating graphical user
31 | interfaces for easy dataset editing and display. It also provides helpers and
32 | application development tools for Qt (PyQt5, PySide2, PyQt6, PySide6).
33 |
34 | Generate GUIs to edit and display all kind of objects regrouped in datasets:
35 |
36 | * Integers, floats, strings
37 | * Lists (single/multiple choices)
38 | * Dictionaries
39 | * `ndarrays` (NumPy's N-dimensional arrays)
40 | * Etc.
41 |
42 | Save and load datasets to/from HDF5, JSON or INI files.
43 |
44 | Application development tools:
45 |
46 | * Data model (internal data structure, serialization, etc.)
47 | * Configuration management
48 | * Internationalization (`gettext`)
49 | * Deployment tools
50 | * HDF5, JSON and INI I/O helpers
51 | * Qt helpers
52 | * Ready-to-use Qt widgets: Python console, source code editor, array editor, etc.
53 |
54 | ## Dependencies and installation
55 |
56 | ### Supported Qt versions and bindings
57 |
58 | The whole PlotPyStack set of libraries relies on the [Qt](https://doc.qt.io/) GUI toolkit, thanks to [QtPy](https://pypi.org/project/QtPy/), an abstraction layer which allows to use the same API to interact with different Python-to-Qt bindings (PyQt5, PyQt6, PySide2, PySide6).
59 |
60 | Compatibility table:
61 |
62 | | guidata version | PyQt5 | PyQt6 | PySide2 | PySide6 |
63 | |----------------|-------|-------|---------|---------|
64 | | 3.0-3.5 | ✅ | ⚠️ | ❌ | ⚠️ |
65 | | Latest | ✅ | ✅ | ❌ | ✅ |
66 |
67 | ### Other dependencies and installation
68 |
69 | See [Installation](https://guidata.readthedocs.io/en/latest/installation.html)
70 | section in the documentation for more details.
71 |
--------------------------------------------------------------------------------
/doc/_static/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/doc/_static/favicon.ico
--------------------------------------------------------------------------------
/doc/autodoc/autodoc_example.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | import atexit
4 | import datetime
5 | import shutil
6 | import tempfile
7 |
8 | import guidata.dataset as gds
9 |
10 | # Creating temporary files and registering cleanup functions
11 | TEMPDIR = tempfile.mkdtemp(prefix="test_")
12 | atexit.register(shutil.rmtree, TEMPDIR)
13 | FILE_ETA = tempfile.NamedTemporaryFile(suffix=".eta", dir=TEMPDIR)
14 | atexit.register(FILE_ETA.close)
15 | FILE_CSV = tempfile.NamedTemporaryFile(suffix=".csv", dir=TEMPDIR)
16 | atexit.register(FILE_CSV.close)
17 |
18 |
19 | class AutodocExampleParam1(gds.DataSet):
20 | """Example of a complete dataset with all possible items. Used as an autodoc
21 | example."""
22 |
23 | dir = gds.DirectoryItem("Directory", TEMPDIR)
24 | a = gds.FloatItem("Parameter #1", default=2.3)
25 | b = gds.IntItem("Parameter #2", min=0, max=10, default=5)
26 | c = gds.StringItem("Parameter #3", default="default value")
27 | type = gds.ChoiceItem("Processing algorithm", ("type 1", "type 2", "type 3"))
28 | fname = gds.FileOpenItem("Open file", ("csv", "eta"), FILE_CSV.name)
29 | fnames = gds.FilesOpenItem("Open files", "csv", FILE_CSV.name)
30 | fname_s = gds.FileSaveItem("Save file", "eta", FILE_ETA.name)
31 | string = gds.StringItem("String")
32 | text = gds.TextItem("Text")
33 | float_slider = gds.FloatItem(
34 | "Float (with slider)", default=0.5, min=0, max=1, step=0.01, slider=True
35 | )
36 | integer = gds.IntItem("Integer", default=5, min=3, max=16, slider=True).set_pos(
37 | col=1
38 | )
39 | dtime = gds.DateTimeItem("Date/time", default=datetime.datetime(2010, 10, 10))
40 | date = gds.DateItem("Date", default=datetime.date(2010, 10, 10)).set_pos(col=1)
41 | bool1 = gds.BoolItem("Boolean option without label")
42 | bool2 = gds.BoolItem("Boolean option with label", "Label")
43 | _bg = gds.BeginGroup("A sub group")
44 | color = gds.ColorItem("Color", default="red")
45 | choice = gds.ChoiceItem(
46 | "Single choice 1",
47 | [
48 | ("16", "first choice"),
49 | ("32", "second choice"),
50 | ("64", "third choice"),
51 | (128, "fourth choice"),
52 | ],
53 | )
54 | mchoice2 = gds.ImageChoiceItem(
55 | "Single choice 2",
56 | [
57 | ("rect", "first choice", "gif.png"),
58 | ("ell", "second choice", "txt.png"),
59 | ("qcq", "third choice", "file.png"),
60 | ],
61 | )
62 | _eg = gds.EndGroup("A sub group")
63 | floatarray = gds.FloatArrayItem("Float array", format=" %.2e ").set_pos(col=1)
64 | mchoice3 = gds.MultipleChoiceItem(
65 | "MC type 1", [str(i) for i in range(12)]
66 | ).horizontal(4)
67 | mchoice1 = (
68 | gds.MultipleChoiceItem(
69 | "MC type 2", ["first choice", "second choice", "third choice"]
70 | )
71 | .vertical(1)
72 | .set_pos(col=1)
73 | )
74 | dictionary = gds.DictItem(
75 | "Dictionary",
76 | help="This is a dictionary",
77 | )
78 |
79 | def doc_test(self, a: int, b: float, c: str) -> str:
80 | """Test method for autodoc.
81 |
82 | Args:
83 | a: first parameter.
84 | b: second parameter.
85 | c: third parameter.
86 |
87 | Returns:
88 | Concatenation of c and (a + b).
89 | """
90 | return c + str(a + b)
91 |
92 |
93 | class AutodocExampleParam2(AutodocExampleParam1):
94 | pass
95 |
--------------------------------------------------------------------------------
/doc/autodoc/index.rst:
--------------------------------------------------------------------------------
1 | :tocdepth: 3
2 |
3 | Sphinx autodoc extension
4 | ========================
5 |
6 | Extension
7 | ---------
8 |
9 | The :mod:`guidata` library provides a Sphinx extension to automatically document
10 | data set classes (:py:class:`guidata.dataset.datatypes.DataSet`).
11 | This extension is based on the Sphinx autodoc extension.
12 |
13 | Three directives are provided:
14 |
15 | * :code:`.. autodataset_create:: [module.dataset].create` to document the
16 | :code:`create()` classmethod of a :code:`DataSet` using its :code:`DataItem`.
17 | * :code:`.. datasetnote:: [module.dataset] [n]` to display a note on how to
18 | instanciate a dataset. Optional parameter :code:`n` gives the number of items
19 | to show.
20 | * :code:`.. autodataset:: [module.dataset]` used to document a dataset class.
21 | It is derived from the :code:`.. autoclass::` directive and therefore has the
22 | same options. By default, it will document a dataset without its constructor
23 | signature but will document its attributes and the :code:`create()` class method
24 | using the :code:`autodataset_create` directive. Several additional options are
25 | available to more finely tune the documentation (see examples below).
26 |
27 | Example dataset
28 | ---------------
29 |
30 | .. literalinclude:: autodoc_example.py
31 |
32 | Generated documentation
33 | -----------------------
34 |
35 | Basic usage
36 | ~~~~~~~~~~~
37 |
38 | In most cases, the :code:`.. autodataset::` directive should be sufficient to document
39 | a dataset. However, it might be useful to display examples on how to instanciate the
40 | given dataset. This can be done using the :code:`:shownote:` option (or the
41 | :code:`.. datasetnote::` directive).
42 |
43 | .. code-block:: rst
44 |
45 | .. autodataset:: autodoc_example.AutodocExampleParam1
46 |
47 | .. autodataset:: autodoc_example.AutodocExampleParam1
48 | :shownote:
49 |
50 |
51 | The second example line would result in the following documentation:
52 |
53 | .. autodataset:: autodoc_example.AutodocExampleParam1
54 | :shownote:
55 |
56 | Advanced usage
57 | ~~~~~~~~~~~~~~
58 |
59 | The :code:`.. autodataset::` directive behavior can be modified using all
60 | :code:`.. autoclass::` options, as well as the the following ones:
61 |
62 | * :code:`:showsig:` to show the constructor signature
63 | * :code:`:hideattr:` to hide the dataset attributes
64 | * :code:`:shownote: [n]` to add a note on how to instanciate the dataset with the
65 | first :code:`n` items. If :code:`n` is not provided, all items will be shown.
66 | * :code:`:hidecreate:` to hide the :code:`create()` method documentation which is
67 | shown by default.
68 |
69 | The following reST example shows how these options can be used.
70 |
71 | .. code-block:: rst
72 |
73 | .. autodataset:: autodoc_example.AutodocExampleParam2
74 | :showsig:
75 | :hideattr:
76 | :hidecreate:
77 | :shownote: 5
78 | :members:
79 |
80 | .. autodataset:: autodoc_example.AutodocExampleParam2
81 | :showsig:
82 | :hideattr:
83 | :hidecreate:
84 | :shownote: 5
85 | :members:
--------------------------------------------------------------------------------
/doc/basic_example.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | import guidata
4 | import guidata.dataset as gds
5 |
6 | # Note: the following line is not required if a QApplication has already been created
7 | _app = guidata.qapplication()
8 |
9 |
10 | class Processing(gds.DataSet):
11 | """Example"""
12 |
13 | a = gds.FloatItem("Parameter #1", default=2.3)
14 | b = gds.IntItem("Parameter #2", min=0, max=10, default=5)
15 | type = gds.ChoiceItem("Processing algorithm", ("type 1", "type 2", "type 3"))
16 |
17 |
18 | param = Processing()
19 | param.edit()
20 | print(param) # Showing param contents
21 | param.b = 4 # Modifying item value
22 | param.view()
23 |
24 | # Alternative way for creating a DataSet instance:
25 | param = Processing.create(a=7.323, b=4)
26 | print(param)
27 |
--------------------------------------------------------------------------------
/doc/changelog.rst:
--------------------------------------------------------------------------------
1 | .. include:: ../CHANGELOG.md
2 | :parser: myst_parser.sphinx_
3 |
--------------------------------------------------------------------------------
/doc/conf.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | import os
4 | import sys
5 |
6 | sys.path.insert(0, os.path.abspath(".."))
7 | sys.path.insert(0, os.path.abspath("autodoc"))
8 |
9 | import guidata # noqa: E402
10 |
11 | creator = "Pierre Raybaut"
12 | project = "guidata"
13 | copyright = "2009 CEA, " + creator
14 | version = ".".join(guidata.__version__.split(".")[:2])
15 | release = guidata.__version__
16 |
17 | extensions = [
18 | "sphinx.ext.autodoc",
19 | "myst_parser",
20 | "sphinx.ext.intersphinx",
21 | "sphinx.ext.doctest",
22 | "sphinx_copybutton",
23 | "sphinx.ext.napoleon",
24 | "sphinx_qt_documentation",
25 | "guidata.dataset.autodoc",
26 | ]
27 | if "htmlhelp" in sys.argv:
28 | extensions += ["sphinx.ext.imgmath"]
29 | else:
30 | extensions += ["sphinx.ext.mathjax"]
31 | templates_path = ["_templates"]
32 | source_suffix = ".rst"
33 | master_doc = "index"
34 |
35 | exclude_trees = []
36 | pygments_style = "sphinx"
37 | modindex_common_prefix = ["guidata."]
38 | autodoc_member_order = "bysource"
39 |
40 | intersphinx_mapping = {
41 | "python": ("https://docs.python.org/3", None),
42 | "numpy": ("https://numpy.org/doc/stable/", None),
43 | "h5py": ("https://docs.h5py.org/en/stable/", None),
44 | }
45 | # nitpicky = True # Uncomment to warn about all broken links
46 |
47 | if "htmlhelp" in sys.argv:
48 | html_theme = "classic"
49 | else:
50 | html_theme = "python_docs_theme"
51 | html_title = "%s %s Manual" % (project, version)
52 | html_short_title = "%s Manual" % project
53 | html_logo = "images/guidata-vertical.png"
54 | html_favicon = "_static/favicon.ico"
55 | html_static_path = ["_static"]
56 | html_use_modindex = True
57 | htmlhelp_basename = "guidata"
58 | latex_documents = [
59 | ("index", "guidata.tex", "guidata Manual", creator, "manual"),
60 | ]
61 |
--------------------------------------------------------------------------------
/doc/dev/contribute.rst:
--------------------------------------------------------------------------------
1 | How to contribute
2 | -----------------
3 |
4 | Coding guidelines
5 | ^^^^^^^^^^^^^^^^^
6 |
7 | In general, we try to follow the standard Python coding guidelines, which cover
8 | all the important coding aspects (docstrings, comments, naming conventions,
9 | import statements, ...) as described here:
10 |
11 | * `Style Guide for Python Code `_
12 |
13 | The easiest way to check that your code is following those guidelines is to
14 | run `pylint` (a note greater than 9/10 seems to be a reasonable goal).
15 |
16 | Moreover, the following guidelines should be followed:
17 |
18 | * Write docstrings for all classes, methods and functions. The docstrings
19 | should follow the `Google style `_.
20 |
21 | * Add typing annotations for all functions and methods. The annotations should
22 | use the future syntax ``from __future__ import annotations`` (see
23 | `PEP 563 `_)
24 | and the ``if TYPE_CHECKING`` pattern to avoid circular imports (see
25 | `PEP 484 `_).
26 |
27 | .. note::
28 |
29 | To ensure that types are properly referenced by ``sphinx`` in the
30 | documentation, you may need to import the individual types for Qt
31 | (e.g. ``from qtpy.QtCore import QRectF``) instead of importing the whole
32 | module (e.g. ``from qtpy import QtCore as QC``): this is a limitation of
33 | ``sphinx-qt-documentation`` extension.
34 |
35 | * Try to keep the code as simple as possible. If you have to write a complex
36 | piece of code, try to split it into several functions or classes.
37 |
38 | * Add as many comments as possible. The code should be self-explanatory, but
39 | it is always useful to add some comments to explain the general idea of the
40 | code, or to explain some tricky parts.
41 |
42 | * Do not use ``from module import *`` statements, even in the ``__init__``
43 | module of a package.
44 |
45 | * Avoid using mixins (multiple inheritance) when possible. It is often
46 | possible to use composition instead of inheritance.
47 |
48 | * Avoid using ``__getattr__`` and ``__setattr__`` methods. They are often used
49 | to implement lazy initialization, but this can be done in a more explicit
50 | way.
51 |
52 |
53 | Submitting patches
54 | ^^^^^^^^^^^^^^^^^^
55 |
56 | Check-list
57 | ~~~~~~~~~~
58 |
59 | Before submitting a patch, please check the following points:
60 |
61 | * The code follows the coding guidelines described above.
62 |
63 | * Build the documentation and check that it is correctly generated. *No warning
64 | should be displayed.*
65 |
66 | * Run pylint on the code and check that there is no error:
67 |
68 | .. code-block:: bash
69 |
70 | pylint --disable=fixme,C,R,W guidata
71 |
72 | * Run the tests and check that they all pass:
73 |
74 | .. code-block:: bash
75 |
76 | pytest
77 |
78 | Pull request
79 | ~~~~~~~~~~~~
80 |
81 | If you want to contribute to the project, you can submit a patch. The
82 | recommended way to do this is to fork the project on GitHub, create a branch
83 | for your modifications and then send a pull request. The pull request will be
84 | reviewed and merged if it is accepted.
85 |
86 | Setting up development environment
87 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
88 |
89 | If you want to contribute to the project, you will probably want to set up a
90 | development environment. The easiest way to do this is to use `virtualenv
91 | `_ and `pip
92 | `_.
93 |
94 | Some parameters are required to be set before using the project from within
95 | Visual Studio Code (used in `launch.json` and `tasks.json`). These are:
96 |
97 | * ``PPSTACK_PYTHONEXE``: The path to the Python interpreter to use. This is
98 | used to launch the application from within Visual Studio Code. If not set,
99 | the default Python interpreter will be used.
100 |
101 | * `.env` file: This file is used to set environment variables for the
102 | application. It is used to set the ``PYTHONPATH`` environment variable to
103 | the root of the project. This is required to be able to import the project
104 | modules from within Visual Studio Code. To create this file, copy the
105 | ``.env.template`` file to ``.env`` (and eventually add your own paths).
106 |
--------------------------------------------------------------------------------
/doc/dev/howto.rst:
--------------------------------------------------------------------------------
1 | How to build, test and deploy
2 | -----------------------------
3 |
4 | Build instructions
5 | ^^^^^^^^^^^^^^^^^^
6 |
7 | To build the package, you need to run the following command::
8 |
9 | python -m build
10 |
11 | It should generate a source package (``.tar.gz`` file) and a Wheel package
12 | (``.whl`` file) in the `dist` directory.
13 |
14 |
15 | Running unittests
16 | ^^^^^^^^^^^^^^^^^
17 |
18 | To run the unittests, you need:
19 |
20 | * Python
21 | * pytest
22 | * coverage (optional)
23 |
24 | Then run the following command::
25 |
26 | pytest
27 |
28 | To run test with coverage support, use the following command::
29 |
30 | pytest -v --cov --cov-report=html guidata
31 |
32 |
33 | Code formatting
34 | ^^^^^^^^^^^^^^^
35 |
36 | The code is formatted with `ruff `_.
37 |
38 | If you are using `Visual Studio Code `_,
39 | the formatting is done automatically when you save a file, thanks to the
40 | project settings in the `.vscode` directory.
41 |
--------------------------------------------------------------------------------
/doc/dev/index.rst:
--------------------------------------------------------------------------------
1 | Development
2 | ===========
3 |
4 | .. toctree::
5 | :maxdepth: 2
6 | :caption: Contents:
7 |
8 | contribute
9 | howto
10 | v2_to_v3
11 | platform
12 |
--------------------------------------------------------------------------------
/doc/dev/platform.rst:
--------------------------------------------------------------------------------
1 | Reference test platforms
2 | ------------------------
3 |
4 | About requirements
5 | ^^^^^^^^^^^^^^^^^^
6 |
7 | The ``requirements.txt`` mentioned in the following sections is a text file which
8 | contains the list of all the Python packages required for building up the projet
9 | environment. It is used by the ``pip`` command to install all the dependencies.
10 |
11 | The ``requirements.txt`` file is generated automatically by the
12 | ``toml-to-requirements`` tool. It is based on the ``pyproject.toml`` file
13 | which is the reference file for the project dependencies.
14 |
15 | .. warning::
16 |
17 | Please note that the generation is not systematic and the ``requirements.txt``
18 | file may not be up-to-date.
19 |
20 | To update the ``requirements.txt`` file, you need to install the
21 | ``toml-to-requirements`` and execute the following command:
22 |
23 | .. code-block:: bash
24 |
25 | toml-to-req --toml-file .\pyproject.toml --include-optional
26 |
27 |
28 | Microsoft Windows 10
29 | ^^^^^^^^^^^^^^^^^^^^
30 |
31 | First, install the latest version of Python 3.10 from the WinPython project.
32 |
33 | .. note::
34 |
35 | At the time of writing, the latest version is 3.10.11.1 which can be
36 | download from `here `_.
37 |
38 | Then install all the requirements using the following command from the WinPython
39 | command prompt:
40 |
41 | .. code-block:: bash
42 |
43 | pip install -r requirements.txt
44 |
45 | That's it, you can now run the tests using the following command:
46 |
47 | .. code-block:: bash
48 |
49 | pytest
50 |
51 | If you want to rely on Visual Studio Code for editing and take advantage of the
52 | project settings and tasks, you will need to set the following environment variable:
53 |
54 | .. code-block:: bash
55 |
56 | set PPSTACK_PYTHONEXE=C:\WPy64-31110\python-3.11.1.amd64\python.exe
57 |
58 | CentOS Stream 8.8
59 | ^^^^^^^^^^^^^^^^^
60 |
61 | .. note::
62 |
63 | The following instructions have been tested on CentOS Stream which is the
64 | reference platform for the project. However, they should work on
65 | any other Linux distribution relying on the ``yum`` package manager.
66 | As for the other distributions, you may need to adapt the instructions
67 | to your specific environment (e.g. use ``apt-get`` instead of ``yum``).
68 |
69 | First, install the prerequisites:
70 |
71 | .. code-block:: bash
72 |
73 | sudo yum install groupinstall "Development Tools" -y
74 | sudo yum install openssl-devel.i686 libffi-devel.i686 bzip2-devel.i686 sqlite-devel -y
75 |
76 | Check that ``gcc`` is installed and available in the ``PATH`` environment variable:
77 |
78 | .. code-block:: bash
79 |
80 | gcc --version
81 |
82 | Install OpenSSL 1.1.1:
83 |
84 | .. code-block:: bash
85 |
86 | wget https://www.openssl.org/source/openssl-1.1.1v.tar.gz
87 | tar -xvf openssl-1.1.1v.tar.gz
88 | cd openssl-1.1.1v
89 | ./config --prefix=/usr --openssldir=/etc/ssl --libdir=lib no-shared zlib-dynamic
90 | make
91 | sudo make install
92 | openssl version
93 | which openssl
94 | cd ..
95 |
96 | Install Python 3.10.13 (the latest 3.10 version at the time of writing):
97 |
98 | .. code-block:: bash
99 |
100 | wget https://www.python.org/ftp/python/3.10.13/Python-3.10.13.tgz
101 | tar -xvf Python-3.10.13.tgz
102 | cd Python-3.10.13
103 | ./configure --enable-optimizations --with-openssl=/usr --enable-loadable-sqlite-extensions
104 | sudo make altinstall
105 | cd ..
106 |
107 | Eventually add the ``/usr/local/bin`` directory to the ``PATH`` environment variable
108 | if Python has warned you about it:
109 |
110 | .. code-block:: bash
111 |
112 | sudo echo 'pathmunge /usr/local/bin' > /etc/profile.d/py310.sh
113 | chmod +x /etc/profile.d/py310.sh
114 | . /etc/profile # or logout and login again (reload the environment variables)
115 | echo $PATH # check that /usr/local/bin is in the PATH
116 |
117 | Create a virtual environment and install the requirements:
118 |
119 | .. code-block:: bash
120 |
121 | python3.10 -m venv guidata-venv
122 | source guidata-venv/bin/activate
123 | pip install --upgrade pip
124 | pip install -r requirements.txt
125 |
126 | That's it, you can now run the tests using the following command:
127 |
128 | .. code-block:: bash
129 |
130 | pytest
--------------------------------------------------------------------------------
/doc/dev/v2_to_v3.csv:
--------------------------------------------------------------------------------
1 | Version 2,Version 3
2 | ``.userconfigio.BaseIOHandler``,``.io.BaseIOHandler``
3 | ``.userconfigio.WriterMixin``,``.io.WriterMixin``
4 | ``.userconfigio.UserConfigIOHandler``,``.io.INIHandler``
5 | ``.userconfigio.UserConfigWriter``,``.io.INIWriter``
6 | ``.userconfigio.UserConfigReader``,``.io.INIReader``
7 | ``.jsonio.JSONHandler``,``.io.JSONHandler``
8 | ``.jsonio.JSONReader``,``.io.JSONReader``
9 | ``.jsonio.JSONWriter``,``.io.JSONWriter``
10 | ``.hdf5io.HDF5Handler``,``.io.HDF5Handler``
11 | ``.hdf5io.HDF5Reader``,``.io.HDF5Reader``
12 | ``.hdf5io.HDF5Writer``,``.io.HDF5Writer``
13 | ``.gettext_helpers``,``.utils.gettext_helpers``
14 | ``.disthelpers``,*removed*
15 | ``.encoding``,``.utils.encoding``
16 | ``.encoding.transcode``,*removed*
17 | ``.encoding.getfilesystemencoding``,*removed*
18 | ``.qthelpers.text_to_qcolor``,*removed*
19 | ``.utils.update_dataset``,``.dataset.update_dataset``
20 | ``.utils.restore_dataset``,``.dataset.restore_dataset``
21 | ``.utils.to_string``,``.utils.misc.to_string.misc``
22 | ``.utils.decode_fs_string``,``.utils.misc.decode_fs_string.misc``
23 | ``.utils.assert_interfaces_valid``,``.utils.misc.assert_interfaces_valid.misc``
24 | ``.utils.get_module_path``,``.utils.misc.get_module_path.misc``
25 | ``.utils.is_program_installed``,``.utils.misc.is_program_installed.misc``
26 | ``.utils.run_program``,``.utils.misc.run_program.misc``
27 | ``.utils.run_shell_command``,``.utils.misc.run_shell_command.misc``
28 | ``.utils.getcwd_or_home``,``.utils.misc.getcwd_or_home.misc``
29 | ``.utils.remove_backslashes``,``.utils.misc.remove_backslashes.misc``
30 | ``.qtwidgets.RotatedLabel``,``.widgets.rotatedlabel.RotatedLabel``
31 | ``.qtwidgets.DockableWidgetMixin``,``.widgets.dockable.DockableWidgetMixin``
32 | ``.qtwidgets.DockableWidget``,``.widgets.dockable.DockableWidget``
33 | ``.utils.min_equals_max``,*removed*
34 | ``.utils.pairs``,*removed*
35 | ``.utils.add_extension``,*removed*
36 | ``.utils.bind``,*removed*
37 | ``.utils.trace``,*removed*
38 | ``.utils.utf8_to_unicode``,*removed*
39 | ``.utils.unicode_to_stdout``,*removed*
40 | ``.utils.localtime_to_isodate``,*removed*
41 | ``.utils.isodate_to_localtime``,*removed*
42 | ``.utils.FormatTime``,*removed*
43 | ``.utils.Timer``,*removed*
44 | ``.utils.tic``,*removed*
45 | ``.utils.toc``,*removed*
46 | ``.utils.is_module_available``,*removed*
47 | ``.utils.get_package_data``,*removed*
48 | ``.utils.get_subpackages``,*removed*
49 | ``.utils.cythonize_all``,*removed*
50 |
--------------------------------------------------------------------------------
/doc/dev/v2_to_v3.rst:
--------------------------------------------------------------------------------
1 | Migrating from version 2 to version 3
2 | =====================================
3 |
4 | Version 3 is a new major version of the library which brings many new features,
5 | fixes bugs and improves the API. However, it is not fully backward compatible
6 | with the previous version.
7 |
8 | The main changes are:
9 |
10 | * New automated test suite
11 | * New documentation
12 | * `guidata.guitest`:
13 |
14 | * Added support for subpackages
15 | * New comment directive (``# guitest: show``) to add test module to test suite or
16 | to show test module in test launcher (this replaces the old ``SHOW = True`` line)
17 |
18 | * `guidata.dataset.datatypes.DataSet`: new `create` class method for concise
19 | dataset creation
20 |
21 | This section describes the steps to migrate your code from :mod:`guidata`
22 | version 2 to version 3.
23 |
24 | The following table gives the equivalence between version 2 and version 3 imports.
25 |
26 | For most of them, the change in the module path is the only difference (only
27 | the import statement have to be updated in your client code). For others, the
28 | third column of this table gives more details about the changes that may be
29 | required in your code.
30 |
31 | .. csv-table:: Compatibility table
32 | :file: v2_to_v3.csv
33 |
--------------------------------------------------------------------------------
/doc/examples.rst:
--------------------------------------------------------------------------------
1 | .. _examples:
2 |
3 | Data set examples
4 | =================
5 |
6 | Basic example
7 | -------------
8 |
9 | Source code :
10 |
11 | .. literalinclude:: basic_example.py
12 |
13 | .. image:: images/basic_example.png
14 |
15 | Other examples
16 | --------------
17 |
18 | A lot of examples are available in the :mod:`guidata` test module ::
19 |
20 | from guidata import tests
21 | tests.run()
22 |
23 | The two lines above execute the `guidata test launcher` :
24 |
25 | .. image:: images/screenshots/__init__.png
26 |
27 | All :mod:`guidata` items demo
28 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
29 |
30 | .. literalinclude:: ../guidata/tests/dataset/test_all_items.py
31 | :start-after: guitest:
32 |
33 | .. image:: images/screenshots/all_items.png
34 |
35 | All (GUI-related) :mod:`guidata` features demo
36 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
37 |
38 | .. literalinclude:: ../guidata/tests/dataset/test_all_features.py
39 | :start-after: guitest:
40 |
41 | .. image:: images/screenshots/all_features.png
42 |
43 | Embedding guidata objects in GUI layouts
44 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
45 |
46 | .. literalinclude:: ../guidata/tests/dataset/test_editgroupbox.py
47 | :start-after: guitest:
48 |
49 | .. image:: images/screenshots/editgroupbox.png
50 |
51 | Data item groups and group selection
52 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
53 |
54 | .. literalinclude:: ../guidata/tests/dataset/test_bool_selector.py
55 | :start-after: guitest:
56 |
57 | .. image:: images/screenshots/bool_selector.png
58 |
59 | Activable data sets
60 | ^^^^^^^^^^^^^^^^^^^
61 |
62 | .. literalinclude:: ../guidata/tests/dataset/test_activable_dataset.py
63 | :start-after: guitest:
64 |
65 | .. image:: images/screenshots/activable_dataset.png
66 |
67 | Data set groups
68 | ^^^^^^^^^^^^^^^
69 |
70 | .. literalinclude:: ../guidata/tests/dataset/test_datasetgroup.py
71 | :start-after: guitest:
72 |
73 | .. image:: images/screenshots/datasetgroup.png
74 |
75 | Utilities
76 | ^^^^^^^^^
77 |
78 | Update/restore a dataset from/to a dictionary
79 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
80 |
81 | .. literalinclude:: ../guidata/tests/unit/test_updaterestoredataset.py
82 | :start-after: guitest:
83 |
84 | Create a dataset class from a function signature
85 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
86 |
87 | .. literalinclude:: ../guidata/tests/unit/test_dataset_from_func.py
88 | :start-after: guitest:
89 |
90 | Create a dataset class from a function dictionary
91 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
92 |
93 | .. literalinclude:: ../guidata/tests/unit/test_dataset_from_dict.py
94 | :start-after: guitest:
95 |
96 | Data set HDF5 serialization/deserialization
97 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
98 |
99 | .. literalinclude:: ../guidata/tests/dataset/test_loadsave_hdf5.py
100 | :start-after: guitest:
101 |
102 | Data set JSON serialization/deserialization
103 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
104 |
105 | .. literalinclude:: ../guidata/tests/dataset/test_loadsave_json.py
106 | :start-after: guitest:
--------------------------------------------------------------------------------
/doc/images/basic_example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/doc/images/basic_example.png
--------------------------------------------------------------------------------
/doc/images/guidata-banner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/doc/images/guidata-banner.png
--------------------------------------------------------------------------------
/doc/images/guidata-vertical.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/doc/images/guidata-vertical.png
--------------------------------------------------------------------------------
/doc/images/layout_example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/doc/images/layout_example.png
--------------------------------------------------------------------------------
/doc/images/screenshots/__init__.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/doc/images/screenshots/__init__.png
--------------------------------------------------------------------------------
/doc/images/screenshots/activable_dataset.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/doc/images/screenshots/activable_dataset.png
--------------------------------------------------------------------------------
/doc/images/screenshots/all_features.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/doc/images/screenshots/all_features.png
--------------------------------------------------------------------------------
/doc/images/screenshots/all_items.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/doc/images/screenshots/all_items.png
--------------------------------------------------------------------------------
/doc/images/screenshots/arrayeditor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/doc/images/screenshots/arrayeditor.png
--------------------------------------------------------------------------------
/doc/images/screenshots/bool_selector.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/doc/images/screenshots/bool_selector.png
--------------------------------------------------------------------------------
/doc/images/screenshots/codeeditor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/doc/images/screenshots/codeeditor.png
--------------------------------------------------------------------------------
/doc/images/screenshots/collectioneditor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/doc/images/screenshots/collectioneditor.png
--------------------------------------------------------------------------------
/doc/images/screenshots/console.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/doc/images/screenshots/console.png
--------------------------------------------------------------------------------
/doc/images/screenshots/dataframeeditor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/doc/images/screenshots/dataframeeditor.png
--------------------------------------------------------------------------------
/doc/images/screenshots/datasetgroup.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/doc/images/screenshots/datasetgroup.png
--------------------------------------------------------------------------------
/doc/images/screenshots/editgroupbox.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/doc/images/screenshots/editgroupbox.png
--------------------------------------------------------------------------------
/doc/images/screenshots/importwizard.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/doc/images/screenshots/importwizard.png
--------------------------------------------------------------------------------
/doc/index.rst:
--------------------------------------------------------------------------------
1 | Welcome to :mod:`guidata`'s documentation!
2 | ==========================================
3 |
4 | .. image:: images/guidata-banner.png
5 | :align: center
6 |
7 | Based on the Qt library :mod:`guidata` is a Python library generating graphical
8 | user interfaces for easy dataset editing and display. It also provides :ref:`widgets`
9 | (Python console, code editor, array editor, etc. - see ), helpers and application
10 | development tools for Qt.
11 |
12 | .. figure:: images/layout_example.png
13 | :class: invert-in-dark-mode
14 |
15 | Simple example of layout generated by :mod:`guidata` (see :ref:`examples`).
16 |
17 | :mod:`guidata` is part of the `PlotPyStack`_ project, which aims at providing
18 | a full set of Python libraries for data plotting and data analysis.
19 |
20 | External resources:
21 |
22 | * Python Package Index: `PyPI`_
23 |
24 | * Bug reports and feature requests: `GitHub`_
25 |
26 | .. _PyPI: https://pypi.python.org/pypi/guidata
27 | .. _GitHub: https://github.com/PlotPyStack/guidata/
28 | .. _PlotPyStack: https://github.com/PlotPyStack
29 |
30 | .. module:: guidata
31 |
32 | Table of contents
33 | -----------------
34 |
35 | .. toctree::
36 | :maxdepth: 2
37 |
38 | overview
39 | installation
40 | examples
41 | widgets
42 | autodoc/index
43 | dev/index
44 | reference/index
45 | changelog
46 |
47 | * :ref:`genindex`
--------------------------------------------------------------------------------
/doc/installation.rst:
--------------------------------------------------------------------------------
1 | Installation
2 | ============
3 |
4 | Dependencies
5 | ------------
6 |
7 | .. include:: requirements.rst
8 |
9 | Installation using pip
10 | ----------------------
11 |
12 | The easiest way to install guidata is using `pip `_::
13 |
14 | pip install guidata
15 |
16 | Installation from source
17 | ------------------------
18 |
19 | To install from source, clone the repository or download the source package
20 | from `PyPI `_.
21 |
22 | Then run the following command (using `build `_)::
23 |
24 | python -m build
25 |
--------------------------------------------------------------------------------
/doc/overview.rst:
--------------------------------------------------------------------------------
1 | Overview
2 | ========
3 |
4 | When developping scientific software, from the simplest script to the
5 | most complex application, one systematically needs to manipulate data sets
6 | (e.g. parameters for a data processing feature).
7 | These data sets may consist of various data types: real numbers (e.g. physical
8 | quantities), integers (e.g. array indexes), strings (e.g. filenames),
9 | booleans (e.g. enable/disable an option), and so on.
10 |
11 | Most of the time, the programmer will need the following features:
12 |
13 | * allow the user to enter each parameter through a graphical user interface,
14 | using widgets which are adapted to data types (e.g. a single combo box or
15 | check boxes are suitable for presenting an option selection among
16 | multiple choices)
17 |
18 | * entered values have to be stored by the program with a convention which
19 | is again adapted to data types (e.g. when storing a combo box selection
20 | value, should we store the option string, the list index or an
21 | associated key?)
22 |
23 | * showing the stored values in a dialog box or within a graphical user
24 | interface layout, again with widgets adapted to data types
25 |
26 | * using the stored values easily (e.g. for data processing) by regrouping
27 | parameters in data structures
28 |
29 | * using those data structures to easily construct application data models
30 | (e.g. for storing application settings or data processing parameters)
31 | and to serialize and deserialize them (i.e. save and load them to/from
32 | HDF5, JSON or INI files)
33 |
34 | * update and restore a data set to/from a dictionary
35 |
36 | * generate a data set from a function signature (i.e. a function prototype)
37 | and use it to automatically generate a graphical user interface for
38 | calling the function
39 |
40 | This library aims to provide these features thanks to automatic graphical user
41 | interface generation for data set editing and display. Widgets inside GUIs are
42 | automatically generated depending on each data item type.
43 |
44 | The :mod:`guidata` library provides the following modules:
45 |
46 | * :py:mod:`guidata.dataset`: data set definition and manipulation
47 | * :py:mod:`guidata.widgets`: ready-to-use Qt widgets (console, code editor, array editor, etc.)
48 | * :py:mod:`guidata.qthelpers`: Qt helpers
49 | * :py:mod:`guidata.configtools`: library/application data management
50 | * :py:mod:`guidata.guitest`: automatic GUI-based test launcher
51 | * :py:mod:`guidata.utils`: utilities
52 |
--------------------------------------------------------------------------------
/doc/reference/dataset/conv.rst:
--------------------------------------------------------------------------------
1 | :tocdepth: 3
2 |
3 | .. automodule:: guidata.dataset.conv
4 |
--------------------------------------------------------------------------------
/doc/reference/dataset/dataitems.rst:
--------------------------------------------------------------------------------
1 | :tocdepth: 3
2 |
3 | .. automodule:: guidata.dataset.dataitems
4 |
--------------------------------------------------------------------------------
/doc/reference/dataset/datatypes.rst:
--------------------------------------------------------------------------------
1 | :tocdepth: 3
2 |
3 | .. automodule:: guidata.dataset.datatypes
4 |
--------------------------------------------------------------------------------
/doc/reference/dataset/index.rst:
--------------------------------------------------------------------------------
1 | Data set features
2 | =================
3 |
4 | .. toctree::
5 | :maxdepth: 2
6 | :caption: Contents:
7 |
8 | datatypes
9 | dataitems
10 | conv
11 | io
12 | qtwidgets
--------------------------------------------------------------------------------
/doc/reference/dataset/io.rst:
--------------------------------------------------------------------------------
1 | :tocdepth: 3
2 |
3 | .. automodule:: guidata.io
--------------------------------------------------------------------------------
/doc/reference/dataset/qtwidgets.rst:
--------------------------------------------------------------------------------
1 | :tocdepth: 3
2 |
3 | .. automodule:: guidata.dataset.qtwidgets
4 |
--------------------------------------------------------------------------------
/doc/reference/guitest.rst:
--------------------------------------------------------------------------------
1 | :tocdepth: 3
2 |
3 | .. automodule:: guidata.guitest
4 |
--------------------------------------------------------------------------------
/doc/reference/index.rst:
--------------------------------------------------------------------------------
1 | Reference
2 | ---------
3 |
4 | .. toctree::
5 | :maxdepth: 2
6 | :caption: Contents:
7 |
8 | dataset/index
9 | utils
10 | widgets
11 | userconfig
12 | guitest
13 |
--------------------------------------------------------------------------------
/doc/reference/userconfig.rst:
--------------------------------------------------------------------------------
1 | :tocdepth: 3
2 |
3 | .. automodule:: guidata.userconfig
--------------------------------------------------------------------------------
/doc/reference/utils.rst:
--------------------------------------------------------------------------------
1 | :tocdepth: 3
2 |
3 | Utilities
4 | =========
5 |
6 | .. automodule:: guidata.utils
7 |
8 | .. automodule:: guidata.utils.misc
9 |
10 | .. automodule:: guidata.configtools
--------------------------------------------------------------------------------
/doc/reference/widgets.rst:
--------------------------------------------------------------------------------
1 | :tocdepth: 3
2 |
3 | Widgets and Qt helpers
4 | ======================
5 |
6 | .. automodule:: guidata.qthelpers
7 |
8 | .. automodule:: guidata.widgets
9 |
--------------------------------------------------------------------------------
/doc/requirements.rst:
--------------------------------------------------------------------------------
1 | The :mod:`guidata` package requires the following Python modules:
2 |
3 | .. list-table::
4 | :header-rows: 1
5 | :align: left
6 |
7 | * - Name
8 | - Version
9 | - Summary
10 | * - Python
11 | - >=3.9, <4
12 | - Python programming language
13 | * - h5py
14 | - >=3.1
15 | - Read and write HDF5 files from Python
16 | * - NumPy
17 | - >=1.19
18 | - Fundamental package for array computing in Python
19 | * - QtPy
20 | - >=1.9
21 | - Provides an abstraction layer on top of the various Qt bindings (PyQt5/6 and PySide2/6).
22 | * - requests
23 | -
24 | - Python HTTP for Humans.
25 | * - tomli
26 | -
27 | - A lil' TOML parser
28 | * - PyQt5
29 | - >=5.11
30 | - Python bindings for the Qt cross platform application toolkit
31 |
32 | Optional modules for development:
33 |
34 | .. list-table::
35 | :header-rows: 1
36 | :align: left
37 |
38 | * - Name
39 | - Version
40 | - Summary
41 | * - ruff
42 | -
43 | - An extremely fast Python linter and code formatter, written in Rust.
44 | * - pylint
45 | -
46 | - python code static checker
47 | * - Coverage
48 | -
49 | - Code coverage measurement for Python
50 |
51 | Optional modules for building the documentation:
52 |
53 | .. list-table::
54 | :header-rows: 1
55 | :align: left
56 |
57 | * - Name
58 | - Version
59 | - Summary
60 | * - PyQt5
61 | -
62 | - Python bindings for the Qt cross platform application toolkit
63 | * - pillow
64 | -
65 | - Python Imaging Library (Fork)
66 | * - pandas
67 | -
68 | - Powerful data structures for data analysis, time series, and statistics
69 | * - sphinx
70 | - >6
71 | - Python documentation generator
72 | * - myst_parser
73 | -
74 | - An extended [CommonMark](https://spec.commonmark.org/) compliant parser,
75 | * - sphinx-copybutton
76 | -
77 | - Add a copy button to each of your code cells.
78 | * - sphinx_qt_documentation
79 | -
80 | - Plugin for proper resolve intersphinx references for Qt elements
81 | * - python-docs-theme
82 | -
83 | - The Sphinx theme for the CPython docs and related projects
84 |
85 | Optional modules for running test suite:
86 |
87 | .. list-table::
88 | :header-rows: 1
89 | :align: left
90 |
91 | * - Name
92 | - Version
93 | - Summary
94 | * - pytest
95 | -
96 | - pytest: simple powerful testing with Python
97 | * - pytest-xvfb
98 | -
99 | - A pytest plugin to run Xvfb (or Xephyr/Xvnc) for tests.
--------------------------------------------------------------------------------
/doc/update_requirements.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """Update requirements.rst file from pyproject.toml or setup.cfg file
4 |
5 | Warning: this has to be done manually at release time.
6 | It is not done automatically by the sphinx 'conf.py' file because it
7 | requires an internet connection to fetch the dependencies metadata - this
8 | is not always possible (e.g., when building the documentation on a machine
9 | without internet connection like the Debian package management infrastructure).
10 | """
11 |
12 | import guidata
13 | from guidata.utils.genreqs import gen_module_req_rst # noqa: E402
14 |
15 | if __name__ == "__main__":
16 | print("Updating requirements.rst file...", end=" ")
17 | gen_module_req_rst(guidata, ["Python>=3.8", "PyQt5>=5.11"])
18 | print("done.")
19 |
--------------------------------------------------------------------------------
/doc/widgets.rst:
--------------------------------------------------------------------------------
1 | .. _widgets:
2 |
3 | Ready-to-use widgets
4 | ====================
5 |
6 | As a complement to the :py:mod:`guidata.dataset` module which focus on automatic
7 | GUI generation for data sets editing and display, the :py:mod:`guidata.widgets`
8 | module provides a set of ready-to-use widgets for building interactive GUIs.
9 |
10 | .. note:: Most of the widgets originally come from the `Spyder`_ project
11 | (Copyright © Spyder Project Contributors, MIT-licensed). They were
12 | adapted to be used outside of the Spyder IDE and to be compatible with
13 | guidata internals.
14 |
15 | .. _Spyder: https://github.com/spyder-ide/spyder
16 |
17 | Python console
18 | --------------
19 |
20 | .. literalinclude:: ../guidata/tests/widgets/test_console.py
21 | :start-after: guitest:
22 |
23 | .. image:: images/screenshots/console.png
24 |
25 | Code editor
26 | -----------
27 |
28 | .. literalinclude:: ../guidata/tests/widgets/test_codeeditor.py
29 | :start-after: guitest:
30 |
31 | .. image:: images/screenshots/codeeditor.png
32 |
33 | Array editor
34 | ------------
35 |
36 | .. literalinclude:: ../guidata/tests/widgets/test_arrayeditor.py
37 | :start-after: guitest:
38 |
39 | .. image:: images/screenshots/arrayeditor.png
40 |
41 | Collection editor
42 | -----------------
43 |
44 | .. literalinclude:: ../guidata/tests/widgets/test_collectionseditor.py
45 | :start-after: guitest:
46 |
47 | .. image:: images/screenshots/collectioneditor.png
48 |
49 | Dataframe editor
50 | ----------------
51 |
52 | .. literalinclude:: ../guidata/tests/widgets/test_dataframeeditor.py
53 | :start-after: guitest:
54 |
55 | .. image:: images/screenshots/dataframeeditor.png
56 |
57 | Import wizard
58 | -------------
59 |
60 | .. literalinclude:: ../guidata/tests/widgets/test_importwizard.py
61 | :start-after: guitest:
62 |
63 | .. image:: images/screenshots/importwizard.png
64 |
--------------------------------------------------------------------------------
/guidata-tests.desktop:
--------------------------------------------------------------------------------
1 | [Desktop Entry]
2 | Version=1.0
3 | Type=Application
4 | Name=guidata-tests
5 | GenericName=guidata Test launcher
6 | Comment=The guidata Python library provides GUIs for easy dataset editing and display
7 | TryExec=guidata-tests
8 | Exec=guidata-tests
9 | Icon=guidata.svg
10 | Categories=Education;Science;Physics;
11 |
--------------------------------------------------------------------------------
/guidata/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | guidata
4 | =======
5 |
6 | Based on the Qt library :mod:`guidata` is a Python library generating graphical
7 | user interfaces for easy dataset editing and display. It also provides helpers
8 | and application development tools for Qt.
9 | """
10 |
11 | __version__ = "3.9.0"
12 |
13 |
14 | # Dear (Debian, RPM, ...) package makers, please feel free to customize the
15 | # following path to module's data (images) and translations:
16 | DATAPATH = LOCALEPATH = ""
17 |
18 |
19 | import guidata.config # noqa: E402, F401
20 |
21 |
22 | def qapplication():
23 | """
24 | Return QApplication instance
25 | Creates it if it doesn't already exist
26 | """
27 | from qtpy.QtWidgets import QApplication
28 |
29 | app = QApplication.instance()
30 | if not app:
31 | app = QApplication([])
32 | install_translator(app)
33 | from guidata import qthelpers
34 |
35 | qthelpers.set_color_mode()
36 | return app
37 |
38 |
39 | QT_TRANSLATOR = None
40 |
41 |
42 | def install_translator(qapp):
43 | """Install Qt translator to the QApplication instance"""
44 | global QT_TRANSLATOR
45 | if QT_TRANSLATOR is None:
46 | from qtpy.QtCore import QLibraryInfo, QLocale, QTranslator
47 |
48 | locale = QLocale.system().name()
49 | # Qt-specific translator
50 | qt_translator = QTranslator()
51 | paths = QLibraryInfo.location(QLibraryInfo.TranslationsPath)
52 | for prefix in ("qt", "qtbase"):
53 | if qt_translator.load(prefix + "_" + locale, paths):
54 | QT_TRANSLATOR = qt_translator # Keep reference alive
55 | break
56 | if QT_TRANSLATOR is not None:
57 | qapp.installTranslator(QT_TRANSLATOR)
58 |
--------------------------------------------------------------------------------
/guidata/data/icons/apply.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/guidata/data/icons/apply.png
--------------------------------------------------------------------------------
/guidata/data/icons/arredit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/guidata/data/icons/arredit.png
--------------------------------------------------------------------------------
/guidata/data/icons/busy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/guidata/data/icons/busy.png
--------------------------------------------------------------------------------
/guidata/data/icons/cell_edit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/guidata/data/icons/cell_edit.png
--------------------------------------------------------------------------------
/guidata/data/icons/copy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/guidata/data/icons/copy.png
--------------------------------------------------------------------------------
/guidata/data/icons/copy_all.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/guidata/data/icons/delete.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/guidata/data/icons/delete.png
--------------------------------------------------------------------------------
/guidata/data/icons/dictedit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/guidata/data/icons/dictedit.png
--------------------------------------------------------------------------------
/guidata/data/icons/dtype.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/guidata/data/icons/dtype.png
--------------------------------------------------------------------------------
/guidata/data/icons/edit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/guidata/data/icons/edit.png
--------------------------------------------------------------------------------
/guidata/data/icons/editors/copywop.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/guidata/data/icons/editors/copywop.png
--------------------------------------------------------------------------------
/guidata/data/icons/editors/edit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/guidata/data/icons/editors/edit.png
--------------------------------------------------------------------------------
/guidata/data/icons/editors/edit_add.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/guidata/data/icons/editors/edit_add.png
--------------------------------------------------------------------------------
/guidata/data/icons/editors/editclear.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/guidata/data/icons/editors/editclear.png
--------------------------------------------------------------------------------
/guidata/data/icons/editors/editcopy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/guidata/data/icons/editors/editcopy.png
--------------------------------------------------------------------------------
/guidata/data/icons/editors/editcut.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/guidata/data/icons/editors/editcut.png
--------------------------------------------------------------------------------
/guidata/data/icons/editors/editdelete.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/guidata/data/icons/editors/editdelete.png
--------------------------------------------------------------------------------
/guidata/data/icons/editors/editpaste.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/guidata/data/icons/editors/editpaste.png
--------------------------------------------------------------------------------
/guidata/data/icons/editors/fileimport.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/guidata/data/icons/editors/fileimport.png
--------------------------------------------------------------------------------
/guidata/data/icons/editors/filesave.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/guidata/data/icons/editors/filesave.png
--------------------------------------------------------------------------------
/guidata/data/icons/editors/imshow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/guidata/data/icons/editors/imshow.png
--------------------------------------------------------------------------------
/guidata/data/icons/editors/insert.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/guidata/data/icons/editors/insert.png
--------------------------------------------------------------------------------
/guidata/data/icons/editors/plot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/guidata/data/icons/editors/plot.png
--------------------------------------------------------------------------------
/guidata/data/icons/editors/rename.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/guidata/data/icons/editors/rename.png
--------------------------------------------------------------------------------
/guidata/data/icons/editors/selectall.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/guidata/data/icons/editors/selectall.png
--------------------------------------------------------------------------------
/guidata/data/icons/exit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/guidata/data/icons/exit.png
--------------------------------------------------------------------------------
/guidata/data/icons/expander_down.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/guidata/data/icons/expander_down.png
--------------------------------------------------------------------------------
/guidata/data/icons/expander_right.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/guidata/data/icons/expander_right.png
--------------------------------------------------------------------------------
/guidata/data/icons/export.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/guidata/data/icons/file.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/guidata/data/icons/file.png
--------------------------------------------------------------------------------
/guidata/data/icons/fileclose.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/guidata/data/icons/fileclose.png
--------------------------------------------------------------------------------
/guidata/data/icons/fileimport.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/guidata/data/icons/fileimport.png
--------------------------------------------------------------------------------
/guidata/data/icons/filenew.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/guidata/data/icons/filenew.png
--------------------------------------------------------------------------------
/guidata/data/icons/fileopen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/guidata/data/icons/fileopen.png
--------------------------------------------------------------------------------
/guidata/data/icons/filesave.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/guidata/data/icons/filesave.png
--------------------------------------------------------------------------------
/guidata/data/icons/filesaveas.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/guidata/data/icons/filesaveas.png
--------------------------------------------------------------------------------
/guidata/data/icons/filetypes/doc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/guidata/data/icons/filetypes/doc.png
--------------------------------------------------------------------------------
/guidata/data/icons/filetypes/gif.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/guidata/data/icons/filetypes/gif.png
--------------------------------------------------------------------------------
/guidata/data/icons/filetypes/html.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/guidata/data/icons/filetypes/html.png
--------------------------------------------------------------------------------
/guidata/data/icons/filetypes/jpg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/guidata/data/icons/filetypes/jpg.png
--------------------------------------------------------------------------------
/guidata/data/icons/filetypes/pdf.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/guidata/data/icons/filetypes/pdf.png
--------------------------------------------------------------------------------
/guidata/data/icons/filetypes/png.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/guidata/data/icons/filetypes/png.png
--------------------------------------------------------------------------------
/guidata/data/icons/filetypes/pps.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/guidata/data/icons/filetypes/pps.png
--------------------------------------------------------------------------------
/guidata/data/icons/filetypes/ps.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/guidata/data/icons/filetypes/ps.png
--------------------------------------------------------------------------------
/guidata/data/icons/filetypes/tar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/guidata/data/icons/filetypes/tar.png
--------------------------------------------------------------------------------
/guidata/data/icons/filetypes/tgz.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/guidata/data/icons/filetypes/tgz.png
--------------------------------------------------------------------------------
/guidata/data/icons/filetypes/tif.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/guidata/data/icons/filetypes/tif.png
--------------------------------------------------------------------------------
/guidata/data/icons/filetypes/txt.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/guidata/data/icons/filetypes/txt.png
--------------------------------------------------------------------------------
/guidata/data/icons/filetypes/xls.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/guidata/data/icons/filetypes/xls.png
--------------------------------------------------------------------------------
/guidata/data/icons/filetypes/zip.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/guidata/data/icons/filetypes/zip.png
--------------------------------------------------------------------------------
/guidata/data/icons/format.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/guidata/data/icons/hist.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/guidata/data/icons/hist.png
--------------------------------------------------------------------------------
/guidata/data/icons/max.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/guidata/data/icons/max.png
--------------------------------------------------------------------------------
/guidata/data/icons/min.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/guidata/data/icons/min.png
--------------------------------------------------------------------------------
/guidata/data/icons/none.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/guidata/data/icons/none.png
--------------------------------------------------------------------------------
/guidata/data/icons/not_found.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/guidata/data/icons/not_found.png
--------------------------------------------------------------------------------
/guidata/data/icons/python.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/guidata/data/icons/python.png
--------------------------------------------------------------------------------
/guidata/data/icons/quickview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/guidata/data/icons/quickview.png
--------------------------------------------------------------------------------
/guidata/data/icons/resize.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/guidata/data/icons/save_all.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/guidata/data/icons/save_all.png
--------------------------------------------------------------------------------
/guidata/data/icons/selection.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/guidata/data/icons/selection.png
--------------------------------------------------------------------------------
/guidata/data/icons/settings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/guidata/data/icons/settings.png
--------------------------------------------------------------------------------
/guidata/data/icons/shape.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/guidata/data/icons/shape.png
--------------------------------------------------------------------------------
/guidata/data/icons/xmax.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/guidata/data/icons/xmax.png
--------------------------------------------------------------------------------
/guidata/data/icons/xmin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/guidata/data/icons/xmin.png
--------------------------------------------------------------------------------
/guidata/dataset/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # Licensed under the terms of the BSD 3-Clause
4 | # (see guidata/LICENSE for details)
5 |
6 | # flake8: noqa
7 |
8 | from .conv import (
9 | create_dataset_from_dict,
10 | create_dataset_from_func,
11 | restore_dataset,
12 | update_dataset,
13 | )
14 | from .dataitems import (
15 | BoolItem,
16 | ButtonItem,
17 | ChoiceItem,
18 | ColorItem,
19 | DateItem,
20 | DateTimeItem,
21 | DictItem,
22 | DirectoryItem,
23 | FileOpenItem,
24 | FileSaveItem,
25 | FilesOpenItem,
26 | FloatArrayItem,
27 | FloatItem,
28 | FontFamilyItem,
29 | ImageChoiceItem,
30 | IntItem,
31 | MultipleChoiceItem,
32 | StringItem,
33 | TextItem,
34 | )
35 | from .datatypes import (
36 | ActivableDataSet,
37 | AnyDataSet,
38 | BeginGroup,
39 | BeginTabGroup,
40 | DataItem,
41 | DataItemProxy,
42 | DataItemVariable,
43 | DataSet,
44 | DataSetGroup,
45 | DataSetMeta,
46 | EndGroup,
47 | EndTabGroup,
48 | FormatProp,
49 | FuncProp,
50 | GetAttrProp,
51 | GroupItem,
52 | ItemProperty,
53 | NoDefault,
54 | NotProp,
55 | Obj,
56 | ObjectItem,
57 | TabGroupItem,
58 | ValueProp,
59 | )
60 |
--------------------------------------------------------------------------------
/guidata/dataset/io.py:
--------------------------------------------------------------------------------
1 | import warnings
2 |
3 | # Compatibility imports with guidata <= 3.3
4 | from guidata.io.base import BaseIOHandler, GroupContext, WriterMixin # noqa
5 | from guidata.io.h5fmt import HDF5Handler, HDF5Reader, HDF5Writer # noqa
6 | from guidata.io.inifmt import INIHandler, INIReader, INIWriter # noqa
7 | from guidata.io.jsonfmt import JSONHandler, JSONReader, JSONWriter # noqa
8 |
9 | warnings.warn(
10 | "guidata.dataset.io module is deprecated and will be removed in a future release. "
11 | "Please use guidata.io instead.",
12 | DeprecationWarning,
13 | stacklevel=2,
14 | )
15 |
--------------------------------------------------------------------------------
/guidata/dataset/note_directive.py:
--------------------------------------------------------------------------------
1 | #
2 | # Licensed under the terms of the BSD 3-Clause
3 | # (see guidata/LICENSE for details)
4 |
5 | """Sphinx directive to display a note about how to instanciate a dataset class"""
6 |
7 | from __future__ import annotations
8 |
9 | from typing import TYPE_CHECKING, Type
10 |
11 | from docutils import nodes
12 | from sphinx.util import logging
13 | from sphinx.util.docutils import SphinxDirective
14 |
15 | import guidata.dataset as gds
16 | from guidata import __version__
17 |
18 | if TYPE_CHECKING:
19 | import sphinx.application
20 |
21 | logger = logging.getLogger(__name__)
22 |
23 |
24 | class DatasetNoteDirective(SphinxDirective):
25 | """Directive to display a note about how to instanciate a dataset class"""
26 |
27 | required_arguments = 1 # the class name is a required argument
28 | optional_arguments = 1 # the number of example lines to display is optional
29 | final_argument_whitespace = True
30 | has_content = True
31 |
32 | def run(self):
33 | class_name = self.arguments[0]
34 |
35 | example_lines: int | None
36 | if len(self.arguments) > self.required_arguments:
37 | example_lines = int(self.arguments[self.required_arguments])
38 | else:
39 | example_lines = None
40 |
41 | cls: Type[gds.DataSet]
42 | try:
43 | # Try to import the class
44 | module_name, class_name = class_name.rsplit(".", 1)
45 | module = __import__(module_name, fromlist=[class_name])
46 | cls = getattr(module, class_name)
47 |
48 | except Exception as e:
49 | logger.warning(f"Failed to import class {class_name}: {e}")
50 | instance_str = f"Failed to import class {class_name}"
51 | note_node = nodes.error(instance_str)
52 | return [note_node]
53 |
54 | # Create an instance of the class and get its string representation
55 | instance = cls()
56 | instance_str = str(instance)
57 | items = instance.get_items()
58 |
59 | formated_args = ", ".join(
60 | f"{item.get_name()}={item._default}" for item in instance.get_items()
61 | )
62 | note_node = nodes.note()
63 | paragraph1 = nodes.paragraph()
64 | paragraph1 += nodes.Text("To instanciate a new ")
65 | paragraph1 += nodes.literal(text=f"{cls.__name__}")
66 | paragraph1 += nodes.Text(" , you can use the create() classmethod like this:")
67 | paragraph1 += nodes.literal_block(
68 | text=f"{cls.__name__}.create({formated_args})", language="python"
69 | )
70 | note_node += paragraph1
71 |
72 | paragraph2 = nodes.paragraph()
73 | paragraph2 += nodes.Text("You can also first instanciate a default ")
74 | paragraph2 += nodes.literal(text=f" {cls.__name__}")
75 | paragraph2 += nodes.Text(" and then set the fields like this:")
76 |
77 | example_lines = min(len(items), example_lines) if example_lines else len(items)
78 | code_lines = [
79 | f"dataset = {cls.__name__}()",
80 | *(
81 | f"dataset.{items[i].get_name()} = {repr(items[i]._default)}"
82 | for i in range(example_lines)
83 | ),
84 | ]
85 | if len(items) > example_lines:
86 | code_lines.append("...")
87 |
88 | paragraph2 += nodes.literal_block(
89 | text="\n".join(code_lines),
90 | language="python",
91 | )
92 | note_node += paragraph2
93 |
94 | # Create a note node
95 |
96 | return [note_node]
97 |
98 |
99 | def setup(app: sphinx.application.Sphinx) -> dict[str, object]:
100 | """Initialize the Sphinx extension"""
101 | app.add_directive("datasetnote", DatasetNoteDirective)
102 |
103 | return {
104 | "version": __version__,
105 | "parallel_read_safe": True,
106 | "parallel_write_safe": True,
107 | }
108 |
--------------------------------------------------------------------------------
/guidata/dataset/textedit.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # Licensed under the terms of the BSD 3-Clause
4 | # (see guidata/LICENSE for details)
5 |
6 | """
7 | Text visitor for DataItem objects
8 | (for test purpose only)
9 | """
10 |
11 |
12 | def prompt(item):
13 | """Get item value"""
14 | return input(item.get_prop("display", "label") + " ? ")
15 |
16 |
17 | class TextEditVisitor:
18 | """Text visitor"""
19 |
20 | def __init__(self, instance):
21 | self.instance = instance
22 |
23 | def visit_generic(self, item):
24 | """Generic visitor"""
25 | while True:
26 | value = prompt(item)
27 | item.set_from_string(self.instance, value)
28 | if item.check_item(self.instance):
29 | break
30 | print("Incorrect value!")
31 |
32 | visit_FloatItem = visit_generic
33 | visit_IntItem = visit_generic
34 | visit_StringItem = visit_generic
35 |
--------------------------------------------------------------------------------
/guidata/external/__init__.py:
--------------------------------------------------------------------------------
1 | #
2 |
--------------------------------------------------------------------------------
/guidata/external/darkdetect/__init__.py:
--------------------------------------------------------------------------------
1 | # -----------------------------------------------------------------------------
2 | # Copyright (C) 2019 Alberto Sottile
3 | #
4 | # Distributed under the terms of the 3-clause BSD License.
5 | # -----------------------------------------------------------------------------
6 |
7 | __version__ = "0.5.2" # Patched (see pull request below)
8 |
9 | import sys
10 | import platform
11 |
12 | if sys.platform == "darwin":
13 | from distutils.version import LooseVersion as V
14 |
15 | if V(platform.mac_ver()[0]) < V("10.14"):
16 | from ._dummy import *
17 | else:
18 | from ._mac_detect import *
19 | del V
20 | # Patch: https://github.com/albertosottile/darkdetect/pull/21
21 | elif (
22 | sys.platform == "win32"
23 | and platform.release().isdigit()
24 | and int(platform.release()) >= 10
25 | ):
26 | # Checks if running Windows 10 version 10.0.14393 (Anniversary Update) OR HIGHER. The getwindowsversion method returns a tuple.
27 | # The third item is the build number that we can use to check if the user has a new enough version of Windows.
28 | winver = int(platform.version().split(".")[2])
29 | if winver >= 14393:
30 | from ._windows_detect import *
31 | else:
32 | from ._dummy import *
33 | elif sys.platform == "linux":
34 | from ._linux_detect import *
35 | else:
36 | from ._dummy import *
37 |
38 | del sys, platform
39 |
--------------------------------------------------------------------------------
/guidata/external/darkdetect/_dummy.py:
--------------------------------------------------------------------------------
1 | #-----------------------------------------------------------------------------
2 | # Copyright (C) 2019 Alberto Sottile
3 | #
4 | # Distributed under the terms of the 3-clause BSD License.
5 | #-----------------------------------------------------------------------------
6 |
7 | def theme():
8 | return None
9 |
10 | def isDark():
11 | return None
12 |
13 | def isLight():
14 | return None
15 |
--------------------------------------------------------------------------------
/guidata/external/darkdetect/_linux_detect.py:
--------------------------------------------------------------------------------
1 | #-----------------------------------------------------------------------------
2 | # Copyright (C) 2019 Alberto Sottile, Eric Larson
3 | #
4 | # Distributed under the terms of the 3-clause BSD License.
5 | #-----------------------------------------------------------------------------
6 |
7 | import subprocess
8 |
9 |
10 | def theme():
11 | # Here we just triage to GTK settings for now
12 | try:
13 | out = subprocess.run(
14 | ['gsettings', 'get', 'org.gnome.desktop.interface', 'gtk-theme'],
15 | capture_output=True)
16 | stdout = out.stdout.decode()
17 | except Exception:
18 | return 'Light'
19 | # we have a string, now remove start and end quote
20 | theme = stdout.lower().strip()[1:-1]
21 | if theme.endswith('-dark'):
22 | return 'Dark'
23 | else:
24 | return 'Light'
25 |
26 | def isDark():
27 | return theme() == 'Dark'
28 |
29 | def isLight():
30 | return theme() == 'Light'
31 |
--------------------------------------------------------------------------------
/guidata/external/darkdetect/_mac_detect.py:
--------------------------------------------------------------------------------
1 | #-----------------------------------------------------------------------------
2 | # Copyright (C) 2019 Alberto Sottile
3 | #
4 | # Distributed under the terms of the 3-clause BSD License.
5 | #-----------------------------------------------------------------------------
6 |
7 | import ctypes
8 | import ctypes.util
9 |
10 | try:
11 | # macOS Big Sur+ use "a built-in dynamic linker cache of all system-provided libraries"
12 | appkit = ctypes.cdll.LoadLibrary('AppKit.framework/AppKit')
13 | objc = ctypes.cdll.LoadLibrary('libobjc.dylib')
14 | except OSError:
15 | # revert to full path for older OS versions and hardened programs
16 | appkit = ctypes.cdll.LoadLibrary(ctypes.util.find_library('AppKit'))
17 | objc = ctypes.cdll.LoadLibrary(ctypes.util.find_library('objc'))
18 |
19 | void_p = ctypes.c_void_p
20 | ull = ctypes.c_uint64
21 |
22 | objc.objc_getClass.restype = void_p
23 | objc.sel_registerName.restype = void_p
24 |
25 | # See https://docs.python.org/3/library/ctypes.html#function-prototypes for arguments description
26 | MSGPROTOTYPE = ctypes.CFUNCTYPE(void_p, void_p, void_p, void_p)
27 | msg = MSGPROTOTYPE(('objc_msgSend', objc), ((1 ,'', None), (1, '', None), (1, '', None)))
28 |
29 | def _utf8(s):
30 | if not isinstance(s, bytes):
31 | s = s.encode('utf8')
32 | return s
33 |
34 | def n(name):
35 | return objc.sel_registerName(_utf8(name))
36 |
37 | def C(classname):
38 | return objc.objc_getClass(_utf8(classname))
39 |
40 | def theme():
41 | NSAutoreleasePool = objc.objc_getClass('NSAutoreleasePool')
42 | pool = msg(NSAutoreleasePool, n('alloc'))
43 | pool = msg(pool, n('init'))
44 |
45 | NSUserDefaults = C('NSUserDefaults')
46 | stdUserDef = msg(NSUserDefaults, n('standardUserDefaults'))
47 |
48 | NSString = C('NSString')
49 |
50 | key = msg(NSString, n("stringWithUTF8String:"), _utf8('AppleInterfaceStyle'))
51 | appearanceNS = msg(stdUserDef, n('stringForKey:'), void_p(key))
52 | appearanceC = msg(appearanceNS, n('UTF8String'))
53 |
54 | if appearanceC is not None:
55 | out = ctypes.string_at(appearanceC)
56 | else:
57 | out = None
58 |
59 | msg(pool, n('release'))
60 |
61 | if out is not None:
62 | return out.decode('utf-8')
63 | else:
64 | return 'Light'
65 |
66 | def isDark():
67 | return theme() == 'Dark'
68 |
69 | def isLight():
70 | return theme() == 'Light'
71 |
--------------------------------------------------------------------------------
/guidata/external/darkdetect/_windows_detect.py:
--------------------------------------------------------------------------------
1 | from winreg import HKEY_CURRENT_USER as hkey, QueryValueEx as getSubkeyValue, OpenKey as getKey
2 |
3 | def theme():
4 | """ Uses the Windows Registry to detect if the user is using Dark Mode """
5 | # Registry will return 0 if Windows is in Dark Mode and 1 if Windows is in Light Mode. This dictionary converts that output into the text that the program is expecting.
6 | valueMeaning = {0: "Dark", 1: "Light"}
7 | # In HKEY_CURRENT_USER, get the Personalisation Key.
8 | try:
9 | key = getKey(hkey, "Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize")
10 | # In the Personalisation Key, get the AppsUseLightTheme subkey. This returns a tuple.
11 | # The first item in the tuple is the result we want (0 or 1 indicating Dark Mode or Light Mode); the other value is the type of subkey e.g. DWORD, QWORD, String, etc.
12 | subkey = getSubkeyValue(key, "AppsUseLightTheme")[0]
13 | except FileNotFoundError:
14 | # some headless Windows instances (e.g. GitHub Actions or Docker images) do not have this key
15 | return None
16 | return valueMeaning[subkey]
17 |
18 | def isDark():
19 | if theme() is not None:
20 | return theme() == 'Dark'
21 |
22 | def isLight():
23 | if theme() is not None:
24 | return theme() == 'Light'
--------------------------------------------------------------------------------
/guidata/io/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # Licensed under the terms of the BSD 3-Clause
4 | # (see guidata/LICENSE for details)
5 |
6 | """
7 | Data serialization and deserialization
8 | --------------------------------------
9 |
10 | The ``guidata.io`` package provides the core features for data
11 | (:py:class:`guidata.dataset.DataSet` or other objects) serialization and
12 | deserialization.
13 |
14 | Base classes for I/O handlers
15 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
16 |
17 | Base classes for writing custom readers and writers:
18 |
19 | * :py:class:`BaseIOHandler`
20 | * :py:class:`WriterMixin`
21 |
22 | .. autoclass:: GroupContext
23 |
24 | .. autoclass:: BaseIOHandler
25 | :members:
26 |
27 | .. autoclass:: WriterMixin
28 | :members:
29 |
30 | Configuration files (.ini)
31 | ^^^^^^^^^^^^^^^^^^^^^^^^^^
32 |
33 | Reader and writer for the serialization of data sets into .ini files:
34 |
35 | * :py:class:`INIReader`
36 | * :py:class:`INIWriter`
37 |
38 | .. autoclass:: INIReader
39 | :members:
40 |
41 | .. autoclass:: INIWriter
42 | :members:
43 |
44 | JSON files (.json)
45 | ^^^^^^^^^^^^^^^^^^
46 |
47 | Reader and writer for the serialization of data sets into .json files:
48 |
49 | * :py:class:`JSONReader`
50 | * :py:class:`JSONWriter`
51 |
52 | .. autoclass:: JSONReader
53 | :members:
54 |
55 | .. autoclass:: JSONWriter
56 | :members:
57 |
58 | HDF5 files (.h5)
59 | ^^^^^^^^^^^^^^^^
60 |
61 | Reader and writer for the serialization of data sets into .h5 files:
62 |
63 | * :py:class:`HDF5Reader`
64 | * :py:class:`HDF5Writer`
65 |
66 | .. autoclass:: HDF5Reader
67 | :members:
68 |
69 | .. autoclass:: HDF5Writer
70 | :members:
71 | """
72 |
73 | # pylint: disable=unused-import
74 | from .base import BaseIOHandler, GroupContext, WriterMixin # noqa
75 | from .h5fmt import HDF5Handler, HDF5Reader, HDF5Writer # noqa
76 | from .inifmt import INIHandler, INIReader, INIWriter # noqa
77 | from .jsonfmt import JSONHandler, JSONReader, JSONWriter # noqa
78 |
--------------------------------------------------------------------------------
/guidata/tests/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # Licensed under the terms of the BSD 3-Clause
4 | # (see guidata/LICENSE for details)
5 |
6 | """
7 | guidata test package
8 | ====================
9 | """
10 |
11 | import os.path as osp
12 |
13 | import guidata
14 | from guidata.configtools import get_module_data_path
15 |
16 | TESTDATAPATH = get_module_data_path("guidata", osp.join("tests", "data"))
17 |
18 |
19 | def get_path(filename: str) -> str:
20 | """Return absolute path of test file"""
21 | return osp.join(TESTDATAPATH, filename)
22 |
23 |
24 | def run():
25 | """Run guidata test launcher"""
26 | from guidata.guitest import run_testlauncher
27 |
28 | run_testlauncher(guidata)
29 |
30 |
31 | if __name__ == "__main__":
32 | run()
33 |
--------------------------------------------------------------------------------
/guidata/tests/conftest.py:
--------------------------------------------------------------------------------
1 | # content of conftest.py
2 |
3 | import qtpy
4 |
5 | import guidata
6 | from guidata.env import execenv
7 |
8 | # Turn on unattended mode for executing tests without user interaction
9 | execenv.unattended = True
10 | execenv.verbose = "quiet"
11 |
12 |
13 | def pytest_report_header(config):
14 | """Add additional information to the pytest report header."""
15 | qtbindings_version = qtpy.PYSIDE_VERSION
16 | if qtbindings_version is None:
17 | qtbindings_version = qtpy.PYQT_VERSION
18 | return [
19 | f"guidata {guidata.__version__}, "
20 | f"{qtpy.API_NAME} {qtbindings_version} [Qt version: {qtpy.QT_VERSION}]",
21 | ]
22 |
--------------------------------------------------------------------------------
/guidata/tests/data/genreqs/pyproject.toml:
--------------------------------------------------------------------------------
1 | # guidata setup configuration file
2 |
3 | # Important note:
4 | # Requirements (see [options]) are parsed by utils\genreqs.py to generate documentation
5 |
6 | [build-system]
7 | requires = ["setuptools"]
8 | build-backend = "setuptools.build_meta"
9 |
10 | [project]
11 | name = "guidata"
12 | authors = [{ name = "Codra", email = "p.raybaut@codra.fr" }]
13 | description = "Signal and image processing software"
14 | readme = "README.md"
15 | license = { file = "LICENSE" }
16 | classifiers = [
17 | "Topic :: Scientific/Engineering",
18 | "Topic :: Software Development :: Libraries :: Python Modules",
19 | "Topic :: Utilities",
20 | "Topic :: Scientific/Engineering",
21 | "Topic :: Scientific/Engineering :: Human Machine Interfaces",
22 | "Topic :: Software Development :: User Interfaces",
23 | "Operating System :: MacOS",
24 | "Operating System :: Microsoft :: Windows",
25 | "Operating System :: OS Independent",
26 | "Operating System :: POSIX",
27 | "Operating System :: Unix",
28 | "Programming Language :: Python :: 3.9",
29 | "Programming Language :: Python :: 3.10",
30 | "Programming Language :: Python :: 3.11",
31 | ]
32 | requires-python = ">=3.9, <4"
33 | dependencies = ["h5py>=3.0", "NumPy>=1.21", "QtPy>=1.9"]
34 | dynamic = ["version"]
35 |
36 | [project.urls]
37 | Homepage = "https://github.com/PlotPyStack/guidata/"
38 | Documentation = "https://guidata.readthedocs.io/en/latest/"
39 |
40 | [project.scripts]
41 |
42 | [project.optional-dependencies]
43 | dev = ["ruff", "pylint", "Coverage"]
44 | doc = [
45 | "PyQt5",
46 | "pillow",
47 | "pandas",
48 | "sphinx",
49 | "sphinx-copybutton",
50 | "sphinx_qt_documentation",
51 | "python-docs-theme",
52 | ]
53 | test = ["pytest", "pytest-cov", "pytest-qt", "pytest-xvfb"]
54 |
55 | [tool.setuptools]
56 | include-package-data = false
57 |
58 | [tool.setuptools.package-data]
59 | "*" = ["*.png", "*.svg", "*.mo", "*.cfg", "*.toml", "*.rst"]
60 |
61 | [tool.setuptools.dynamic]
62 | version = { attr = "guidata.__version__" }
63 |
--------------------------------------------------------------------------------
/guidata/tests/data/genreqs/requirements.rst:
--------------------------------------------------------------------------------
1 | The :mod:`guidata` package requires the following Python modules:
2 |
3 | .. list-table::
4 | :header-rows: 1
5 | :align: left
6 |
7 | * - Name
8 | - Version
9 | - Summary
10 | * - Python
11 | - >=3.8, <4
12 | -
13 | * - h5py
14 | - >=3.0
15 | - Read and write HDF5 files from Python
16 | * - NumPy
17 | - >=1.21
18 | - Fundamental package for array computing in Python
19 | * - QtPy
20 | - >=1.9
21 | - Provides an abstraction layer on top of the various Qt bindings (PyQt5/6 and PySide2/6).
22 | * - PyQt
23 | - >=5.11
24 | -
25 |
26 | Optional modules for development:
27 |
28 | .. list-table::
29 | :header-rows: 1
30 | :align: left
31 |
32 | * - Name
33 | - Version
34 | - Summary
35 | * - black
36 | -
37 | - The uncompromising code formatter.
38 | * - isort
39 | -
40 | - A Python utility / library to sort Python imports.
41 | * - pylint
42 | -
43 | - python code static checker
44 | * - Coverage
45 | -
46 | - Code coverage measurement for Python
47 |
48 | Optional modules for building the documentation:
49 |
50 | .. list-table::
51 | :header-rows: 1
52 | :align: left
53 |
54 | * - Name
55 | - Version
56 | - Summary
57 | * - PyQt5
58 | -
59 | - Python bindings for the Qt cross platform application toolkit
60 | * - pillow
61 | -
62 | - Python Imaging Library (Fork)
63 | * - pandas
64 | -
65 | - Powerful data structures for data analysis, time series, and statistics
66 | * - sphinx
67 | -
68 | - Python documentation generator
69 | * - sphinx-copybutton
70 | -
71 | - Add a copy button to each of your code cells.
72 | * - sphinx_qt_documentation
73 | -
74 | - Plugin for proper resolve intersphinx references for Qt elements
75 | * - python-docs-theme
76 | -
77 | - The Sphinx theme for the CPython docs and related projects
78 |
79 | Optional modules for running test suite:
80 |
81 | .. list-table::
82 | :header-rows: 1
83 | :align: left
84 |
85 | * - Name
86 | - Version
87 | - Summary
88 | * - pytest
89 | -
90 | - pytest: simple powerful testing with Python
91 | * - pytest-cov
92 | -
93 | - Pytest plugin for measuring coverage.
94 | * - pytest-qt
95 | -
96 | - pytest support for PyQt and PySide applications
97 | * - pytest-xvfb
98 | -
99 | - A pytest plugin to run Xvfb (or Xephyr/Xvnc) for tests.
--------------------------------------------------------------------------------
/guidata/tests/data/genreqs/setup.cfg:
--------------------------------------------------------------------------------
1 |
2 | [metadata]
3 | name = guidata
4 | version = attr: guidata.__version__
5 | author = Codra
6 | author_email = p.raybaut@codra.fr
7 | url = https://github.com/PlotPyStack/guidata/
8 | description = Signal and image processing software
9 | long_description = file: README.md
10 | long_description_content_type = text/markdown
11 | license = BSD 3-Clause License
12 | classifiers =
13 | Topic :: Scientific/Engineering
14 | Topic :: Software Development :: Libraries :: Python Modules
15 | Topic :: Utilities
16 | Topic :: Scientific/Engineering
17 | Topic :: Scientific/Engineering :: Human Machine Interfaces
18 | Topic :: Software Development :: User Interfaces
19 | Operating System :: MacOS
20 | Operating System :: Microsoft :: Windows
21 | Operating System :: OS Independent
22 | Operating System :: POSIX
23 | Operating System :: Unix
24 | Programming Language :: Python :: 3.9
25 | Programming Language :: Python :: 3.10
26 | Programming Language :: Python :: 3.11
27 |
28 | [options]
29 | python_requires = >=3.9, <4
30 | install_requires =
31 | h5py>=3.0
32 | NumPy>=1.21
33 | QtPy>=1.9
34 | packages = find_namespace:
35 | include_package_data = True
36 |
37 | [options.packages.find]
38 | include = guidata*
39 |
40 | [options.extras_require]
41 | dev =
42 | ruff
43 | pylint
44 | Coverage
45 | doc =
46 | PyQt5
47 | pillow
48 | pandas
49 | sphinx
50 | sphinx-copybutton
51 | sphinx_qt_documentation
52 | python-docs-theme
53 | test =
54 | pytest
55 | pytest-cov
56 | pytest-qt
57 | pytest-xvfb
58 |
--------------------------------------------------------------------------------
/guidata/tests/dataset/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/guidata/tests/dataset/__init__.py
--------------------------------------------------------------------------------
/guidata/tests/dataset/test_activable_dataset.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # Licensed under the terms of the BSD 3-Clause
4 | # (see guidata/LICENSE for details)
5 |
6 | """
7 | ActivableDataSet example
8 |
9 | Warning: ActivableDataSet objects were made to be integrated inside GUI layouts.
10 | So this example with dialog boxes may be confusing.
11 | --> see tests/editgroupbox.py to understand the activable dataset usage
12 | """
13 |
14 | # When editing, all items are shown.
15 | # When showing dataset in read-only mode (e.g. inside another layout), all items
16 | # are shown except the enable item.
17 |
18 | import guidata.dataset as gds
19 | from guidata.env import execenv
20 | from guidata.qthelpers import qt_app_context
21 |
22 | # guitest: show
23 |
24 |
25 | class Parameters(gds.ActivableDataSet):
26 | """
27 | Example
28 | Activable dataset example
29 | """
30 |
31 | def __init__(self, title=None, comment=None, icon=""):
32 | gds.ActivableDataSet.__init__(self, title, comment, icon)
33 |
34 | enable = gds.BoolItem(
35 | "Enable parameter set",
36 | help="If disabled, the following parameters will be ignored",
37 | default=False,
38 | )
39 | param0 = gds.ChoiceItem("Param 0", ["choice #1", "choice #2", "choice #3"])
40 | param1 = gds.FloatItem("Param 1", default=0, min=0)
41 | param2 = gds.FloatItem("Param 2", default=0.93)
42 | color = gds.ColorItem("Color", default="red")
43 |
44 |
45 | Parameters.active_setup()
46 |
47 |
48 | def test_activable_dataset():
49 | """Test activable dataset"""
50 | with qt_app_context():
51 | prm = Parameters()
52 | prm.set_activable(True)
53 | prm.edit()
54 | prm.set_activable(False)
55 | prm.edit()
56 | prm.set_readonly()
57 | prm.edit()
58 | prm.set_readonly(False)
59 | prm.edit()
60 | execenv.print("OK")
61 |
62 |
63 | if __name__ == "__main__":
64 | test_activable_dataset()
65 |
--------------------------------------------------------------------------------
/guidata/tests/dataset/test_activable_items.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # Licensed under the terms of the BSD 3-Clause
4 | # (see guidata/LICENSE for details)
5 |
6 | """
7 | Example with activable items: items which active state is changed depending
8 | on another item's value.
9 | """
10 |
11 | # guitest: show
12 |
13 | from guidata.dataset import ChoiceItem, DataSet, FloatItem, FuncProp, GetAttrProp
14 | from guidata.env import execenv
15 | from guidata.qthelpers import qt_app_context
16 |
17 | choices = (("A", "Choice #1: A"), ("B", "Choice #2: B"), ("C", "Choice #3: C"))
18 |
19 |
20 | class Parameters(DataSet):
21 | """Example dataset"""
22 |
23 | _prop = GetAttrProp("choice")
24 | choice = ChoiceItem("Choice", choices).set_prop("display", store=_prop)
25 | x1 = FloatItem("x1")
26 | x2 = FloatItem("x2").set_prop("display", active=FuncProp(_prop, lambda x: x == "B"))
27 | x3 = FloatItem("x3").set_prop("display", active=FuncProp(_prop, lambda x: x == "C"))
28 |
29 |
30 | def test_activable_items():
31 | """Test activable items"""
32 | with qt_app_context():
33 | test = Parameters()
34 | test.edit()
35 | execenv.print("OK")
36 |
37 |
38 | if __name__ == "__main__":
39 | test_activable_items()
40 |
--------------------------------------------------------------------------------
/guidata/tests/dataset/test_all_items.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # Licensed under the terms of the BSD 3-Clause
4 | # (see guidata/LICENSE for details)
5 |
6 | """
7 | All guidata DataItem objects demo
8 |
9 | A DataSet object is a set of parameters of various types (integer, float,
10 | boolean, string, etc.) which may be edited in a dialog box thanks to the
11 | 'edit' method. Parameters are defined by assigning DataItem objects to a
12 | DataSet class definition: each parameter type has its own DataItem class
13 | (IntItem for integers, FloatItem for floats, StringItem for strings, etc.)
14 | """
15 |
16 | # guitest: show
17 |
18 | import atexit
19 | import datetime
20 | import shutil
21 | import tempfile
22 |
23 | import numpy as np
24 |
25 | import guidata.dataset as gds
26 | from guidata.env import execenv
27 | from guidata.qthelpers import qt_app_context
28 |
29 | # Creating temporary files and registering cleanup functions
30 | TEMPDIR = tempfile.mkdtemp(prefix="test_")
31 | atexit.register(shutil.rmtree, TEMPDIR)
32 | FILE_ETA = tempfile.NamedTemporaryFile(suffix=".eta", dir=TEMPDIR)
33 | atexit.register(FILE_ETA.close)
34 | FILE_CSV = tempfile.NamedTemporaryFile(suffix=".csv", dir=TEMPDIR)
35 | atexit.register(FILE_CSV.close)
36 |
37 |
38 | class Parameters(gds.DataSet):
39 | """
40 | DataSet test
41 | The following text is the DataSet 'comment':
Plain text or
42 | rich text2 are both supported,
43 | as well as special characters (α, β, γ, δ, ...)
44 | """
45 |
46 | dir = gds.DirectoryItem("Directory", TEMPDIR)
47 | fname = gds.FileOpenItem("Open file", ("csv", "eta"), FILE_CSV.name)
48 | fnames = gds.FilesOpenItem("Open files", "csv", FILE_CSV.name)
49 | fname_s = gds.FileSaveItem("Save file", "eta", FILE_ETA.name)
50 | string = gds.StringItem("String")
51 | text = gds.TextItem("Text")
52 | float_slider = gds.FloatItem(
53 | "Float (with slider)", default=0.5, min=0, max=1, step=0.01, slider=True
54 | )
55 | integer = gds.IntItem("Integer", default=5, min=3, max=16, slider=True).set_pos(
56 | col=1
57 | )
58 | dtime = gds.DateTimeItem("Date/time", default=datetime.datetime(2010, 10, 10))
59 | date = gds.DateItem("Date", default=datetime.date(2010, 10, 10)).set_pos(col=1)
60 | bool1 = gds.BoolItem("Boolean option without label")
61 | bool2 = gds.BoolItem("Boolean option with label", "Label")
62 | _bg = gds.BeginGroup("A sub group")
63 | color = gds.ColorItem("Color", default="red")
64 | choice = gds.ChoiceItem(
65 | "Single choice 1",
66 | [("16", "first choice"), ("32", "second choice"), ("64", "third choice")],
67 | )
68 | mchoice2 = gds.ImageChoiceItem(
69 | "Single choice 2",
70 | [
71 | ("rect", "first choice", "gif.png"),
72 | ("ell", "second choice", "txt.png"),
73 | ("qcq", "third choice", "file.png"),
74 | ],
75 | )
76 | _eg = gds.EndGroup("A sub group")
77 | floatarray = gds.FloatArrayItem(
78 | "Float array", default=np.ones((50, 5), float), format=" %.2e "
79 | ).set_pos(col=1)
80 | mchoice3 = gds.MultipleChoiceItem(
81 | "MC type 1", [str(i) for i in range(12)]
82 | ).horizontal(4)
83 | mchoice1 = (
84 | gds.MultipleChoiceItem(
85 | "MC type 2", ["first choice", "second choice", "third choice"]
86 | )
87 | .vertical(1)
88 | .set_pos(col=1)
89 | )
90 | dictionary = gds.DictItem(
91 | "Dictionary",
92 | default={
93 | "lkl": 2,
94 | "tototo": 3,
95 | "zzzz": "lklk",
96 | "bool": True,
97 | "float": 1.234,
98 | "list": [1, 2.5, 3, "str", False, 5, {"lkl": 2, "l": [1, 2, 3]}],
99 | },
100 | help="This is a dictionary",
101 | )
102 |
103 |
104 | def test_all_items():
105 | """Test all DataItem objects"""
106 | with qt_app_context():
107 | prm = Parameters()
108 |
109 | prm.floatarray[:, 0] = np.linspace(-5, 5, 50)
110 | execenv.print(prm)
111 | if prm.edit():
112 | execenv.print(prm)
113 | prm.view()
114 | execenv.print("OK")
115 |
116 |
117 | if __name__ == "__main__":
118 | test_all_items()
119 |
--------------------------------------------------------------------------------
/guidata/tests/dataset/test_all_items_readonly.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # Licensed under the terms of the BSD 3-Clause
4 | # (see guidata/LICENSE for details)
5 |
6 | """
7 | Same as test_all_items.py but with readonly=True to check that the readonly mode works
8 | on all DataItem types.
9 | """
10 |
11 | # guitest: show
12 |
13 |
14 | import numpy as np
15 |
16 | from guidata.env import execenv
17 | from guidata.qthelpers import qt_app_context
18 | from guidata.tests.dataset.test_all_items import Parameters
19 |
20 | # check if array variable_size is ignored thanks to readonly
21 | Parameters.floatarray.set_prop("edit", variable_size=True)
22 |
23 |
24 | def test_all_features():
25 | """Test all guidata item/group features"""
26 | with qt_app_context():
27 | prm1 = Parameters(readonly=True)
28 | prm1.floatarray[:, 0] = np.linspace(-5, 5, 50)
29 | execenv.print(prm1)
30 |
31 | if prm1.edit():
32 | prm1.edit()
33 | execenv.print(prm1)
34 | prm1.view()
35 |
36 | prm2 = Parameters.create(integer=10101010, string="Using create class method")
37 | print(prm2)
38 |
39 | execenv.print("OK")
40 |
41 |
42 | if __name__ == "__main__":
43 | test_all_features()
44 |
--------------------------------------------------------------------------------
/guidata/tests/dataset/test_bool_selector.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # Licensed under the terms of the BSD 3-Clause
4 | # (see guidata/LICENSE for details)
5 |
6 | """
7 | DataItem groups and group selection
8 |
9 | DataSet items may be included in groups (these items are then shown in group
10 | box widgets in the editing dialog box) and item groups may be enabled/disabled
11 | using one group parameter (a boolean item).
12 | """
13 |
14 | # guitest: show
15 |
16 | import guidata.dataset as gds
17 | from guidata.env import execenv
18 | from guidata.qthelpers import qt_app_context
19 |
20 | prop1 = gds.ValueProp(False)
21 | prop2 = gds.ValueProp(False)
22 |
23 |
24 | class Parameters(gds.DataSet):
25 | """
26 | Group selection test
27 | Group selection example:
28 | """
29 |
30 | g1 = gds.BeginGroup("group 1")
31 | enable1 = gds.BoolItem(
32 | "Enable parameter set #1",
33 | help="If disabled, the following parameters will be ignored",
34 | default=False,
35 | ).set_prop("display", store=prop1)
36 | prm11 = gds.FloatItem("Prm 1.1", default=0, min=0).set_prop("display", active=prop1)
37 | prm12 = gds.FloatItem("Prm 1.2", default=0.93).set_prop("display", active=prop1)
38 | _g1 = gds.EndGroup("group 1")
39 | g2 = gds.BeginGroup("group 2")
40 | enable2 = gds.BoolItem(
41 | "Enable parameter set #2",
42 | help="If disabled, the following parameters will be ignored",
43 | default=True,
44 | ).set_prop("display", store=prop2)
45 | prm21 = gds.FloatItem("Prm 2.1", default=0, min=0).set_prop("display", active=prop2)
46 | prm22 = gds.FloatItem("Prm 2.2", default=0.93).set_prop("display", active=prop2)
47 | _g2 = gds.EndGroup("group 2")
48 |
49 |
50 | def test_bool_selector():
51 | """Test bool selector"""
52 | with qt_app_context():
53 | prm = Parameters()
54 | prm.edit()
55 | execenv.print(prm)
56 | execenv.print("OK")
57 |
58 |
59 | if __name__ == "__main__":
60 | test_bool_selector()
61 |
--------------------------------------------------------------------------------
/guidata/tests/dataset/test_callbacks.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # Licensed under the terms of the BSD 3-Clause
4 | # (see guidata/LICENSE for details)
5 |
6 | """
7 | Demonstrates how items may trigger callbacks when activated, or how
8 | the list of choices may be dynamically changed.
9 | """
10 |
11 | # guitest: show
12 |
13 | import guidata.dataset as gds
14 | from guidata.env import execenv
15 | from guidata.qthelpers import qt_app_context
16 |
17 |
18 | class Parameters(gds.DataSet):
19 | """Example dataset"""
20 |
21 | def cb_example(self, item, value):
22 | execenv.print("\nitem: ", item, "\nvalue:", value)
23 | if self.results is None:
24 | self.results = ""
25 | self.results += str(value) + "\n"
26 | execenv.print("results:", self.results)
27 |
28 | def update_x1plusx2(self, item, value):
29 | execenv.print("\nitem: ", item, "\nvalue:", value)
30 | if self.x1 is not None and self.x2 is not None:
31 | self.x1plusx2 = self.x1 + self.x2
32 | else:
33 | self.x1plusx2 = None
34 |
35 | string = gds.StringItem("String", default="foobar").set_prop(
36 | "display", callback=cb_example
37 | )
38 | x1 = gds.FloatItem("x1").set_prop("display", callback=update_x1plusx2)
39 | x2 = gds.FloatItem("x2").set_prop("display", callback=update_x1plusx2)
40 | x1plusx2 = gds.FloatItem("x1+x2").set_prop("display", active=False)
41 | boolean = gds.BoolItem("Boolean", default=True).set_prop(
42 | "display", callback=cb_example
43 | )
44 | color = gds.ColorItem("Color", default="red").set_prop(
45 | "display", callback=cb_example
46 | )
47 |
48 | def choices_callback(self, item, value):
49 | """Choices callback: this demonstrates how to dynamically change
50 | the list of choices... even if it is not very useful in this case
51 |
52 | Note that `None` is systematically added as the third element of
53 | the returned tuples: that is to ensure the compatibility between
54 | `ChoiceItem` and `ImageChoiceItem` (see `guidata.dataset.dataitems`)
55 | """
56 | execenv.print(f"[choices_callback]: item={item}, value={value}")
57 | return [
58 | (16, "first choice", None),
59 | (32, "second choice", None),
60 | (64, "third choice", None),
61 | ]
62 |
63 | choice = (
64 | gds.ChoiceItem(
65 | "Single choice",
66 | choices_callback,
67 | default=64,
68 | )
69 | .set_pos(col=1, colspan=2)
70 | .set_prop("display", callback=cb_example)
71 | )
72 | results = gds.TextItem("Results").set_prop("display", callback=cb_example)
73 |
74 |
75 | def test_callbacks():
76 | """Test callbacks"""
77 | with qt_app_context():
78 | prm = Parameters()
79 | execenv.print(prm)
80 | if prm.edit():
81 | execenv.print(prm)
82 | prm.view()
83 | execenv.print("OK")
84 |
85 |
86 | if __name__ == "__main__":
87 | test_callbacks()
88 |
--------------------------------------------------------------------------------
/guidata/tests/dataset/test_datasetgroup.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # Licensed under the terms of the BSD 3-Clause
4 | # (see guidata/LICENSE for details)
5 |
6 | """
7 | DataSetGroup demo
8 |
9 | DataSet objects may be grouped into DataSetGroup, allowing them to be edited
10 | in a single dialog box (with one tab per DataSet object). This code tests both the
11 | normal dataset group mode and the table mode (with one tab per DataSet object).
12 | """
13 |
14 | # guitest: show
15 |
16 | from guidata.dataset import DataSetGroup
17 | from guidata.env import execenv
18 | from guidata.qthelpers import qt_app_context
19 | from guidata.tests.dataset.test_all_features import Parameters
20 |
21 |
22 | def test_dataset_group():
23 | """Test DataSetGroup"""
24 | with qt_app_context():
25 | e1 = Parameters("DataSet #1")
26 | e2 = Parameters("DataSet #2")
27 |
28 | g = DataSetGroup([e1, e2], title="Parameters group")
29 | g.edit()
30 | execenv.print(e1)
31 | g.edit()
32 | execenv.print("OK")
33 |
34 | g = DataSetGroup([e1, e2], title="Parameters group in table mode")
35 | g.edit(mode="table")
36 | execenv.print(e1)
37 | g.edit()
38 | execenv.print("OK")
39 |
40 | g.edit()
41 | execenv.print(e1)
42 | g.edit()
43 | execenv.print("OK")
44 |
45 |
46 | if __name__ == "__main__":
47 | test_dataset_group()
48 |
--------------------------------------------------------------------------------
/guidata/tests/dataset/test_inheritance.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # Licensed under the terms of the BSD 3-Clause
4 | # (see guidata/LICENSE for details)
5 |
6 | """
7 | DataSet objects inheritance test
8 |
9 | From time to time, it may be useful to derive a DataSet from another. The main
10 | application is to extend a parameter set with additionnal parameters.
11 | """
12 |
13 | # guitest: show
14 |
15 | import guidata.dataset as gds
16 | from guidata.env import execenv
17 | from guidata.qthelpers import qt_app_context
18 |
19 |
20 | class OriginalDataset(gds.DataSet):
21 | """Original dataset
22 | This is the original dataset"""
23 |
24 | bool = gds.BoolItem("Boolean")
25 | string = gds.StringItem("String")
26 | text = gds.TextItem("Text")
27 | float = gds.FloatItem("Float", default=0.5, min=0, max=1, step=0.01, slider=True)
28 |
29 |
30 | class DerivedDataset(OriginalDataset):
31 | """Derived dataset
32 | This is the derived dataset"""
33 |
34 | bool = gds.BoolItem("Boolean (modified in derived dataset)")
35 | a = gds.FloatItem("Level 1 (added in derived dataset)", default=0)
36 | b = gds.FloatItem("Level 2 (added in derived dataset)", default=0)
37 | c = gds.FloatItem("Level 3 (added in derived dataset)", default=0)
38 |
39 |
40 | def test_inheritance():
41 | """Test DataSet inheritance"""
42 | with qt_app_context():
43 | e = OriginalDataset()
44 | e.edit()
45 | execenv.print(e)
46 |
47 | e = DerivedDataset()
48 | e.edit()
49 | execenv.print(e)
50 | execenv.print("OK")
51 |
52 |
53 | if __name__ == "__main__":
54 | test_inheritance()
55 |
--------------------------------------------------------------------------------
/guidata/tests/dataset/test_item_order.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # Licensed under the terms of the BSD 3-Clause
4 | # (see guidata/LICENSE for details)
5 |
6 | """
7 | DataSet item order test
8 |
9 | From time to time, it may be useful to change the item order,
10 | for example when deriving a dataset from another.
11 | """
12 |
13 | # guitest: show
14 |
15 | import guidata.dataset as gds
16 | from guidata.env import execenv
17 | from guidata.qthelpers import qt_app_context
18 |
19 |
20 | class OriginalDataset(gds.DataSet):
21 | """Original dataset
22 | This is the original dataset"""
23 |
24 | param1 = gds.BoolItem("P1 | Boolean")
25 | param2 = gds.StringItem("P2 | String")
26 | param3 = gds.TextItem("P3 | Text")
27 | param4 = gds.FloatItem("P4 | Float", default=0)
28 |
29 |
30 | class DerivedDataset(OriginalDataset):
31 | """Derived dataset
32 | This is the derived dataset, with modified item order"""
33 |
34 | param5 = gds.IntItem("P5 | Int", default=0).set_pos(row=2)
35 | param6 = gds.DateItem("P6 | Date", default=0).set_pos(row=4)
36 |
37 |
38 | def test_item_order():
39 | """Test DataSet item order"""
40 | with qt_app_context():
41 | e = OriginalDataset()
42 | e.edit()
43 | execenv.print(e)
44 |
45 | e = DerivedDataset()
46 | e.edit()
47 | execenv.print(e)
48 | execenv.print("OK")
49 |
50 |
51 | if __name__ == "__main__":
52 | test_item_order()
53 |
--------------------------------------------------------------------------------
/guidata/tests/dataset/test_loadsave_hdf5.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # Licensed under the terms of the BSD 3-Clause
4 | # (see guidata/LICENSE for details)
5 |
6 | """
7 | HDF5 I/O demo
8 |
9 | DataSet objects may be saved in HDF5 files, a universal hierarchical dataset
10 | file type. This script shows how to save in and then reload data from a HDF5
11 | file.
12 | """
13 |
14 | # guitest: show
15 |
16 | import os
17 |
18 | from guidata.env import execenv
19 | from guidata.io import HDF5Reader, HDF5Writer
20 | from guidata.qthelpers import qt_app_context
21 | from guidata.tests.dataset.test_all_items import Parameters
22 |
23 |
24 | def test_loadsave_hdf5():
25 | """Test HDF5 I/O"""
26 | fname = "test.h5"
27 | with qt_app_context():
28 | if os.path.exists(fname):
29 | os.unlink(fname)
30 |
31 | e = Parameters()
32 | if execenv.unattended or e.edit():
33 | writer = HDF5Writer(fname)
34 | e.serialize(writer)
35 | writer.close()
36 |
37 | e = Parameters()
38 | reader = HDF5Reader(fname)
39 | e.deserialize(reader)
40 | reader.close()
41 | e.edit()
42 | os.unlink(fname)
43 | execenv.print("OK")
44 |
45 |
46 | if __name__ == "__main__":
47 | test_loadsave_hdf5()
48 |
--------------------------------------------------------------------------------
/guidata/tests/dataset/test_loadsave_json.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # Licensed under the terms of the BSD 3-Clause
4 | # (see guidata/LICENSE for details)
5 |
6 | """
7 | JSON I/O demo
8 |
9 | DataSet objects may be saved in JSON files.
10 | This script shows how to save in and then reload data from a JSON file.
11 | """
12 |
13 | # guitest: show
14 |
15 | import os
16 |
17 | from guidata.env import execenv
18 | from guidata.io import JSONReader, JSONWriter
19 | from guidata.qthelpers import qt_app_context
20 | from guidata.tests.dataset.test_all_items import Parameters
21 |
22 |
23 | def test_loadsave_json():
24 | """Test JSON I/O"""
25 | fname = "test.json"
26 | with qt_app_context():
27 | if os.path.exists(fname):
28 | os.unlink(fname)
29 |
30 | e = Parameters()
31 | if execenv.unattended or e.edit():
32 | writer = JSONWriter(fname)
33 | e.serialize(writer)
34 | writer.save()
35 |
36 | e = Parameters()
37 | reader = JSONReader(fname)
38 | e.deserialize(reader)
39 | e.edit()
40 | os.unlink(fname)
41 | execenv.print("OK")
42 |
43 |
44 | if __name__ == "__main__":
45 | test_loadsave_json()
46 |
--------------------------------------------------------------------------------
/guidata/tests/dataset/test_rotatedlabel.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # Licensed under the terms of the BSD 3-Clause
4 | # (see guidata/LICENSE for details)
5 |
6 | """
7 | RotatedLabel test
8 |
9 | RotatedLabel is derived from QLabel: it provides rotated text display.
10 | """
11 |
12 | # guitest: show
13 |
14 | from qtpy.QtCore import Qt
15 | from qtpy.QtWidgets import QFrame, QGridLayout
16 |
17 | from guidata.env import execenv
18 | from guidata.qthelpers import qt_app_context, win32_fix_title_bar_background
19 | from guidata.widgets.rotatedlabel import RotatedLabel
20 |
21 |
22 | class Frame(QFrame):
23 | """Test frame"""
24 |
25 | def __init__(self, parent=None):
26 | QFrame.__init__(self, parent)
27 | win32_fix_title_bar_background(self)
28 | layout = QGridLayout()
29 | self.setLayout(layout)
30 | angle = 0
31 | for row in range(7):
32 | for column in range(7):
33 | layout.addWidget(
34 | RotatedLabel(
35 | "Label %03d°" % angle, angle=angle, color=Qt.blue, bold=True
36 | ),
37 | row,
38 | column,
39 | Qt.AlignCenter,
40 | )
41 | angle += 10
42 |
43 |
44 | def test_rotatedlabel():
45 | """Test RotatedLabel"""
46 | with qt_app_context(exec_loop=True):
47 | frame = Frame()
48 | frame.show()
49 | execenv.print("OK")
50 |
51 |
52 | if __name__ == "__main__":
53 | test_rotatedlabel()
54 |
--------------------------------------------------------------------------------
/guidata/tests/unit/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/guidata/tests/unit/__init__.py
--------------------------------------------------------------------------------
/guidata/tests/unit/test_config.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # Licensed under the terms of the BSD 3-Clause
4 | # (see guidata/LICENSE for details)
5 |
6 | """Config test"""
7 |
8 | import pytest
9 |
10 | from guidata.config import UserConfig
11 | from guidata.tests.dataset.test_all_features import Parameters
12 |
13 |
14 | @pytest.fixture()
15 | def config():
16 | """Create a config object"""
17 | CONF = UserConfig({})
18 | eta = Parameters()
19 | eta.write_config(CONF, "TestParameters", "")
20 | yield CONF
21 |
22 |
23 | def test_load(config):
24 | """Test load config"""
25 | eta = Parameters()
26 | eta.read_config(config, "TestParameters", "")
27 |
28 |
29 | def test_default(config):
30 | """Test default config"""
31 | eta = Parameters()
32 | eta.write_config(config, "etagere2", "")
33 | eta = Parameters()
34 | eta.read_config(config, "etagere2", "")
35 |
36 |
37 | def test_restore(config):
38 | """Test restore config"""
39 | eta = Parameters()
40 | eta.fl2 = 2
41 | eta.integer = 6
42 | eta.write_config(config, "etagere3", "")
43 |
44 | eta = Parameters()
45 | eta.read_config(config, "etagere3", "")
46 |
47 | assert eta.fl2 == 2.0
48 | assert eta.integer == 6
49 |
--------------------------------------------------------------------------------
/guidata/tests/unit/test_data.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # Licensed under the terms of the BSD 3-Clause
4 | # (see guidata/LICENSE for details)
5 |
6 | """Unit tests"""
7 |
8 |
9 | import unittest
10 |
11 | import guidata.dataset as gds
12 | from guidata.dataset.conv import update_dataset
13 | from guidata.env import execenv
14 |
15 |
16 | class Parameters(gds.DataSet):
17 | """Example dataset"""
18 |
19 | float1 = gds.FloatItem("float #1", min=1, max=250, help="height in cm")
20 | float2 = gds.FloatItem("float #2", min=1, max=250, help="width in cm")
21 | number = gds.IntItem("number", min=3, max=20)
22 |
23 |
24 | class TestCheck(unittest.TestCase):
25 | def test_range(self):
26 | """Test range checking of FloatItem"""
27 | e = Parameters()
28 | e.float1 = 150.0
29 | e.float2 = 400.0
30 | e.number = 4
31 | errors = e.check()
32 | self.assertEqual(errors, ["float2"])
33 |
34 | def test_typechecking(self):
35 | """Test type checking of FloatItem"""
36 | e = Parameters()
37 | e.float1 = 150
38 | e.float2 = 400
39 | e.number = 4.0
40 | errors = e.check()
41 | self.assertEqual(errors, ["float1", "float2", "number"])
42 |
43 | def test_update(self):
44 | e1 = Parameters()
45 | e2 = Parameters()
46 | e1.float1 = 23
47 | update_dataset(e2, e1)
48 | self.assertEqual(e2.float1, 23)
49 |
50 |
51 | if __name__ == "__main__":
52 | unittest.main()
53 | execenv.print("OK")
54 |
--------------------------------------------------------------------------------
/guidata/tests/unit/test_dataset_from_dict.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # Licensed under the terms of the BSD 3-Clause
4 | # (see guidata/LICENSE for details)
5 |
6 | """
7 | Generate a dataset class from a dictionary
8 | """
9 |
10 | # guitest: show
11 |
12 | import numpy as np
13 |
14 | from guidata.dataset import create_dataset_from_dict
15 | from guidata.env import execenv
16 |
17 | TEST_DICT1 = {
18 | "a": 1,
19 | "b": 2.0,
20 | "c": "test",
21 | "d": {"x": 1, "y": 3},
22 | "data": np.array([1, 2, 3]),
23 | }
24 | TEST_DICT2 = {
25 | "a": 1,
26 | "unsupported": [2.0, 3.0],
27 | }
28 |
29 |
30 | def test_dataset_from_dict():
31 | """Test generate dataset class from a dictionary"""
32 | for dictionary in (TEST_DICT1,):
33 | execenv.print(dictionary)
34 | dataset = create_dataset_from_dict(dictionary)
35 | execenv.print(dataset)
36 | execenv.print(dataset.create())
37 | execenv.print("")
38 | try:
39 | create_dataset_from_dict(TEST_DICT2)
40 | assert False, "Should have raised a ValueError"
41 | except ValueError:
42 | # This is expected
43 | pass
44 |
45 |
46 | if __name__ == "__main__":
47 | test_dataset_from_dict()
48 |
--------------------------------------------------------------------------------
/guidata/tests/unit/test_dataset_from_func.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # Licensed under the terms of the BSD 3-Clause
4 | # (see guidata/LICENSE for details)
5 |
6 | """
7 | Generate a dataset class from a function signature
8 |
9 | This function is used to generate a dataset from a function signature which has
10 | type annotations. See the example below.
11 | """
12 |
13 | # guitest: show
14 |
15 | from __future__ import annotations
16 |
17 | import numpy as np
18 |
19 | from guidata.dataset import create_dataset_from_func
20 | from guidata.env import execenv
21 |
22 |
23 | def func_ok(
24 | a: int, b: float, c: str = "test", d: dict[str, int] = {"x": 1, "y": 3}
25 | ) -> None:
26 | """A function with type annotations"""
27 | pass
28 |
29 |
30 | def func_no_type(a, b, c="test"):
31 | """A function without type annotations"""
32 | pass
33 |
34 |
35 | def func_no_default(a: int, b: float, c: str, data: np.ndarray) -> None:
36 | """A function without default values"""
37 | pass
38 |
39 |
40 | def test_dataset_from_func():
41 | """Test generate dataset class from function"""
42 | for func in (func_ok, func_no_default):
43 | execenv.print(func.__name__)
44 | dataset = create_dataset_from_func(func)
45 | execenv.print(dataset)
46 | execenv.print(dataset.create(a=1, b=2.0))
47 | execenv.print("")
48 | func = func_no_type
49 | try:
50 | create_dataset_from_func(func)
51 | assert False, "Should have raised a ValueError"
52 | except ValueError:
53 | # This is expected
54 | pass
55 |
56 |
57 | if __name__ == "__main__":
58 | test_dataset_from_func()
59 |
--------------------------------------------------------------------------------
/guidata/tests/unit/test_genreqs.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # Licensed under the terms of the BSD 3-Clause
4 | # (see guidata/LICENSE for details)
5 |
6 | """Generate install requirements RST table."""
7 |
8 | import pytest
9 |
10 | from guidata.tests import get_path
11 | from guidata.utils import genreqs
12 |
13 | GR_PATH = get_path("genreqs")
14 |
15 |
16 | def test_compare_cfg_toml():
17 | """Compare requirements generated from setup.cfg and pyproject.toml."""
18 | req_toml = genreqs.extract_requirements_from_toml(GR_PATH)
19 | req_cfg = genreqs.extract_requirements_from_cfg(GR_PATH)
20 | assert req_toml == req_cfg
21 |
22 |
23 | @pytest.mark.skip(reason="This test should be run manually (development only)")
24 | def test_generate_requirement_tables():
25 | """Test generate_requirement_tables."""
26 | genreqs.gen_path_req_rst(GR_PATH, "guidata", ["Python>=3.9", "PyQt>=5.11"], GR_PATH)
27 |
28 |
29 | if __name__ == "__main__":
30 | test_compare_cfg_toml()
31 | test_generate_requirement_tables()
32 |
--------------------------------------------------------------------------------
/guidata/tests/unit/test_no_qt.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # Licensed under the terms of the BSD 3-Clause
4 | # (see guidata/LICENSE for details)
5 |
6 | """
7 | Test if some guidata features work without Qt
8 | (and they should!)
9 | """
10 |
11 | import os
12 |
13 |
14 | def test_imports_without_qt():
15 | """Test if some guidata features work without Qt"""
16 | os.environ["QT_API"] = "invalid_value" # Invalid Qt API
17 | try:
18 | # pylint: disable=unused-import
19 | # pylint: disable=import-outside-toplevel
20 | import guidata.dataset.dataitems # noqa: F401
21 | import guidata.dataset.datatypes # noqa: F401
22 | except ValueError as exc:
23 | raise AssertionError("guidata imports failed without Qt") from exc
24 |
25 |
26 | if __name__ == "__main__":
27 | test_imports_without_qt()
28 |
--------------------------------------------------------------------------------
/guidata/tests/unit/test_text.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # Licensed under the terms of the BSD 3-Clause
4 | # (see guidata/LICENSE for details)
5 |
6 | """Test in text mode"""
7 |
8 |
9 | import pytest
10 |
11 | import guidata.dataset as gds
12 | from guidata.env import execenv
13 |
14 |
15 | class Parameters(gds.DataSet):
16 | """Example dataset"""
17 |
18 | height = gds.FloatItem("Height", min=1, max=250, help="height in cm")
19 | width = gds.FloatItem("Width", min=1, max=250, help="width in cm")
20 | number = gds.IntItem("Number", min=3, max=20)
21 |
22 |
23 | @pytest.mark.skip(reason="interactive text mode: not suitable for automated testing")
24 | def test_text():
25 | """Test text mode"""
26 | prm = Parameters()
27 | prm.text_edit()
28 | execenv.print(prm)
29 | execenv.print("OK")
30 |
31 |
32 | if __name__ == "__main__":
33 | test_text()
34 |
--------------------------------------------------------------------------------
/guidata/tests/unit/test_translations.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # Licensed under the terms of the BSD 3-Clause
4 | # (see guidata/LICENSE for details)
5 |
6 | """Little translation test"""
7 |
8 | # guitest: show
9 |
10 | from guidata.config import _
11 | from guidata.env import execenv
12 |
13 | translations = (_("Some required entries are incorrect"),)
14 |
15 |
16 | def test_translations():
17 | """Test translations"""
18 | for text in translations:
19 | execenv.print(text)
20 | execenv.print("OK")
21 |
22 |
23 | if __name__ == "__main__":
24 | test_translations()
25 |
--------------------------------------------------------------------------------
/guidata/tests/unit/test_updaterestoredataset.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # Licensed under the terms of the BSD 3-Clause
4 | # (see guidata/LICENSE for details)
5 |
6 | """
7 | Update/Restore dataset from/to another dataset or dictionary
8 | """
9 |
10 | # guitest: show
11 |
12 | import numpy as np
13 |
14 | from guidata.dataset.conv import restore_dataset, update_dataset
15 | from guidata.tests.dataset.test_all_items import Parameters
16 |
17 |
18 | def test_update_restore_dataset():
19 | """Test update/restore dataset from/to another dataset or dictionary"""
20 | dataset = Parameters()
21 | dsdict = {
22 | "integer": 1,
23 | "float_slider": 1.0,
24 | "bool1": True,
25 | "string": "test",
26 | "floatarray": np.array([1, 2, 3]),
27 | "dictionary": {"a": 1, "b": 2},
28 | }
29 | # Update dataset from dictionary
30 | update_dataset(dataset, dsdict)
31 | # Check dataset values
32 | assert dataset.integer == dsdict["integer"]
33 | assert dataset.float_slider == dsdict["float_slider"]
34 | assert dataset.bool1 == dsdict["bool1"]
35 | assert dataset.string == dsdict["string"]
36 | assert np.all(dataset.floatarray == dsdict["floatarray"])
37 | assert dataset.dictionary == dsdict["dictionary"]
38 | # Update dataset from another dataset
39 | dataset2 = Parameters()
40 | update_dataset(dataset2, dataset)
41 | # Check dataset values
42 | assert dataset2.integer == dataset.integer
43 | assert dataset2.float_slider == dataset.float_slider
44 | assert dataset2.bool1 == dataset.bool1
45 | assert dataset2.string == dataset.string
46 | assert np.all(dataset2.floatarray == dataset.floatarray)
47 | assert dataset2.dictionary == dataset.dictionary
48 | # Restore dataset from dictionary
49 | restore_dataset(dataset, dsdict)
50 | # Check dataset values
51 | assert dataset.integer == dsdict["integer"]
52 | assert dataset.float_slider == dsdict["float_slider"]
53 | assert dataset.bool1 == dsdict["bool1"]
54 | assert dataset.string == dsdict["string"]
55 | assert np.all(dataset.floatarray == dsdict["floatarray"])
56 | assert dataset.dictionary == dsdict["dictionary"]
57 |
58 |
59 | if __name__ == "__main__":
60 | test_update_restore_dataset()
61 |
--------------------------------------------------------------------------------
/guidata/tests/unit/test_userconfig_app.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """
4 | userconfig
5 |
6 | Application settings example
7 |
8 | This should create a file named ".app.ini" in your HOME directory containing:
9 |
10 | [main]
11 | version = 1.0.0
12 |
13 | [a]
14 | b/f = 1.0
15 | """
16 |
17 | import guidata.dataset as gds
18 | from guidata import userconfig
19 | from guidata.env import execenv
20 |
21 |
22 | class DS(gds.DataSet):
23 | """Example dataset"""
24 |
25 | f = gds.FloatItem("F", 1.0)
26 |
27 |
28 | def test_userconfig_app():
29 | """Test userconfig"""
30 | ds = DS("")
31 | uc = userconfig.UserConfig({})
32 | uc.set_application("app", "1.0.0")
33 | ds.write_config(uc, "a", "b")
34 |
35 | print("Settings saved in: ", uc.filename())
36 | execenv.print("OK")
37 |
38 |
39 | if __name__ == "__main__":
40 | test_userconfig_app()
41 |
--------------------------------------------------------------------------------
/guidata/tests/widgets/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PlotPyStack/guidata/ce320fdabfc3888c867c8716dda127a7dbd65bbe/guidata/tests/widgets/__init__.py
--------------------------------------------------------------------------------
/guidata/tests/widgets/test_arrayeditor.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # Copyright © Spyder Project Contributors
4 | # Licensed under the terms of the MIT License
5 |
6 |
7 | """
8 | Tests for arrayeditor.py
9 | """
10 |
11 | # guitest: show
12 |
13 | import numpy as np
14 |
15 | from guidata.env import execenv
16 | from guidata.qthelpers import exec_dialog, qt_app_context
17 | from guidata.widgets.arrayeditor import ArrayEditor
18 |
19 |
20 | def launch_arrayeditor(data, title="", xlabels=None, ylabels=None, variable_size=False):
21 | """Helper routine to launch an arrayeditor and return its result"""
22 | dlg = ArrayEditor()
23 | dlg.setup_and_check(
24 | data, title, xlabels=xlabels, ylabels=ylabels, variable_size=variable_size
25 | )
26 | exec_dialog(dlg)
27 | return dlg.get_value()
28 |
29 |
30 | def test_arrayeditor():
31 | """Test array editor for all supported data types"""
32 | with qt_app_context():
33 | # Variable size version
34 | for title, data in (
35 | ("string array", np.array(["kjrekrjkejr"])),
36 | (
37 | "masked array",
38 | np.ma.array([[1, 0], [1, 0]], mask=[[True, False], [False, False]]),
39 | ),
40 | ("int array", np.array([1, 2, 3], dtype="int8")),
41 | ):
42 | launch_arrayeditor(data, "[Variable size] " + title, variable_size=True)
43 | for title, data in (
44 | ("string array", np.array(["kjrekrjkejr"])),
45 | ("unicode array", np.array(["ñññéáíó"])),
46 | (
47 | "masked array",
48 | np.ma.array([[1, 0], [1, 0]], mask=[[True, False], [False, False]]),
49 | ),
50 | (
51 | "record array",
52 | np.zeros(
53 | (2, 2),
54 | {
55 | "names": ("red", "green", "blue"),
56 | "formats": (np.float32, np.float32, np.float32),
57 | },
58 | ),
59 | ),
60 | (
61 | "record array with titles",
62 | np.array(
63 | [(0, 0.0), (0, 0.0), (0, 0.0)],
64 | dtype=[(("title 1", "x"), "|i1"), (("title 2", "y"), ">f4")],
65 | ),
66 | ),
67 | ("bool array", np.array([True, False, True])),
68 | ("int array", np.array([1, 2, 3], dtype="int8")),
69 | ("float16 array", np.zeros((5, 5), dtype=np.float16)),
70 | ):
71 | launch_arrayeditor(data, title)
72 | for title, data, xlabels, ylabels in (
73 | ("float array", np.random.rand(5, 5), ["a", "b", "c", "d", "e"], None),
74 | (
75 | "complex array",
76 | np.round(np.random.rand(5, 5) * 10)
77 | + np.round(np.random.rand(5, 5) * 10) * 1j,
78 | np.linspace(-12, 12, 5),
79 | np.linspace(-12, 12, 5),
80 | ),
81 | ):
82 | launch_arrayeditor(data, title, xlabels, ylabels)
83 |
84 | arr = np.zeros((3, 3, 4))
85 | arr[0, 0, 0] = 1
86 | arr[0, 0, 1] = 2
87 | arr[0, 0, 2] = 3
88 | launch_arrayeditor(arr, "3D array")
89 |
90 | execenv.print("OK")
91 |
92 |
93 | if __name__ == "__main__":
94 | test_arrayeditor()
95 |
--------------------------------------------------------------------------------
/guidata/tests/widgets/test_codeeditor.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # Copyright © Spyder Project Contributors
4 | # Licensed under the terms of the MIT License
5 |
6 |
7 | """
8 | Tests for codeeditor.py
9 | """
10 |
11 | # guitest: show
12 |
13 | from guidata.configtools import get_icon
14 | from guidata.env import execenv
15 | from guidata.qthelpers import qt_app_context
16 | from guidata.widgets import codeeditor
17 |
18 |
19 | def test_codeeditor():
20 | """Test Code editor."""
21 | with qt_app_context(exec_loop=True):
22 | widget = codeeditor.CodeEditor(language="python")
23 | widget.set_text_from_file(codeeditor.__file__)
24 | widget.resize(800, 600)
25 | widget.setWindowTitle("Code editor")
26 | widget.setWindowIcon(get_icon("guidata.svg"))
27 | widget.show()
28 | execenv.print("OK")
29 |
30 |
31 | if __name__ == "__main__":
32 | test_codeeditor()
33 |
--------------------------------------------------------------------------------
/guidata/tests/widgets/test_collectionseditor.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # Copyright © Spyder Project Contributors
4 | # Licensed under the terms of the MIT License
5 |
6 |
7 | """
8 | Tests for collectionseditor.py
9 | """
10 |
11 | # guitest: show
12 |
13 | import datetime
14 |
15 | import numpy as np
16 |
17 | from guidata.env import execenv
18 | from guidata.qthelpers import qt_app_context
19 | from guidata.widgets.collectionseditor import CollectionsEditor
20 |
21 | try:
22 | from PIL import Image as PILImage
23 | except ImportError:
24 | # PIL is not installed
25 | PILImage = None
26 |
27 |
28 | def get_test_data():
29 | """Create test data."""
30 | testdict = {"d": 1, "a": np.random.rand(10, 10), "b": [1, 2]}
31 | testdate = datetime.date(1945, 5, 8)
32 | test_timedelta = datetime.timedelta(days=-1, minutes=42, seconds=13)
33 |
34 | try:
35 | import pandas as pd
36 | except (ModuleNotFoundError, ImportError):
37 | test_timestamp = None
38 | test_pd_td = None
39 | test_dtindex = None
40 | test_series = None
41 | test_df = None
42 | else:
43 | test_timestamp = pd.Timestamp("1945-05-08T23:01:00.12345")
44 | test_pd_td = pd.Timedelta(days=2193, hours=12)
45 | test_dtindex = pd.date_range(start="1939-09-01T", end="1939-10-06", freq="12h")
46 | test_series = pd.Series({"series_name": [0, 1, 2, 3, 4, 5]})
47 | test_df = pd.DataFrame(
48 | {
49 | "string_col": ["a", "b", "c", "d"],
50 | "int_col": [0, 1, 2, 3],
51 | "float_col": [1.1, 2.2, 3.3, 4.4],
52 | "bool_col": [True, False, False, True],
53 | }
54 | )
55 |
56 | class Foobar:
57 | """ """
58 |
59 | def __init__(self):
60 | self.text = "toto"
61 | self.testdict = testdict
62 | self.testdate = testdate
63 |
64 | foobar = Foobar()
65 | test_data = {
66 | "object": foobar,
67 | "module": np,
68 | "str": "kjkj kj k j j kj k jkj",
69 | "unicode": "éù",
70 | "list": [1, 3, [sorted, 5, 6], "kjkj", None],
71 | "tuple": ([1, testdate, testdict, test_timedelta], "kjkj", None),
72 | "dict": testdict,
73 | "float": 1.2233,
74 | "int": 223,
75 | "bool": True,
76 | "array": np.random.rand(10, 10).astype(np.int64),
77 | "masked_array": np.ma.array(
78 | [[1, 0], [1, 0]], mask=[[True, False], [False, False]]
79 | ),
80 | "1D-array": np.linspace(-10, 10).astype(np.float16),
81 | "3D-array": np.random.randint(2, size=(5, 5, 5)).astype(np.bool_),
82 | "empty_array": np.array([]),
83 | "date": testdate,
84 | "datetime": datetime.datetime(1945, 5, 8, 23, 1, 0, int(1.5e5)),
85 | "timedelta": test_timedelta,
86 | "complex": 2 + 1j,
87 | "complex64": np.complex64(2 + 1j),
88 | "complex128": np.complex128(9j),
89 | "int8_scalar": np.int8(8),
90 | "int16_scalar": np.int16(16),
91 | "int32_scalar": np.int32(32),
92 | "int64_scalar": np.int64(64),
93 | "float16_scalar": np.float16(16),
94 | "float32_scalar": np.float32(32),
95 | "float64_scalar": np.float64(64),
96 | "bool_scalar": bool,
97 | "bool__scalar": np.bool_(8),
98 | "timestamp": test_timestamp,
99 | "timedelta_pd": test_pd_td,
100 | "datetimeindex": test_dtindex,
101 | "series": test_series,
102 | "ddataframe": test_df,
103 | "None": None,
104 | "unsupported1": np.arccos,
105 | # Test for Issue #3518
106 | "big_struct_array": np.zeros(
107 | 1000, dtype=[("ID", "f8"), ("param1", "f8", 5000)]
108 | ),
109 | }
110 | if PILImage is not None:
111 | image = PILImage.fromarray(np.random.randint(256, size=(100, 100)), mode="P")
112 | test_data["image"] = image
113 | return test_data
114 |
115 |
116 | def test_collectionseditor():
117 | """Test Collections editor."""
118 | with qt_app_context(exec_loop=True):
119 | dialog = CollectionsEditor()
120 | dialog.setup(get_test_data())
121 | dialog.show()
122 | execenv.print("OK")
123 |
124 |
125 | if __name__ == "__main__":
126 | test_collectionseditor()
127 |
--------------------------------------------------------------------------------
/guidata/tests/widgets/test_console.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # Copyright © Spyder Project Contributors
4 | # Licensed under the terms of the MIT License
5 |
6 |
7 | """
8 | Tests for codeeditor.py
9 | """
10 |
11 | # guitest: show
12 |
13 | from guidata.configtools import get_icon
14 | from guidata.env import execenv
15 | from guidata.qthelpers import qt_app_context
16 | from guidata.widgets.console import Console
17 |
18 |
19 | def test_console():
20 | """Test Console widget."""
21 | with qt_app_context(exec_loop=True):
22 | widget = Console(debug=False, multithreaded=True)
23 | widget.resize(800, 600)
24 | widget.setWindowTitle("Console")
25 | widget.setWindowIcon(get_icon("guidata.svg"))
26 | widget.show()
27 | execenv.print("OK")
28 |
29 |
30 | if __name__ == "__main__":
31 | test_console()
32 |
--------------------------------------------------------------------------------
/guidata/tests/widgets/test_dataframeeditor.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # Copyright © Spyder Project Contributors
4 | # Licensed under the terms of the MIT License
5 |
6 |
7 | """
8 | Tests for dataframeeditor.py
9 | """
10 |
11 | # guitest: show
12 |
13 | import numpy as np
14 | import pytest
15 |
16 | try:
17 | import pandas as pd
18 | import pandas.testing as pdt
19 |
20 | from guidata.widgets.dataframeeditor import DataFrameEditor
21 | except ImportError:
22 | # pandas is not installed
23 | pd = pdt = DataFrameEditor = None
24 |
25 | from guidata.env import execenv
26 | from guidata.qthelpers import exec_dialog, qt_app_context
27 |
28 |
29 | @pytest.mark.skipif(pd is None, reason="pandas is not installed")
30 | def test_dataframeeditor():
31 | """DataFrame editor test"""
32 |
33 | def test_edit(data, title="", parent=None):
34 | """Test subroutine"""
35 | dlg = DataFrameEditor(parent=parent)
36 |
37 | if dlg.setup_and_check(data, title=title):
38 | exec_dialog(dlg)
39 | return dlg.get_value()
40 | else:
41 | import sys
42 |
43 | sys.exit(1)
44 |
45 | with qt_app_context():
46 | df1 = pd.DataFrame(
47 | [
48 | [True, "bool"],
49 | [1 + 1j, "complex"],
50 | ["test", "string"],
51 | [1.11, "float"],
52 | [1, "int"],
53 | [np.random.rand(3, 3), "Unkown type"],
54 | ["Large value", 100],
55 | ["áéí", "unicode"],
56 | ],
57 | index=["a", "b", np.nan, np.nan, np.nan, "c", "Test global max", "d"],
58 | columns=[np.nan, "Type"],
59 | )
60 | out = test_edit(df1)
61 | pdt.assert_frame_equal(df1, out)
62 |
63 | result = pd.Series([True, "bool"], index=[np.nan, "Type"], name="a")
64 | out = test_edit(df1.iloc[0])
65 | pdt.assert_series_equal(result, out)
66 |
67 | df1 = pd.DataFrame(np.random.rand(100100, 10))
68 | out = test_edit(df1)
69 | pdt.assert_frame_equal(out, df1)
70 |
71 | series = pd.Series(np.arange(10), name=0)
72 | out = test_edit(series)
73 | pdt.assert_series_equal(series, out)
74 |
75 | execenv.print("OK")
76 |
77 |
78 | if __name__ == "__main__":
79 | test_dataframeeditor()
80 |
--------------------------------------------------------------------------------
/guidata/tests/widgets/test_importwizard.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # Copyright © Spyder Project Contributors
4 | # Licensed under the terms of the MIT License
5 |
6 |
7 | """
8 | Tests for importwizard.py
9 | """
10 |
11 | # guitest: show
12 |
13 | import pytest
14 |
15 | from guidata.env import execenv
16 | from guidata.qthelpers import exec_dialog, qt_app_context
17 | from guidata.widgets.importwizard import ImportWizard
18 |
19 |
20 | @pytest.fixture()
21 | def text():
22 | """Return text to test"""
23 | return "17/11/1976\t1.34\n14/05/09\t3.14"
24 |
25 |
26 | def test_importwizard(text):
27 | """Test"""
28 | with qt_app_context():
29 | dialog = ImportWizard(None, text)
30 | if exec_dialog(dialog):
31 | execenv.print(dialog.get_data())
32 | execenv.print("OK")
33 |
34 |
35 | if __name__ == "__main__":
36 | test_importwizard("17/11/1976\t1.34\n14/05/09\t3.14")
37 |
--------------------------------------------------------------------------------
/guidata/tests/widgets/test_objecteditor.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # Copyright © Spyder Project Contributors
4 | # Licensed under the terms of the MIT License
5 | """
6 | Tests for objecteditor.py
7 | """
8 |
9 | # guitest: show
10 |
11 | import datetime
12 |
13 | import numpy as np
14 |
15 | try:
16 | from PIL import Image
17 | except ImportError:
18 | # PIL is not installed
19 | Image = None
20 |
21 | from guidata.env import execenv
22 | from guidata.qthelpers import qt_app_context
23 | from guidata.widgets.objecteditor import oedit
24 |
25 |
26 | def test_objecteditor():
27 | """Run object editor test"""
28 | with qt_app_context():
29 | example = {
30 | "str": "kjkj kj k j j kj k jkj",
31 | "list": [1, 3, 4, "kjkj", None],
32 | "dict": {"d": 1, "a": np.random.rand(10, 10), "b": [1, 2]},
33 | "float": 1.2233,
34 | "array": np.random.rand(10, 10),
35 | "date": datetime.date(1945, 5, 8),
36 | "datetime": datetime.datetime(1945, 5, 8),
37 | }
38 | if Image is not None:
39 | data = np.random.randint(255, size=(100, 100)).astype("uint8")
40 | image = Image.fromarray(data)
41 | example["image"] = image
42 | image = oedit(image)
43 |
44 | class Foobar:
45 | """ """
46 |
47 | def __init__(self):
48 | self.text = "toto"
49 |
50 | foobar = Foobar()
51 | execenv.print(oedit(foobar))
52 | execenv.print(oedit(example))
53 | execenv.print(oedit(np.random.rand(10, 10)))
54 | execenv.print(oedit(oedit.__doc__))
55 | execenv.print(example)
56 | execenv.print("OK")
57 |
58 |
59 | if __name__ == "__main__":
60 | test_objecteditor()
61 |
--------------------------------------------------------------------------------
/guidata/tests/widgets/test_theme.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # Licensed under the terms of the BSD 3-Clause
4 | # (see guidata/LICENSE for details)
5 |
6 | """
7 | Test dark/light theme switching
8 | """
9 |
10 | from __future__ import annotations
11 |
12 | import os
13 | import sys
14 | from typing import Literal
15 |
16 | import pytest
17 | from qtpy import QtCore as QC
18 | from qtpy import QtWidgets as QW
19 |
20 | from guidata import qthelpers as qth
21 | from guidata.widgets.codeeditor import CodeEditor
22 | from guidata.widgets.console import Console
23 |
24 |
25 | class BaseColorModeWidget(QW.QWidget):
26 | """Base class for testing dark/light theme switching"""
27 |
28 | SIZE = (1200, 600)
29 |
30 | def __init__(self, default: Literal["light", "dark", "auto"] = qth.AUTO) -> None:
31 | super(BaseColorModeWidget, self).__init__()
32 | self.resize(*self.SIZE)
33 | self.default_theme = default
34 | self.combo: QW.QComboBox | None = None
35 | self.setWindowTitle(self.__doc__)
36 | self.grid_layout = QW.QGridLayout()
37 | self.setLayout(self.grid_layout)
38 | self.setup_widgets()
39 | if default != qth.AUTO:
40 | self.change_color_mode(default)
41 |
42 | def setup_widgets(self):
43 | """Setup widgets"""
44 | label = QW.QLabel("Select color mode:")
45 | self.combo = QW.QComboBox()
46 | self.combo.addItems(qth.COLOR_MODES)
47 | self.combo.setCurrentText(self.default_theme)
48 | self.combo.currentTextChanged.connect(self.change_color_mode)
49 | self.combo.setSizePolicy(QW.QSizePolicy.Expanding, QW.QSizePolicy.Minimum)
50 | self.combo.setToolTip(
51 | "Select color mode:"
52 | "- auto: follow system settings
"
53 | "- light: use light theme
"
54 | "- dark: use dark theme
"
55 | )
56 | hlayout = QW.QHBoxLayout()
57 | hlayout.addWidget(label)
58 | hlayout.addWidget(self.combo)
59 | self.grid_layout.addLayout(hlayout, 0, 0, 1, -1)
60 |
61 | def change_color_mode(self, mode: str) -> None:
62 | """Change color mode"""
63 | qth.set_color_mode(mode)
64 |
65 | def closeEvent(self, event):
66 | """Close event"""
67 | self.console.close()
68 | event.accept()
69 |
70 |
71 | class ColorModeWidget(BaseColorModeWidget):
72 | """Testing color mode switching for guidata's widgets: code editor and console"""
73 |
74 | def __init__(self, default: Literal["light", "dark", "auto"] = qth.AUTO) -> None:
75 | self.editor: CodeEditor | None = None
76 | self.console: Console | None = None
77 | super().__init__(default)
78 | qth.win32_fix_title_bar_background(self)
79 |
80 | def setup_widgets(self):
81 | """Setup widgets"""
82 | super().setup_widgets()
83 | self.editor = CodeEditor(self)
84 | self.console = Console(self, debug=False)
85 | for widget in (self.editor, self.console):
86 | widget.setSizePolicy(QW.QSizePolicy.Expanding, QW.QSizePolicy.Expanding)
87 | self.editor.setPlainText("Change theme using the combo box above" + os.linesep)
88 | self.add_info_to_codeeditor()
89 | self.console.execute_command("print('Console output')")
90 | self.grid_layout.addWidget(self.editor, 1, 0)
91 | self.grid_layout.addWidget(self.console, 1, 1)
92 |
93 | def add_info_to_codeeditor(self):
94 | """Add current color mode and theme to the code editor, with a prefix with
95 | date and time"""
96 | self.editor.setPlainText(
97 | os.linesep.join(
98 | [
99 | self.editor.toPlainText(),
100 | "",
101 | f"{QC.QDateTime.currentDateTime().toString()}:",
102 | f" Current color mode: {qth.get_color_mode()}",
103 | f" Current theme: {qth.get_color_theme()}",
104 | ]
105 | )
106 | )
107 |
108 | def change_color_mode(self, mode: str) -> None:
109 | """Change color mode"""
110 | super().change_color_mode(mode)
111 | for widget in (self.editor, self.console):
112 | widget.update_color_mode()
113 | self.add_info_to_codeeditor()
114 |
115 |
116 | @pytest.mark.skipif(reason="Not suitable for automated testing")
117 | def test_dark_light_themes(
118 | default: Literal["light", "dark", "auto"] | None = None,
119 | ) -> None:
120 | """Test dark/light theme switching"""
121 | with qth.qt_app_context(exec_loop=True):
122 | widget = ColorModeWidget(default=qth.AUTO if default is None else default)
123 | widget.show()
124 |
125 |
126 | if __name__ == "__main__":
127 | test_dark_light_themes(None if len(sys.argv) < 2 else sys.argv[1])
128 |
--------------------------------------------------------------------------------
/guidata/utils/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
--------------------------------------------------------------------------------
/guidata/utils/gettext_helpers.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # Licensed under the terms of the BSD 3-Clause
4 | # (see guidata/LICENSE for details)
5 |
6 | import os
7 | import os.path as osp
8 | import subprocess
9 | import sys
10 |
11 | if os.name == "nt":
12 | # Find pygettext.py source on a windows install
13 | pygettext = ["python", osp.join(sys.prefix, "Tools", "i18n", "pygettext.py")]
14 | msgfmt = ["python", osp.join(sys.prefix, "Tools", "i18n", "msgfmt.py")]
15 | else:
16 | pygettext = ["pygettext"]
17 | msgfmt = ["msgfmt"]
18 |
19 |
20 | def get_files(modname):
21 | if not osp.isdir(modname):
22 | return [modname]
23 | files = []
24 | for dirname, _dirnames, filenames in os.walk(modname):
25 | files += [
26 | osp.join(dirname, f)
27 | for f in filenames
28 | if f.endswith(".py") or f.endswith(".pyw")
29 | ]
30 | for dirname, _dirnames, filenames in os.walk("tests"):
31 | files += [
32 | osp.join(dirname, f)
33 | for f in filenames
34 | if f.endswith(".py") or f.endswith(".pyw")
35 | ]
36 | return files
37 |
38 |
39 | def get_lang(modname):
40 | localedir = osp.join(modname, "locale")
41 | for _dirname, dirnames, _filenames in os.walk(localedir):
42 | break # we just want the list of first level directories
43 | return dirnames
44 |
45 |
46 | def do_rescan(modname):
47 | files = get_files(modname)
48 | dirname = modname
49 | do_rescan_files(files, modname, dirname)
50 |
51 |
52 | def do_rescan_files(files, modname, dirname):
53 | localedir = osp.join(dirname, "locale")
54 | potfile = modname + ".pot"
55 | subprocess.call(
56 | pygettext
57 | + [
58 | ##"-D", # Extract docstrings
59 | "-o",
60 | potfile, # Nom du fichier pot
61 | "-p",
62 | localedir, # dest
63 | "--no-location",
64 | ]
65 | + files
66 | )
67 | for lang in get_lang(dirname):
68 | pofilepath = osp.join(localedir, lang, "LC_MESSAGES", modname + ".po")
69 | potfilepath = osp.join(localedir, potfile)
70 | print("Updating...", pofilepath)
71 | if not osp.exists(osp.join(localedir, lang, "LC_MESSAGES")):
72 | os.mkdir(osp.join(localedir, lang, "LC_MESSAGES"))
73 | if not osp.exists(pofilepath):
74 | outf = open(pofilepath, "w")
75 | outf.write("# -*- coding: utf-8 -*-\n")
76 | data = open(potfilepath).read()
77 | data = data.replace("charset=CHARSET", "charset=utf-8")
78 | data = data.replace(
79 | "Content-Transfer-Encoding: ENCODING",
80 | "Content-Transfer-Encoding: utf-8",
81 | )
82 | outf.write(data)
83 | else:
84 | print("merge...")
85 | subprocess.call(["msgmerge", "--update", pofilepath, potfilepath])
86 |
87 |
88 | def do_compile(modname, dirname=None):
89 | if dirname is None:
90 | dirname = modname
91 | localedir = osp.join(dirname, "locale")
92 | for lang in get_lang(dirname):
93 | pofilepath = osp.join(localedir, lang, "LC_MESSAGES", modname + ".po")
94 | subprocess.call(msgfmt + [pofilepath])
95 |
96 |
97 | def main(modname):
98 | if len(sys.argv) < 2:
99 | cmd = "help"
100 | else:
101 | cmd = sys.argv[1]
102 | # lang = get_lang( modname )
103 | if cmd == "help":
104 | print("Available commands:")
105 | print(" help : this message")
106 | print(" help_gettext : pygettext --help")
107 | print(" help_msgfmt : msgfmt --help")
108 | print(" scan : rescan .py files and updates existing .po files")
109 | print(" compile : recompile .po files")
110 | print()
111 | print("Pour fonctionner ce programme doit être lancé depuis")
112 | print("la racine du module")
113 | print("Traductions disponibles:")
114 | for i in get_lang(modname):
115 | print(i)
116 | elif cmd == "help_gettext":
117 | subprocess.call(pygettext + ["--help"])
118 | elif cmd == "help_msgfmt":
119 | subprocess.call(msgfmt + ["--help"])
120 | elif cmd == "scan":
121 | print("Updating pot files")
122 | do_rescan(modname)
123 | elif cmd == "compile":
124 | print("Builtin .mo files")
125 | do_compile(modname)
126 |
--------------------------------------------------------------------------------
/guidata/widgets/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # Licensed under the terms of the BSD 3-Clause
4 | # (see guidata/LICENSE for details)
5 |
6 | """
7 | Ready-to-use Qt widgets
8 | -----------------------
9 |
10 | Data editors
11 | ^^^^^^^^^^^^
12 |
13 | .. autoclass:: guidata.widgets.arrayeditor.ArrayEditor
14 |
15 | .. autoclass:: guidata.widgets.collectionseditor.CollectionsEditor
16 |
17 | .. autoclass:: guidata.widgets.dataframeeditor.DataFrameEditor
18 |
19 | .. autoclass:: guidata.widgets.texteditor.TextEditor
20 |
21 | .. autofunction:: guidata.widgets.objecteditor.oedit
22 |
23 | Console and code editor
24 | ^^^^^^^^^^^^^^^^^^^^^^^
25 |
26 | .. autoclass:: guidata.widgets.console.Console
27 |
28 | .. autoclass:: guidata.widgets.console.DockableConsole
29 |
30 | .. autoclass:: guidata.widgets.codeeditor.CodeEditor
31 |
32 | """
33 |
--------------------------------------------------------------------------------
/guidata/widgets/arrayeditor/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # Licensed under the terms of the BSD 3-Clause
4 | # (see guidata/LICENSE for details)
5 | #
6 | # The array editor subpackage was derived from Spyder's arrayeditor.py module
7 | # which is licensed under the terms of the MIT License (see spyder/__init__.py
8 | # for details), copyright © Spyder Project Contributors
9 |
10 | """guidata.widgets.arrayeditor
11 | ===========================
12 |
13 | This package provides a NumPy Array Editor Dialog based on Qt.
14 |
15 | .. autoclass:: ArrayEditor
16 | :show-inheritance:
17 | :members:
18 |
19 | """
20 |
21 | from .arrayeditor import ArrayEditor # noqa: F401
22 |
--------------------------------------------------------------------------------
/guidata/widgets/arrayeditor/utils.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # Licensed under the terms of the BSD 3-Clause
4 | # (see guidata/LICENSE for details)
5 | #
6 | # The array editor subpackage was derived from Spyder's arrayeditor.py module
7 | # which is licensed under the terms of the MIT License (see spyder/__init__.py
8 | # for details), copyright © Spyder Project Contributors
9 |
10 | # Note: string and unicode data types will be formatted with '%s' (see below)
11 |
12 | """Basic utilitarian functions and variables for the various array editor classes"""
13 |
14 | import numpy as np
15 |
16 | SUPPORTED_FORMATS = {
17 | "single": "%.6g",
18 | "double": "%.6g",
19 | "float_": "%.6g",
20 | "longfloat": "%.6g",
21 | "float16": "%.6g",
22 | "float32": "%.6g",
23 | "float64": "%.6g",
24 | "float96": "%.6g",
25 | "float128": "%.6g",
26 | "csingle": "%r",
27 | "complex_": "%r",
28 | "clongfloat": "%r",
29 | "complex64": "%r",
30 | "complex128": "%r",
31 | "complex192": "%r",
32 | "complex256": "%r",
33 | "byte": "%d",
34 | "bytes8": "%s",
35 | "short": "%d",
36 | "intc": "%d",
37 | "int_": "%d",
38 | "longlong": "%d",
39 | "intp": "%d",
40 | "int8": "%d",
41 | "int16": "%d",
42 | "int32": "%d",
43 | "int64": "%d",
44 | "ubyte": "%d",
45 | "ushort": "%d",
46 | "uintc": "%d",
47 | "uint": "%d",
48 | "ulonglong": "%d",
49 | "uintp": "%d",
50 | "uint8": "%d",
51 | "uint16": "%d",
52 | "uint32": "%d",
53 | "uint64": "%d",
54 | "bool_": "%r",
55 | "bool8": "%r",
56 | "bool": "%r",
57 | }
58 |
59 |
60 | LARGE_SIZE = 5e5
61 | LARGE_NROWS = 1e5
62 | LARGE_COLS = 60
63 |
64 |
65 | # ==============================================================================
66 | # Utility functions
67 | # ==============================================================================
68 | def is_float(dtype: np.dtype) -> bool:
69 | """Return True if datatype dtype is a float kind
70 |
71 | Args:
72 | dtype: numpy datatype
73 |
74 | Returns:
75 | True if dtype is a float kind
76 | """
77 | return ("float" in dtype.name) or dtype.name in ["single", "double"]
78 |
79 |
80 | def is_number(dtype: np.dtype) -> bool:
81 | """Return True is datatype dtype is a number kind
82 |
83 | Args:
84 | dtype: numpy datatype
85 |
86 | Returns:
87 | True if dtype is a number kind
88 | """
89 | return (
90 | is_float(dtype)
91 | or ("int" in dtype.name)
92 | or ("long" in dtype.name)
93 | or ("short" in dtype.name)
94 | )
95 |
96 |
97 | def get_idx_rect(index_list: list) -> tuple:
98 | """Extract the boundaries from a list of indexes
99 |
100 | Args:
101 | index_list: list of indexes
102 |
103 | Returns:
104 | tuple: (min_row, max_row, min_col, max_col)
105 | """
106 | rows, cols = list(zip(*[(i.row(), i.column()) for i in index_list]))
107 | return (min(rows), max(rows), min(cols), max(cols))
108 |
--------------------------------------------------------------------------------
/guidata/widgets/console/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """
4 | guidata.widgets.console
5 | =======================
6 |
7 | This package provides a Python console widget.
8 |
9 | .. autoclass:: Console
10 | :show-inheritance:
11 | :members:
12 |
13 | .. autoclass:: DockableConsole
14 | :show-inheritance:
15 | :members:
16 |
17 | """
18 |
19 | from qtpy.QtCore import Qt
20 |
21 | from guidata.config import CONF
22 | from guidata.configtools import get_font
23 | from guidata.qthelpers import win32_fix_title_bar_background
24 | from guidata.widgets.console.internalshell import InternalShell
25 | from guidata.widgets.dockable import DockableWidgetMixin
26 |
27 |
28 | class Console(InternalShell):
29 | """
30 | Python console that run an interactive shell linked to
31 | the running process.
32 |
33 | :param parent: parent Qt widget
34 | :param namespace: available python namespace when the console start
35 | :type namespace: dict
36 | :param message: banner displayed before the first prompt
37 | :param commands: commands run when the interpreter starts
38 | :param type commands: list of string
39 | :param multithreaded: multithreaded support
40 | """
41 |
42 | def __init__(
43 | self,
44 | parent=None,
45 | namespace=None,
46 | message=None,
47 | commands=None,
48 | multithreaded=True,
49 | debug=False,
50 | ):
51 | InternalShell.__init__(
52 | self,
53 | parent=parent,
54 | namespace=namespace,
55 | message=message,
56 | commands=commands or [],
57 | multithreaded=multithreaded,
58 | debug=debug,
59 | )
60 | win32_fix_title_bar_background(self)
61 | self.setup()
62 |
63 | def setup(self):
64 | """Setup the calltip widget and show the console once all
65 | internal handler are ready."""
66 | font = get_font(CONF, "console")
67 | font.setPointSize(10)
68 | self.set_font(font)
69 | self.set_codecompletion_auto(True)
70 | self.set_calltips(True)
71 | self.setup_completion(size=(300, 180), font=font)
72 | try:
73 | self.exception_occurred.connect(self.show_console)
74 | except AttributeError:
75 | pass
76 |
77 | def closeEvent(self, event):
78 | """Reimplement Qt base method"""
79 | InternalShell.closeEvent(self, event)
80 | self.exit_interpreter()
81 | event.accept()
82 |
83 |
84 | class DockableConsole(Console, DockableWidgetMixin):
85 | """
86 | Dockable Python console that run an interactive shell linked to
87 | the running process.
88 |
89 | :param parent: parent Qt widget
90 | :param namespace: available python namespace when the console start
91 | :type namespace: dict
92 | :param message: banner displayed before the first prompt
93 | :param commands: commands run when the interpreter starts
94 | :param type commands: list of string
95 | """
96 |
97 | LOCATION = Qt.BottomDockWidgetArea
98 |
99 | def __init__(
100 | self, parent, namespace, message, commands=None, multithreaded=True, debug=False
101 | ):
102 | DockableWidgetMixin.__init__(self)
103 | Console.__init__(
104 | self,
105 | parent=parent,
106 | namespace=namespace,
107 | message=message,
108 | commands=commands or [],
109 | multithreaded=multithreaded,
110 | debug=debug,
111 | )
112 |
113 | def show_console(self):
114 | """Show the console widget."""
115 | self.dockwidget.raise_()
116 | self.dockwidget.show()
117 |
--------------------------------------------------------------------------------
/guidata/widgets/console/terminal.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # Copyright © Spyder Project Contributors
4 | # Licensed under the terms of the MIT License
5 | # (see spyder/__init__.py for details)
6 |
7 | """Terminal emulation tools"""
8 |
9 | import os
10 |
11 |
12 | class ANSIEscapeCodeHandler(object):
13 | """ANSI Escape sequences handler"""
14 |
15 | if os.name == "nt":
16 | # Windows terminal colors:
17 | ANSI_COLORS = ( # Normal, Bright/Light
18 | ("#000000", "#808080"), # 0: black
19 | ("#800000", "#ff0000"), # 1: red
20 | ("#008000", "#00ff00"), # 2: green
21 | ("#808000", "#ffff00"), # 3: yellow
22 | ("#000080", "#0000ff"), # 4: blue
23 | ("#800080", "#ff00ff"), # 5: magenta
24 | ("#008080", "#00ffff"), # 6: cyan
25 | ("#c0c0c0", "#ffffff"), # 7: white
26 | )
27 | elif os.name == "mac":
28 | # Terminal.app colors:
29 | ANSI_COLORS = ( # Normal, Bright/Light
30 | ("#000000", "#818383"), # 0: black
31 | ("#C23621", "#FC391F"), # 1: red
32 | ("#25BC24", "#25BC24"), # 2: green
33 | ("#ADAD27", "#EAEC23"), # 3: yellow
34 | ("#492EE1", "#5833FF"), # 4: blue
35 | ("#D338D3", "#F935F8"), # 5: magenta
36 | ("#33BBC8", "#14F0F0"), # 6: cyan
37 | ("#CBCCCD", "#E9EBEB"), # 7: white
38 | )
39 | else:
40 | # xterm colors:
41 | ANSI_COLORS = ( # Normal, Bright/Light
42 | ("#000000", "#7F7F7F"), # 0: black
43 | ("#CD0000", "#ff0000"), # 1: red
44 | ("#00CD00", "#00ff00"), # 2: green
45 | ("#CDCD00", "#ffff00"), # 3: yellow
46 | ("#0000EE", "#5C5CFF"), # 4: blue
47 | ("#CD00CD", "#ff00ff"), # 5: magenta
48 | ("#00CDCD", "#00ffff"), # 6: cyan
49 | ("#E5E5E5", "#ffffff"), # 7: white
50 | )
51 |
52 | def __init__(self):
53 | self.intensity = 0
54 | self.italic = None
55 | self.bold = None
56 | self.underline = None
57 | self.foreground_color = None
58 | self.background_color = None
59 | self.default_foreground_color = 30
60 | self.default_background_color = 47
61 |
62 | def set_code(self, code):
63 | """
64 |
65 | :param code:
66 | """
67 | assert isinstance(code, int)
68 | if code == 0:
69 | # Reset all settings
70 | self.reset()
71 | elif code == 1:
72 | # Text color intensity
73 | self.intensity = 1
74 | # The following line is commented because most terminals won't
75 | # change the font weight, against ANSI standard recommendation:
76 | # self.bold = True
77 | elif code == 3:
78 | # Italic on
79 | self.italic = True
80 | elif code == 4:
81 | # Underline simple
82 | self.underline = True
83 | elif code == 22:
84 | # Normal text color intensity
85 | self.intensity = 0
86 | self.bold = False
87 | elif code == 23:
88 | # No italic
89 | self.italic = False
90 | elif code == 24:
91 | # No underline
92 | self.underline = False
93 | elif code >= 30 and code <= 37:
94 | # Text color
95 | self.foreground_color = code
96 | elif code == 39:
97 | # Default text color
98 | self.foreground_color = self.default_foreground_color
99 | elif code >= 40 and code <= 47:
100 | # Background color
101 | self.background_color = code
102 | elif code == 49:
103 | # Default background color
104 | self.background_color = self.default_background_color
105 | self.set_style()
106 |
107 | def set_style(self):
108 | """
109 | Set font style with the following attributes:
110 | 'foreground_color', 'background_color', 'italic',
111 | 'bold' and 'underline'
112 | """
113 | raise NotImplementedError
114 |
115 | def reset(self):
116 | """
117 |
118 | """
119 | self.current_format = None
120 | self.intensity = 0
121 | self.italic = False
122 | self.bold = False
123 | self.underline = False
124 | self.foreground_color = None
125 | self.background_color = None
126 |
--------------------------------------------------------------------------------
/guidata/widgets/dockable.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # Licensed under the terms of the BSD 3-Clause
4 | # (see guidata/LICENSE for details)
5 |
6 | """
7 | dockable
8 | --------
9 |
10 | The `dockable` module provides a mixin class for widgets that can be docked
11 | into a QMainWindow.
12 | """
13 |
14 | from __future__ import annotations
15 |
16 | from qtpy.QtCore import Qt
17 | from qtpy.QtWidgets import QDockWidget, QWidget
18 |
19 |
20 | class DockableWidgetMixin:
21 | """Mixin class for widgets that can be docked into a QMainWindow"""
22 |
23 | ALLOWED_AREAS = Qt.AllDockWidgetAreas
24 | LOCATION = Qt.TopDockWidgetArea
25 | FEATURES = (
26 | QDockWidget.DockWidgetClosable
27 | | QDockWidget.DockWidgetFloatable
28 | | QDockWidget.DockWidgetMovable
29 | )
30 |
31 | def __init__(self):
32 | self._isvisible = False
33 | self.dockwidget: QDockWidget | None = None
34 | self._allowed_areas = self.ALLOWED_AREAS
35 | self._location = self.LOCATION
36 | self._features = self.FEATURES
37 |
38 | @property
39 | def parent_widget(self) -> QWidget | None:
40 | """Return associated QWidget parent"""
41 | return self.parent()
42 |
43 | def setup_dockwidget(
44 | self,
45 | location: Qt.DockWidgetArea | None = None,
46 | features: QDockWidget.DockWidgetFeatures | None = None,
47 | allowed_areas: Qt.DockWidgetAreas | None = None,
48 | ) -> None:
49 | """Setup dockwidget parameters
50 |
51 | Args:
52 | location (Qt.DockWidgetArea): Dockwidget location
53 | features (QDockWidget.DockWidgetFeatures): Dockwidget features
54 | allowed_areas (Qt.DockWidgetAreas): Dockwidget allowed areas
55 | """
56 | assert (
57 | self.dockwidget is None
58 | ), "Dockwidget must be setup before calling 'create_dockwidget'"
59 | if location is not None:
60 | self._location = location
61 | if features is not None:
62 | self._features = features
63 | if allowed_areas is not None:
64 | self._allowed_areas = allowed_areas
65 |
66 | def get_focus_widget(self) -> QWidget | None:
67 | """Return widget to focus when dockwidget is visible"""
68 | return None
69 |
70 | def create_dockwidget(self, title: str) -> tuple[QDockWidget, Qt.DockWidgetArea]:
71 | """Add to parent QMainWindow as a dock widget
72 |
73 | Args:
74 | title (str): Dockwidget title
75 |
76 | Returns:
77 | tuple[QDockWidget, Qt.DockWidgetArea]: Dockwidget and location
78 | """
79 | dock = QDockWidget(title, self.parent_widget)
80 | dock.setObjectName(self.__class__.__name__ + "_dw")
81 | dock.setAllowedAreas(self._allowed_areas)
82 | dock.setFeatures(self._features)
83 | dock.setWidget(self)
84 | dock.visibilityChanged.connect(self.visibility_changed)
85 | self.dockwidget = dock
86 | return (dock, self._location)
87 |
88 | def is_visible(self) -> bool:
89 | """Return dockwidget visibility state"""
90 | return self._isvisible
91 |
92 | def visibility_changed(self, enable: bool) -> None:
93 | """DockWidget visibility has changed
94 |
95 | Args:
96 | enable (bool): Dockwidget visibility state
97 | """
98 | if enable:
99 | self.dockwidget.raise_()
100 | widget = self.get_focus_widget() # pylint: disable=assignment-from-none
101 | if widget is not None:
102 | widget.setFocus()
103 | self._isvisible = enable and self.dockwidget.isVisible()
104 |
105 |
106 | class DockableWidget(QWidget, DockableWidgetMixin):
107 | """Dockable widget
108 |
109 | Args:
110 | parent (QWidget): Parent widget
111 | """
112 |
113 | def __init__(self, parent: QWidget):
114 | QWidget.__init__(self, parent)
115 | DockableWidgetMixin.__init__(self)
116 |
--------------------------------------------------------------------------------
/guidata/widgets/objecteditor.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # Copyright © Spyder Project Contributors
4 | # Licensed under the terms of the MIT License
5 | # (see spyder/__init__.py for details)
6 |
7 | """
8 | guidata.widgets.objecteditor
9 | ============================
10 |
11 | This package provides a generic object editor widget.
12 |
13 | .. autofunction:: oedit
14 |
15 | """
16 |
17 | from __future__ import annotations
18 |
19 | from typing import TYPE_CHECKING
20 |
21 | import numpy as np
22 |
23 | try:
24 | from PIL import Image as PILImage
25 | except ImportError:
26 | PILImage = None
27 |
28 | from guidata.qthelpers import exec_dialog
29 | from guidata.widgets.arrayeditor import ArrayEditor
30 | from guidata.widgets.collectionseditor import CollectionsEditor
31 | from guidata.widgets.nsview import DataFrame, FakeObject, Series, is_known_type
32 | from guidata.widgets.texteditor import TextEditor
33 |
34 | try:
35 | from guidata.widgets.dataframeeditor import DataFrameEditor
36 | except ImportError:
37 | DataFrameEditor = FakeObject()
38 |
39 |
40 | if TYPE_CHECKING:
41 | from qtpy import QtWidgets as QW
42 |
43 |
44 | def create_dialog(obj, title, parent=None):
45 | """Creates the editor dialog and returns a tuple (dialog, func) where func
46 | is the function to be called with the dialog instance as argument, after
47 | quitting the dialog box
48 |
49 | The role of this intermediate function is to allow easy monkey-patching.
50 | (uschmitt suggested this indirection here so that he can monkey patch
51 | oedit to show eMZed related data)
52 | """
53 |
54 | def conv_func(data):
55 | """Conversion function"""
56 | return data
57 |
58 | readonly = not is_known_type(obj)
59 | if isinstance(obj, np.ndarray):
60 | dialog = ArrayEditor(parent)
61 | if not dialog.setup_and_check(obj, title=title, readonly=readonly):
62 | return
63 | elif PILImage is not None and isinstance(obj, PILImage.Image):
64 | dialog = ArrayEditor(parent)
65 |
66 | data = np.array(obj)
67 | if not dialog.setup_and_check(data, title=title, readonly=readonly):
68 | return
69 |
70 | def conv_func(data): # pylint: disable=function-redefined
71 | """Conversion function"""
72 | return PILImage.fromarray(data, mode=obj.mode)
73 |
74 | elif isinstance(obj, (DataFrame, Series)) and DataFrame is not FakeObject:
75 | dialog = DataFrameEditor(parent)
76 | if not dialog.setup_and_check(obj):
77 | return
78 | elif isinstance(obj, str):
79 | dialog = TextEditor(obj, title=title, readonly=readonly, parent=parent)
80 | else:
81 | dialog = CollectionsEditor(parent)
82 | dialog.setup(obj, title=title, readonly=readonly)
83 |
84 | def end_func(dialog):
85 | """
86 |
87 | :param dialog:
88 | :return:
89 | """
90 | return conv_func(dialog.get_value())
91 |
92 | return dialog, end_func
93 |
94 |
95 | def oedit(
96 | obj: dict | list | tuple | str | np.ndarray,
97 | title: str = None,
98 | parent: QW.QWidget = None,
99 | ) -> dict | list | tuple | str | np.ndarray:
100 | """Edit the object 'obj' in a GUI-based editor and return the edited copy
101 | (if Cancel is pressed, return None)
102 |
103 | Args:
104 | obj (dict | list | tuple | str | np.ndarray): object to edit
105 | title (str): dialog title
106 | parent (QW.QWidget): parent widget
107 |
108 | Returns:
109 | dict | list | tuple | str | np.ndarray: edited object
110 | """
111 | title = "" if title is None else title
112 | result = create_dialog(obj, title, parent)
113 | if result is None:
114 | return
115 | dialog, end_func = result
116 | if exec_dialog(dialog):
117 | return end_func(dialog)
118 | return None
119 |
--------------------------------------------------------------------------------
/guidata/widgets/rotatedlabel.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # Licensed under the terms of the BSD 3-Clause
4 | # (see guidata/LICENSE for details)
5 |
6 | """
7 | rotatedlabel
8 | ------------
9 |
10 | The ``guidata.widgets.rotatedlabel`` module provides a widget for displaying
11 | rotated text.
12 | """
13 |
14 |
15 | from math import cos, pi, sin
16 |
17 | from qtpy.QtCore import QSize, Qt
18 | from qtpy.QtGui import QPainter, QPen
19 | from qtpy.QtWidgets import QLabel
20 |
21 | from guidata.configtools import get_family
22 |
23 |
24 | class RotatedLabel(QLabel):
25 | """
26 | Rotated QLabel
27 | (rich text is not supported)
28 | Arguments:
29 | * parent: parent widget
30 | * angle=270 (int): rotation angle in degrees
31 | * family (string): font family
32 | * bold (bool): font weight
33 | * italic (bool): font italic style
34 | * color (QColor): font color
35 | """
36 |
37 | def __init__(
38 | self,
39 | text,
40 | parent=None,
41 | angle=270,
42 | family=None,
43 | bold=False,
44 | italic=False,
45 | color=None,
46 | ):
47 | QLabel.__init__(self, text, parent)
48 | font = self.font()
49 | if family is not None:
50 | font.setFamily(get_family(family))
51 | font.setBold(bold)
52 | font.setItalic(italic)
53 | self.setFont(font)
54 | self.color = color
55 | self.angle = angle
56 | self.setAlignment(Qt.AlignCenter)
57 |
58 | def paintEvent(self, evt):
59 | painter = QPainter(self)
60 | if self.color is not None:
61 | painter.setPen(QPen(self.color))
62 | painter.rotate(self.angle)
63 | transform = painter.transform().inverted()[0]
64 | rct = transform.mapRect(self.rect())
65 | painter.drawText(rct, self.alignment(), self.text())
66 |
67 | def sizeHint(self):
68 | hint = QLabel.sizeHint(self)
69 | width, height = hint.width(), hint.height()
70 | angle = self.angle * pi / 180
71 | rotated_width = int(abs(width * cos(angle)) + abs(height * sin(angle)))
72 | rotated_height = int(abs(width * sin(angle)) + abs(height * cos(angle)))
73 | return QSize(rotated_width, rotated_height)
74 |
75 | def minimumSizeHint(self):
76 | return self.sizeHint()
77 |
--------------------------------------------------------------------------------
/guidata/widgets/texteditor.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # Copyright © Spyder Project Contributors
4 | # Licensed under the terms of the MIT License
5 | # (see spyder/__init__.py for details)
6 |
7 | # ruff: noqa
8 |
9 | """
10 | guidata.widgets.texteditor
11 | ==========================
12 |
13 | This package provides a text editor widget based on QtGui.QPlainTextEdit.
14 |
15 | .. autoclass:: TextEditor
16 | :show-inheritance:
17 | :members:
18 |
19 | """
20 |
21 |
22 | from qtpy.QtCore import Qt, Slot
23 | from qtpy.QtWidgets import QDialog, QHBoxLayout, QPushButton, QTextEdit, QVBoxLayout
24 |
25 | from guidata.config import CONF, _
26 | from guidata.configtools import get_font, get_icon
27 | from guidata.qthelpers import win32_fix_title_bar_background
28 |
29 |
30 | class TextEditor(QDialog):
31 | """Array Editor Dialog"""
32 |
33 | def __init__(
34 | self, text, title="", font=None, parent=None, readonly=False, size=(400, 300)
35 | ):
36 | QDialog.__init__(self, parent)
37 |
38 | win32_fix_title_bar_background(self)
39 |
40 | # Destroying the C++ object right after closing the dialog box,
41 | # otherwise it may be garbage-collected in another QThread
42 | # (e.g. the editor's analysis thread in Spyder), thus leading to
43 | # a segmentation fault on UNIX or an application crash on Windows
44 | self.setAttribute(Qt.WA_DeleteOnClose)
45 |
46 | self.text = None
47 | self.btn_save_and_close = None
48 |
49 | # Display text as unicode if it comes as bytes, so users see
50 | # its right representation
51 | if isinstance(text, bytes):
52 | self.is_binary = True
53 | text = str(text, "utf8")
54 | else:
55 | self.is_binary = False
56 |
57 | self.layout = QVBoxLayout()
58 | self.setLayout(self.layout)
59 |
60 | # Text edit
61 | self.edit = QTextEdit(parent)
62 | self.edit.setReadOnly(readonly)
63 | self.edit.textChanged.connect(self.text_changed)
64 | self.edit.setPlainText(text)
65 | if font is None:
66 | font = get_font(CONF, "texteditor")
67 | self.edit.setFont(font)
68 | self.layout.addWidget(self.edit)
69 |
70 | # Buttons configuration
71 | btn_layout = QHBoxLayout()
72 | btn_layout.addStretch()
73 | if not readonly:
74 | self.btn_save_and_close = QPushButton(_("Save and Close"))
75 | self.btn_save_and_close.setDisabled(True)
76 | self.btn_save_and_close.clicked.connect(self.accept)
77 | btn_layout.addWidget(self.btn_save_and_close)
78 |
79 | self.btn_close = QPushButton(_("Close"))
80 | self.btn_close.setAutoDefault(True)
81 | self.btn_close.setDefault(True)
82 | self.btn_close.clicked.connect(self.reject)
83 | btn_layout.addWidget(self.btn_close)
84 |
85 | self.layout.addLayout(btn_layout)
86 |
87 | # Make the dialog act as a window
88 | self.setWindowFlags(Qt.Window)
89 |
90 | self.setWindowIcon(get_icon("edit.png"))
91 | self.setWindowTitle(
92 | _("Text editor") + "%s" % (" - " + str(title) if str(title) else "")
93 | )
94 | self.resize(size[0], size[1])
95 |
96 | @Slot()
97 | def text_changed(self):
98 | """Text has changed"""
99 | # Save text as bytes, if it was initially bytes
100 | if self.is_binary:
101 | self.text = bytes(self.edit.toPlainText(), "utf8")
102 | else:
103 | self.text = str(self.edit.toPlainText())
104 | if self.btn_save_and_close:
105 | self.btn_save_and_close.setEnabled(True)
106 | self.btn_save_and_close.setAutoDefault(True)
107 | self.btn_save_and_close.setDefault(True)
108 |
109 | def get_value(self):
110 | """Return modified text"""
111 | # It is import to avoid accessing Qt C++ object as it has probably
112 | # already been destroyed, due to the Qt.WA_DeleteOnClose attribute
113 | return self.text
114 |
115 | def setup_and_check(self, value):
116 | """Verify if TextEditor is able to display strings passed to it."""
117 | if isinstance(value, str):
118 | return True
119 | try:
120 | str(value, "utf8")
121 | return True
122 | except:
123 | return False
124 |
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | # guidata setup configuration file
2 |
3 | # Important note:
4 | # Requirements are parsed by utils\genreqs.py to generate documentation
5 |
6 | [build-system]
7 | requires = ["setuptools"]
8 | build-backend = "setuptools.build_meta"
9 |
10 | [project]
11 | name = "guidata"
12 | authors = [{ name = "Codra", email = "p.raybaut@codra.fr" }]
13 | description = "Automatic GUI generation for easy dataset editing and display"
14 | readme = "README.md"
15 | license = { file = "LICENSE" }
16 | classifiers = [
17 | "Development Status :: 5 - Production/Stable",
18 | "Intended Audience :: Developers",
19 | "Intended Audience :: Education",
20 | "Intended Audience :: Science/Research",
21 | "License :: OSI Approved :: BSD License",
22 | "Operating System :: MacOS :: MacOS X",
23 | "Operating System :: Microsoft :: Windows :: Windows 7",
24 | "Operating System :: Microsoft :: Windows :: Windows 8",
25 | "Operating System :: Microsoft :: Windows :: Windows 10",
26 | "Operating System :: Microsoft :: Windows :: Windows 11",
27 | "Operating System :: POSIX :: Linux",
28 | "Programming Language :: Python :: 3.9",
29 | "Programming Language :: Python :: 3.10",
30 | "Programming Language :: Python :: 3.11",
31 | "Programming Language :: Python :: 3.12",
32 | "Programming Language :: Python :: 3.13",
33 | "Topic :: Scientific/Engineering",
34 | "Topic :: Scientific/Engineering :: Human Machine Interfaces",
35 | "Topic :: Software Development :: Libraries :: Python Modules",
36 | "Topic :: Software Development :: User Interfaces",
37 | "Topic :: Software Development :: Widget Sets",
38 | "Topic :: Utilities",
39 | ]
40 | requires-python = ">=3.9, <4"
41 | dependencies = ["h5py>=3.1", "NumPy>=1.19", "QtPy>=1.9", "requests", "tomli"]
42 | dynamic = ["version"]
43 |
44 | [project.urls]
45 | Homepage = "https://github.com/PlotPyStack/guidata/"
46 | Documentation = "https://guidata.readthedocs.io/en/latest/"
47 |
48 | [project.gui-scripts]
49 | guidata-tests = "guidata.tests:run"
50 |
51 | [project.optional-dependencies]
52 | dev = ["ruff", "pylint", "Coverage"]
53 | doc = [
54 | "PyQt5",
55 | "pillow",
56 | "pandas",
57 | "sphinx>6",
58 | "myst_parser",
59 | "sphinx-copybutton",
60 | "sphinx_qt_documentation",
61 | "python-docs-theme",
62 | ]
63 | test = ["pytest", "pytest-xvfb"]
64 |
65 | [tool.setuptools.packages.find]
66 | include = ["guidata*"]
67 |
68 | [tool.setuptools.package-data]
69 | "*" = ["*.png", "*.svg", "*.mo", "*.cfg", "*.toml"]
70 |
71 | [tool.setuptools.dynamic]
72 | version = { attr = "guidata.__version__" }
73 |
74 | [tool.pytest.ini_options]
75 | addopts = "guidata"
76 |
77 | [tool.ruff]
78 | exclude = [".git", ".vscode", "build", "dist", "guidata/external"]
79 | line-length = 88 # Same as Black.
80 | indent-width = 4 # Same as Black.
81 | target-version = "py39" # Assume Python 3.9.
82 |
83 | [tool.ruff.lint]
84 | # all rules can be found here: https://beta.ruff.rs/docs/rules/
85 | select = ["E", "F", "W", "I", "NPY201"]
86 | ignore = [
87 | "E203", # space before : (needed for how black formats slicing)
88 | ]
89 |
90 | [tool.ruff.format]
91 | quote-style = "double" # Like Black, use double quotes for strings.
92 | indent-style = "space" # Like Black, indent with spaces, rather than tabs.
93 | skip-magic-trailing-comma = false # Like Black, respect magic trailing commas.
94 | line-ending = "auto" # Like Black, automatically detect the appropriate line ending.
95 |
96 | [tool.ruff.lint.per-file-ignores]
97 | "doc/*" = ["E402"]
98 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | Coverage
2 | NumPy>=1.21
3 | PyQt5
4 | QtPy>=1.9
5 | h5py>=3.0
6 | pandas
7 | pillow
8 | pylint
9 | ruff
10 | pytest
11 | pytest-xvfb
12 | python-docs-theme
13 | requests
14 | sphinx
15 | myst_parser
16 | sphinx-copybutton
17 | sphinx_qt_documentation
18 | tomli
--------------------------------------------------------------------------------
/scripts/build_dist.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | REM This script was copied from PythonQwt project
3 | REM ======================================================
4 | REM Package build script
5 | REM ======================================================
6 | REM Licensed under the terms of the MIT License
7 | REM Copyright (c) 2020 Pierre Raybaut
8 | REM (see PythonQwt LICENSE file for more details)
9 | REM ======================================================
10 | call %~dp0utils GetScriptPath SCRIPTPATH
11 | call %FUNC% GetLibName LIBNAME
12 | call %FUNC% GetModName MODNAME
13 | call %FUNC% SetPythonPath
14 | call %FUNC% UsePython
15 | call %FUNC% GetVersion VERSION
16 |
17 | set REPODIR=%SCRIPTPATH%\..
18 |
19 | @REM Clone repository in a temporary directory
20 | set CLONEDIR=%REPODIR%\..\%LIBNAME%-tempdir
21 | if exist %CLONEDIR% ( rmdir /s /q %CLONEDIR% )
22 | git clone -l -s . %CLONEDIR%
23 |
24 | @REM Build distribution files
25 | pushd %CLONEDIR%
26 | %PYTHON% -m build
27 | popd
28 |
29 | @REM Copy distribution files to the repository
30 | set DISTDIR=%REPODIR%\dist
31 | if not exist %DISTDIR% ( mkdir %DISTDIR% )
32 | copy /y %CLONEDIR%\dist\%MODNAME%-%VERSION%*.whl %DISTDIR%
33 | copy /y %CLONEDIR%\dist\%MODNAME%-%VERSION%*.tar.gz %DISTDIR%
34 |
35 | @REM Clean up
36 | rmdir /s /q %CLONEDIR%
37 | call %FUNC% EndOfScript
--------------------------------------------------------------------------------
/scripts/build_doc.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | REM This script was copied from PythonQwt project
3 | REM ======================================================
4 | REM Documentation build script
5 | REM ======================================================
6 | REM Licensed under the terms of the MIT License
7 | REM Copyright (c) 2020 Pierre Raybaut
8 | REM (see PythonQwt LICENSE file for more details)
9 | REM ======================================================
10 | setlocal
11 | call %~dp0utils GetScriptPath SCRIPTPATH
12 | call %FUNC% GetLibName LIBNAME
13 | call %FUNC% GetModName MODNAME
14 | call %FUNC% SetPythonPath
15 | call %FUNC% UsePython
16 | cd %SCRIPTPATH%\..
17 | %PYTHON% doc\update_requirements.py
18 | sphinx-build -b html doc build\doc
19 | start build\doc\index.html
20 | call %FUNC% EndOfScript
--------------------------------------------------------------------------------
/scripts/clean_up.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | REM This script was copied from PythonQwt project
3 | REM ======================================================
4 | REM Clean up repository
5 | REM ======================================================
6 | REM Licensed under the terms of the MIT License
7 | REM Copyright (c) 2020 Pierre Raybaut
8 | REM (see PythonQwt LICENSE file for more details)
9 | REM ======================================================
10 | call %~dp0utils GetScriptPath SCRIPTPATH
11 | call %FUNC% GetLibName LIBNAME
12 | call %FUNC% GetModName MODNAME
13 | cd %SCRIPTPATH%\..\
14 |
15 | @REM Removing files/directories related to Python/doc build process
16 | if exist %LIBNAME%.egg-info ( rmdir /s /q %LIBNAME%.egg-info )
17 | if exist %MODNAME%.egg-info ( rmdir /s /q %MODNAME%.egg-info )
18 | if exist MANIFEST ( del /q MANIFEST )
19 | if exist build ( rmdir /s /q build )
20 | if exist dist ( rmdir /s /q dist )
21 | if exist doc\_build ( rmdir /s /q doc\_build )
22 |
23 | @REM Removing cache files/directories related to Python execution
24 | del /s /q *.pyc 1>nul 2>&1
25 | del /s /q *.pyo 1>nul 2>&1
26 | FOR /d /r %%d IN ("__pycache__") DO @IF EXIST "%%d" rd /s /q "%%d"
27 |
28 | @REM Removing directories related to public repository upload
29 | set TEMP=%SCRIPTPATH%\..\..\%LIBNAME%_temp
30 | set PUBLIC=%SCRIPTPATH%\..\..\%LIBNAME%_public
31 | if exist %TEMP% ( rmdir /s /q %TEMP% )
32 | if exist %PUBLIC% ( rmdir /s /q %PUBLIC% )
33 |
34 | @REM Removing files/directories related to Coverage
35 | if exist .coverage ( del /q .coverage )
36 | if exist coverage.xml ( del /q coverage.xml )
37 | if exist htmlcov ( rmdir /s /q htmlcov )
38 | del /q .coverage.* 1>nul 2>&1
39 | if exist sitecustomize.py ( del /q sitecustomize.py )
40 |
--------------------------------------------------------------------------------
/scripts/gettext.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | REM This script was derived from PythonQwt project
3 | REM ======================================================
4 | REM Run gettext translation tool
5 | REM ======================================================
6 | REM Licensed under the terms of the MIT License
7 | REM Copyright (c) 2020 Pierre Raybaut
8 | REM (see PythonQwt LICENSE file for more details)
9 | REM ======================================================
10 | setlocal
11 | call %~dp0utils GetScriptPath SCRIPTPATH
12 | call %FUNC% SetPythonPath
13 | call %FUNC% UsePython
14 | call %FUNC% GetModName MODNAME
15 | %PYTHON% -c "from guidata.utils.gettext_helpers import do_%1; do_%1('%MODNAME%')"
16 | call %FUNC% EndOfScript
--------------------------------------------------------------------------------
/scripts/run_coverage.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | REM This script was derived from PythonQwt project
3 | REM ======================================================
4 | REM Run coverage tests
5 | REM ======================================================
6 | REM Licensed under the terms of the MIT License
7 | REM Copyright (c) 2020 Pierre Raybaut
8 | REM (see PythonQwt LICENSE file for more details)
9 | REM ======================================================
10 | setlocal
11 | call %~dp0utils GetScriptPath SCRIPTPATH
12 | call %FUNC% GetModName MODNAME
13 | call %FUNC% SetPythonPath
14 | call %FUNC% UsePython
15 | set COVERAGE_PROCESS_START=%SCRIPTPATH%\..\.coveragerc
16 | coverage run -m pytest
17 | coverage combine
18 | coverage html
19 | start .\htmlcov\index.html
20 | call %FUNC% EndOfScript
21 |
--------------------------------------------------------------------------------
/scripts/run_pylint.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | REM This script was derived from PythonQwt project
3 | REM ======================================================
4 | REM Run pylint analysis
5 | REM ======================================================
6 | REM Licensed under the terms of the MIT License
7 | REM Copyright (c) 2020 Pierre Raybaut
8 | REM (see PythonQwt LICENSE file for more details)
9 | REM ======================================================
10 | setlocal
11 | call %~dp0utils GetScriptPath SCRIPTPATH
12 | call %FUNC% GetModName MODNAME
13 | call %FUNC% SetPythonPath
14 | set PYLINT_ARG=%*
15 | if "%PYLINT_ARG%"=="" set PYLINT_ARG=--disable=fixme
16 | %PYTHON% -m pylint --rcfile=%SCRIPTPATH%\..\.pylintrc %PYLINT_ARG% %MODNAME%
17 | call %FUNC% EndOfScript
--------------------------------------------------------------------------------
/scripts/run_pytest.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | REM This script was derived from PythonQwt project
3 | REM ======================================================
4 | REM Run pytest script
5 | REM ======================================================
6 | REM Licensed under the terms of the MIT License
7 | REM Copyright (c) 2020 Pierre Raybaut
8 | REM (see PythonQwt LICENSE file for more details)
9 | REM ======================================================
10 | setlocal enabledelayedexpansion
11 | call %~dp0utils GetScriptPath SCRIPTPATH
12 | call %FUNC% GetModName MODNAME
13 | call %FUNC% SetPythonPath
14 |
15 | :: Iterate over all directories in the grandparent directory
16 | :: (WinPython base directories)
17 | call %FUNC% GetPythonExeGrandParentDir DIR0
18 | for /D %%d in ("%DIR0%*") do (
19 | set WINPYDIRBASE=%%d
20 | call !WINPYDIRBASE!\scripts\env.bat
21 | echo Running pytest from "%%d":
22 | pytest --ff -q %MODNAME%
23 | echo ----
24 | )
25 | call %FUNC% EndOfScript
26 |
--------------------------------------------------------------------------------
/scripts/run_ruff.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | REM This script was derived from PythonQwt project
3 | REM ======================================================
4 | REM Run Ruff analysis
5 | REM ======================================================
6 | REM Licensed under the terms of the MIT License
7 | REM Copyright (c) 2020 Pierre Raybaut
8 | REM (see PythonQwt LICENSE file for more details)
9 | REM ======================================================
10 | setlocal
11 | call %~dp0utils GetScriptPath SCRIPTPATH
12 | call %FUNC% GetModName MODNAME
13 | call %FUNC% SetPythonPath
14 | call %FUNC% UsePython
15 | ruff check
16 | call %FUNC% EndOfScript
--------------------------------------------------------------------------------
/scripts/run_test_launcher.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | REM This script was derived from PythonQwt project
3 | REM ======================================================
4 | REM Test launcher script
5 | REM ======================================================
6 | REM Licensed under the terms of the MIT License
7 | REM Copyright (c) 2020 Pierre Raybaut
8 | REM (see PythonQwt LICENSE file for more details)
9 | REM ======================================================
10 | setlocal
11 | call %~dp0utils GetScriptPath SCRIPTPATH
12 | call %FUNC% SetPythonPath
13 | call %FUNC% UsePython
14 | call %FUNC% GetModName MODNAME
15 | python -m %MODNAME%.tests.__init__
16 | call %FUNC% EndOfScript
--------------------------------------------------------------------------------
/scripts/upgrade_env.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | REM This script was derived from PythonQwt project
3 | REM ======================================================
4 | REM Upgrade environment
5 | REM ======================================================
6 | REM Licensed under the terms of the MIT License
7 | REM Copyright (c) 2020 Pierre Raybaut
8 | REM (see PythonQwt LICENSE file for more details)
9 | REM ======================================================
10 | setlocal
11 | call %~dp0utils GetScriptPath SCRIPTPATH
12 | cd %SCRIPTPATH%\..
13 | %PYTHON% -m pip install --upgrade -r dev\requirements.txt
14 | %PYTHON% -m pip list > dev\pip_list.txt
15 | call %FUNC% EndOfScript
--------------------------------------------------------------------------------
/scripts/utils.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | set FUNC=%0
3 | call:%*
4 | goto Exit
5 |
6 | REM ======================================================
7 | REM Utilities for deployment, test and build scripts
8 | REM ======================================================
9 | REM Licensed under the terms of the MIT License
10 | REM Copyright (c) 2020 Pierre Raybaut
11 | REM (see LICENSE file for more details)
12 | REM ======================================================
13 |
14 | :GetScriptPath
15 | set _tmp_=%~dp0
16 | if %_tmp_:~-1%==\ set %1=%_tmp_:~0,-1%
17 | EXIT /B 0
18 |
19 | :GetLibName
20 | pushd %~dp0..
21 | for %%I in (.) do set %1=%%~nxI
22 | popd
23 | goto:eof
24 |
25 | :GetModName
26 | pushd %~dp0..
27 | for /D %%I in (*) DO (
28 | if exist %%I\__init__.py (
29 | set %1=%%I
30 | goto :found_module
31 | )
32 | )
33 | :found_module
34 | popd
35 | goto:eof
36 |
37 | :GetVersion
38 | call:GetModName MODNAME
39 | call:SetPythonPath
40 | echo import %MODNAME%;print(%MODNAME%.__version__) | python > _tmp_.txt
41 | set /p %1=<_tmp_.txt
42 | del _tmp_.txt
43 | goto:eof
44 |
45 | :GetVersionWithoutAlphaBeta
46 | call:GetModName MODNAME
47 | call:SetPythonPath
48 | echo import %MODNAME%;ver=%MODNAME%.__version__;print(ver.split("b")[0] if "b" in ver else ver.split("a")[0] if "a" in ver else ver) | python > _tmp_.txt
49 | set /p %1=<_tmp_.txt
50 | del _tmp_.txt
51 | goto:eof
52 |
53 | :SetPythonPath
54 | set ORIGINAL_PYTHONPATH=%PYTHONPATH%
55 | cd %~dp0..
56 | for /F "tokens=*" %%A in (.env) do (
57 | set %%A
58 | )
59 | set PYTHONPATH=%PYTHONPATH%;%ORIGINAL_PYTHONPATH%
60 | goto:eof
61 |
62 | :GetPythonExeGrandParentDir
63 | for %%i in (%PYTHON%) do set DIR2=%%~dpi
64 | set DIR2=%DIR2:~0,-1%
65 | for %%j in (%DIR2%) do set DIR1=%%~dpj
66 | set DIR1=%DIR1:~0,-1%
67 | for %%k in (%DIR1%) do set %1=%%~dpk
68 | goto:eof
69 |
70 | :UsePython
71 | if defined WINPYVER (goto:eof)
72 | if not defined PYTHON (goto :nopython)
73 | for %%a in ("%PYTHON%") do set "p_dir=%%~dpa"
74 | if exist "%p_dir%\activate.bat" (goto :venvpython)
75 | for %%a in (%p_dir:~0,-1%) do set "WINPYDIRBASE=%%~dpa"
76 | if exist "%WINPYDIRBASE%\scripts\env.bat" (goto :nopython)
77 | goto :python
78 | :venvpython
79 | call "%p_dir%\activate.bat"
80 | call :ShowTitle "Using Python Virtual Environment from %p_dir%"
81 | goto:eof
82 | :python
83 | set PATH=%p_dir%;%PATH%
84 | call :ShowTitle "Using Python from %p_dir%"
85 | goto:eof
86 | :nopython
87 | if defined WINPYDIRBASE (
88 | call %WINPYDIRBASE%\scripts\env.bat
89 | call :ShowTitle "Using WinPython from %WINPYDIRBASE%"
90 | ) else (
91 | echo Warning: WINPYDIRBASE environment variable is not defined, switching to system Python
92 | echo ********
93 | echo (if nothing happens, that's probably because Python is not installed either:
94 | echo please set the WINPYDIRBASE variable to select WinPython directory, or install Python)
95 | )
96 | goto:eof
97 |
98 | :ShowTitle
99 | @echo:
100 | @echo ========= %~1 =========
101 | @echo:
102 | goto:eof
103 |
104 | :EndOfScript
105 | @echo:
106 | @echo **********************************************************************************
107 | @echo:
108 | if not defined UNATTENDED (
109 | @echo End of script
110 | pause
111 | )
112 | goto:eof
113 |
114 | :Exit
115 | exit /b
--------------------------------------------------------------------------------