├── .editorconfig
├── .gitignore
├── HISTORY.rst
├── LICENSE
├── MANIFEST.in
├── Makefile
├── README.md
├── README.rst
├── argos
├── __init__.py
├── __main__.py
├── application.py
├── argos_make_wrappers.py
├── collect
│ ├── __init__.py
│ ├── collector.py
│ └── collectortree.py
├── config
│ ├── __init__.py
│ ├── abstractcti.py
│ ├── boolcti.py
│ ├── choicecti.py
│ ├── configitemdelegate.py
│ ├── configtreemodel.py
│ ├── configtreeview.py
│ ├── floatcti.py
│ ├── groupcti.py
│ ├── intcti.py
│ ├── qtctis.py
│ ├── stringcti.py
│ └── untypedcti.py
├── external
│ ├── __init__.py
│ ├── ez_setup.py
│ ├── json_with_comments.py
│ └── six.py
├── img
│ ├── argos.css
│ ├── logo
│ │ └── drafts.svg
│ └── snipicons
│ │ ├── README.txt
│ │ ├── align-left.svg
│ │ ├── arrow-down-l.svg
│ │ ├── arrow-left-l.svg
│ │ ├── arrow-right-l.svg
│ │ ├── arrow-up-l.svg
│ │ ├── asterisk.svg
│ │ ├── circle-arrow-down-l.svg
│ │ ├── circle-arrow-left-l.svg
│ │ ├── circle-arrow-right-l.svg
│ │ ├── circle-arrow-up-l.svg
│ │ ├── exclamation-sign.svg
│ │ ├── file-inverse.svg
│ │ ├── file.svg
│ │ ├── folder-close.svg
│ │ ├── folder-open.svg
│ │ ├── leaf.svg
│ │ ├── list.svg
│ │ ├── minus-l.svg
│ │ ├── minus-sign-l.svg
│ │ ├── move.svg
│ │ ├── plus-sign-l.svg
│ │ ├── remove-circle-l.svg
│ │ ├── remove-sign-l.svg
│ │ ├── reset-l.svg
│ │ ├── th-large.svg
│ │ ├── th-list.svg
│ │ ├── transparent_1x1.svg
│ │ └── warning-sign.svg
├── info.py
├── inspector
│ ├── __init__.py
│ ├── abstract.py
│ ├── debug.py
│ ├── dialog.py
│ ├── errormsg.py
│ ├── pgplugins
│ │ ├── __init__.py
│ │ ├── colorbar.py
│ │ ├── imageplot2d.py
│ │ ├── lineplot1d.py
│ │ ├── old_imageplot2d.py
│ │ ├── pgctis.py
│ │ ├── pghistlutitem.py
│ │ ├── pgplotitem.py
│ │ ├── uml.dia
│ │ ├── uml.pdf
│ │ └── uml.png
│ ├── qtplugins
│ │ ├── __init__.py
│ │ ├── table.py
│ │ └── text.py
│ ├── registry.py
│ └── selectionpane.py
├── main.py
├── mytest.py
├── qt
│ ├── __init__.py
│ ├── bindings.py
│ ├── colorselect.py
│ ├── labeledwidget.py
│ ├── misc.py
│ ├── scientificspinbox.py
│ ├── shortcutedit.py
│ ├── togglecolumn.py
│ ├── treeitems.py
│ └── treemodels.py
├── reg
│ ├── __init__.py
│ ├── basereg.py
│ ├── dialog.py
│ ├── tabmodel.py
│ └── tabview.py
├── repo
│ ├── __init__.py
│ ├── baserti.py
│ ├── colors.py
│ ├── detailpanes.py
│ ├── detailplugins
│ │ ├── __init__.py
│ │ ├── attr.py
│ │ ├── prop.py
│ │ └── quicklook.py
│ ├── filesytemrtis.py
│ ├── iconfactory.py
│ ├── memoryrtis.py
│ ├── registry.py
│ ├── repotreemodel.py
│ ├── repotreeview.py
│ ├── rtiplugins
│ │ ├── __init__.py
│ │ ├── exdir.py
│ │ ├── hdf5.py
│ │ ├── jsonio.py
│ │ ├── ncdf.py
│ │ ├── numpyio.py
│ │ ├── pandasio.py
│ │ ├── pillowio.py
│ │ └── scipyio.py
│ └── testdata.py
├── utils
│ ├── __init__.py
│ ├── cls.py
│ ├── config.py
│ ├── default_logging.json
│ ├── defs.py
│ ├── dirs.py
│ ├── logs.py
│ ├── masks.py
│ ├── misc.py
│ └── moduleinfo.py
└── widgets
│ ├── __init__.py
│ ├── aboutdialog.py
│ ├── argostableview.py
│ ├── argostreeview.py
│ ├── constants.py
│ ├── display.py
│ ├── mainwindow.py
│ ├── misc.py
│ └── testwalkdialog.py
├── development
├── 3rd_party_data.txt
├── 3rd_party_packages.txt
├── AUTHORS.rst
├── CONTRIBUTING.rst
├── activate.sh
├── argos.py
├── environment.yaml
├── environment_min.yaml
├── int_coercion.py
├── mypy.ini
├── paths.txt
├── readme.txt
├── to_be_checked.txt
└── todo.txt
├── docs
├── Makefile
├── create_html_doc.sh
├── examples
│ ├── buggy_inspector.py
│ ├── example_plugin.py
│ ├── nonexisting.py
│ └── print_plugins.py
├── make.bat
├── screen_shots
│ ├── argos_gui.png
│ ├── collector_1d.png
│ ├── collector_2d.png
│ ├── color_legend.png
│ ├── reset_menu.png
│ └── settings.png
└── source
│ ├── conf.py
│ └── index.rst
├── docs_new.zip
├── dot_pylintrc.old
├── pylint_check.sh
├── pyproject.toml
├── setup.py
└── tests
├── create_test_data
├── hdf5
│ ├── create_test_data.py
│ ├── make_null_vars.py
│ ├── make_strings.py
│ └── read.py
├── netcdf
│ ├── create_test_data.py
│ └── make_large_dims.py
├── numpy
│ └── numpyio.py
└── readme.txt
├── plot_nans.py
├── test_cti.py
├── test_qsettings.py
├── test_rti.py
├── test_treemodels.py
└── test_utils.py
/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 |
3 | root = true
4 |
5 | [*]
6 | indent_style = space
7 | indent_size = 4
8 | trim_trailing_whitespace = true
9 | insert_final_newline = true
10 | charset = utf-8
11 | end_of_line = lf
12 |
13 | [*.bat]
14 | indent_style = tab
15 | end_of_line = crlf
16 |
17 | [LICENSE]
18 | insert_final_newline = false
19 |
20 | [Makefile]
21 | indent_style = tab
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by https://www.gitignore.io/api/python
2 |
3 | ### Python ###
4 | # Byte-compiled / optimized / DLL files
5 | __pycache__/
6 | *.py[cod]
7 | *$py.class
8 |
9 | # C extensions
10 | *.so
11 |
12 | # Distribution / packaging
13 | .Python
14 | env/
15 | build/
16 | develop-eggs/
17 | dist/
18 | downloads/
19 | eggs/
20 | .eggs/
21 | lib/
22 | lib64/
23 | parts/
24 | sdist/
25 | var/
26 | wheels/
27 | *.egg-info/
28 | .installed.cfg
29 | *.egg
30 |
31 | # PyInstaller
32 | # Usually these files are written by a python script from a template
33 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
34 | *.manifest
35 | *.spec
36 |
37 | # Installer logs
38 | pip-log.txt
39 | pip-delete-this-directory.txt
40 |
41 | # Unit test / coverage reports
42 | htmlcov/
43 | .tox/
44 | .coverage
45 | .coverage.*
46 | .cache
47 | nosetests.xml
48 | coverage.xml
49 | *,cover
50 | .hypothesis/
51 |
52 | # Translations
53 | *.mo
54 | *.pot
55 |
56 | # Django stuff:
57 | *.log
58 | local_settings.py
59 |
60 | # Flask stuff:
61 | instance/
62 | .webassets-cache
63 |
64 | # Scrapy stuff:
65 | .scrapy
66 |
67 | # Sphinx documentation
68 | docs/_build/
69 |
70 | # PyBuilder
71 | target/
72 |
73 | # Jupyter Notebook
74 | .ipynb_checkpoints
75 |
76 | # pyenv
77 | .python-version
78 |
79 | # celery beat schedule file
80 | celerybeat-schedule
81 |
82 | # dotenv
83 | .env
84 |
85 | # virtualenv
86 | .venv/
87 | venv/
88 | ENV/
89 |
90 | # Spyder project settings
91 | .spyderproject
92 |
93 | # Rope project settings
94 | .ropeproject
95 |
96 | # End of https://www.gitignore.io/api/python
97 | *.prof
98 | *.pdf
99 | *.json
100 | .idea/
101 | tests/test_data/
102 | tests/create_test_data/test_data
103 | docs_new
104 | docs/source/api
105 |
106 |
107 | # Argos data formats
108 | *.h5
109 | *.nc4
110 | *.spi
111 |
--------------------------------------------------------------------------------
/HISTORY.rst:
--------------------------------------------------------------------------------
1 | .. :changelog:
2 |
3 | History
4 | =======
5 |
6 | Note that version 0.3 will be the last release that supports Python 2.
7 |
8 | 0.4.5 (2024-??-??)
9 | ------------------
10 |
11 | * HDF5 dimension label and dimension scale name take precedence when calculating dimension name.
12 | * HDF5 support for enumerations. These can't be plotted but enum values are displayed in the table.
13 | * HDF5 plugin opens *.si5 extension by default.
14 | * HDF5 fix sub array dimension names (since 0.4.2)
15 | * HDF5 fix item order in root group when track_order == True.
16 |
17 | 0.4.4 (2023-02-06)
18 | ------------------
19 |
20 | * Removed deprecated numpy aliases so it works with numpy 1.24.
21 |
22 | 0.4.3 (2022-07-26)
23 | ------------------
24 |
25 | * Removed spurious log messages.
26 |
27 | 0.4.2 (2022-07-24)
28 | ------------------
29 |
30 | * Compound/stuctured HDF5 datasets can be nested.
31 | * Can display JSON files (with comments https://github.com/sidneycadot/json_with_comments)
32 | * Removed many harmless warnings.
33 | * Small fixes
34 |
35 |
36 | 0.4.1 (2022-03-02)
37 | ------------------
38 |
39 | * Fix: don't import numpy during running setup.py
40 |
41 | 0.4.0 (2022-02-27)
42 | ------------------
43 |
44 | * Quick Look panel for showing quickly the contents of scalars and small arrays.
45 | * The Repository tree shows Kind, Element Type and Summary columns by default.
46 | * If one file or directory is opened, or given on the command line, it is expanded.
47 | * Only add a '-' item to the collector combo boxes if the array has less dimensions than the inspector.
48 | * The table inspector has "#8.4g" as the default format for floats. The tooltip shows all digits.
49 | * Added __main__.py so argos can be started with: python -m argos.
50 | * Fixed import in Line Plot and Image Plot inspectors (issue #20).
51 | * Fixed errors when running Argos with Python 3.10.
52 |
53 |
54 | 0.3.1 (2021-02-07)
55 | ------------------
56 |
57 | * Fixed error in style sheet.
58 |
59 |
60 | 0.3.0 (2021-02-07)
61 | ------------------
62 |
63 | * New legend in 2D image inspector
64 | * Extensive database of color maps and color map picker (CmLib).
65 | * Directories are listed in alphabetical order.
66 | * Image inspector: cross hair and axis ticks are displayed in the middle of the pixels.
67 | * Image inspector: added 'auto down-sample' config option (on by default).
68 | This improves performance for large images and reduces aliasing.
69 | * Added rectangle zoom mode in 1D line plot and 2D image plot inspectors (issue #8)
70 | * Zooming can be also done by dragging while holding the right mouse button (issue #8)
71 | * If possible, only informative dimensions (i.e. length > 1) are selected in the collector
72 | combo boxes. (issue #9)
73 | * Sliders next to the spinboxes in the collector panel
74 | * Fix: slices are updated in plot title when spinbox values change.
75 | * Fix: when table headers autosize was enabled, restarting argos could be slow for large tables.
76 | * Users can add/remove/configure plugins via the GUI.
77 | * Grouped the Details dock widgets together with repo viewer as they always apply to the selected item.
78 | * Properties tab shows chunking information for HDF5 and NetCDF data.
79 | * Updated style and layout.
80 | * Added --qt-style and --qss command line options. Using Qt Fusion style as default.
81 | * Displays informative message in case the plot remains empty.
82 | * Accepts unix-like patterns on the Windows command line. E.g. 'argos \*.h5' opens all files with the h5 extension.
83 | * Persistent settings are stored in json file instead of QSettings.
84 | * Added -c command line option for specifying the general configuration file. Settings profiles are thus obsolete
85 | and have been removed.
86 | * Added --log-config command line option for specifying the logging configuration file.
87 | * Collapsing an item in the data tree closes the underlying file or resources.
88 | * Open Exdir files read-only
89 | * Ctrl-C copies all selected cells in the Table instpector to the clipboard.
90 |
91 | 0.2.2 (2020-03-28)
92 | ---------------------
93 |
94 | * Exdir file format plugin. (Thanks to Lui Habl).
95 |
96 |
97 | 0.2.1 (2017-01-12)
98 | ------------------
99 | * Fix: in PyQt 5.7 the slot decorator wouldn't connect anymore if the class didn't derive
100 | from QObject.
101 |
102 |
103 | 0.2.0 (2017-01-01)
104 | ------------------
105 | * First usable release.
106 |
107 |
108 | 0.1.0 (2014-11-01)
109 | ------------------
110 | * First release on PyPI.
111 |
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include *.rst
2 | include development/argos.py
3 | include development/readme.txt
4 | include development/AUTHORS.rst
5 | include development/CONTRIBUTING.rst
6 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | # Make file taken from cookiecutter. Not al targets may be up to date.
2 |
3 | .PHONY: clean-pyc clean-build docs clean
4 |
5 | help:
6 | @echo "clean - remove all build, test, coverage and Python artifacts"
7 | @echo "clean-build - remove build artifacts"
8 | @echo "clean-pyc - remove Python file artifacts"
9 | @echo "clean-test - remove test and coverage artifacts"
10 | @echo "lint - check style with flake8"
11 | @echo "test - run tests quickly with the default Python"
12 | @echo "test-all - run tests on every Python version with tox"
13 | @echo "coverage - check code coverage quickly with the default Python"
14 | @echo "docs - generate Sphinx HTML documentation, including API docs"
15 | @echo "release - package and upload a release"
16 | @echo "dist - package"
17 |
18 | clean: clean-build clean-pyc clean-test
19 |
20 | clean-build:
21 | rm -fr build/
22 | rm -fr dist/
23 | rm -fr *.egg-info
24 |
25 | clean-pyc:
26 | find . -name '*.pyc' -exec rm -f {} +
27 | find . -name '*.pyo' -exec rm -f {} +
28 | find . -name '*~' -exec rm -f {} +
29 | find . -name '__pycache__' -exec rm -fr {} +
30 |
31 | clean-test:
32 | rm -fr .tox/
33 | rm -f .coverage
34 | rm -fr htmlcov/
35 |
36 | lint:
37 | flake8 argos tests
38 |
39 | test:
40 | python setup.py test
41 |
42 | test-all:
43 | tox
44 |
45 | coverage:
46 | coverage run --source argos setup.py test
47 | coverage report -m
48 | coverage html
49 | open htmlcov/index.html
50 |
51 | docs:
52 | rm -f docs/argos.rst
53 | rm -f docs/modules.rst
54 | sphinx-apidoc -o docs/ argos
55 | $(MAKE) -C docs clean
56 | $(MAKE) -C docs html
57 | open docs/_build/html/index.html
58 |
59 | release: clean
60 | python setup.py sdist upload
61 | #python setup.py bdist_wheel upload
62 |
63 | dist: clean
64 | python setup.py sdist
65 | #python setup.py bdist_wheel
66 | #ls -l dist
67 |
--------------------------------------------------------------------------------
/README.rst:
--------------------------------------------------------------------------------
1 | ===============================
2 | Argos
3 | ===============================
4 |
5 |
6 | Argos data inspector for HDF5, NetCDF4 and other file formats.
7 |
8 | For more info, see: https://github.com/titusjan/argos
9 |
--------------------------------------------------------------------------------
/argos/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # This file is part of Argos.
4 | #
5 | # Argos is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # Argos is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with Argos. If not, see .
17 |
18 | """ Functionality for Argos
19 |
20 | This is the top level module and should not be imported by sub modules.
21 | The only way is up in that respect. It imports a few symbols itself for convenience. This
22 | allows users, for instance, to call argos.browse().
23 | """
24 |
25 | from typing import TYPE_CHECKING
26 |
27 | # Prevent import of further modules when type checking so that we can run mypy on individual
28 | # modules without other (unneeded) modules from being checked in.
29 | if not TYPE_CHECKING:
30 |
31 | # IMPORTANT: do not do any imports here that (indirectly) import any dependencies (PyQt, etc)
32 | # The browse function is imported by the argos package, which in turn is imported by setup.py.
33 | # If you import (for instance) numpy here, the setup.py will fail if numpy is not installed.
34 |
35 | from .info import VERSION as __version__
36 | from .main import browse
37 |
38 |
--------------------------------------------------------------------------------
/argos/__main__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # This file is part of Argos.
4 | #
5 | # Argos is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # Argos is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with Argos. If not, see .
17 |
18 | """ The main entry point for argos.
19 |
20 | This file is executed when calling "python -m argos" on the command line.
21 | """
22 |
23 | # IMPORTANT: do not do any imports here that (indirectly) import any dependencies (PyQt, numpy, etc)
24 | # The browse function is imported by the argos package, which in turn is imported by setup.py.
25 | # If you import (for instance) numpy here, the setup.py will fail if numpy is not installed.
26 |
27 | from .main import main
28 | main()
29 |
--------------------------------------------------------------------------------
/argos/collect/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/titusjan/argos/5a334a5d5625300dab5f4e98a36e924600b6e55e/argos/collect/__init__.py
--------------------------------------------------------------------------------
/argos/config/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/titusjan/argos/5a334a5d5625300dab5f4e98a36e924600b6e55e/argos/config/__init__.py
--------------------------------------------------------------------------------
/argos/config/configitemdelegate.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # This file is part of Argos.
3 | #
4 | # Argos is free software: you can redistribute it and/or modify
5 | # it under the terms of the GNU General Public License as published by
6 | # the Free Software Foundation, either version 3 of the License, or
7 | # (at your option) any later version.
8 | #
9 | # Argos is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | # GNU General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public License
15 | # along with Argos. If not, see .
16 |
17 | """ Contains the ConfigItemDelegate class.
18 | """
19 | from __future__ import print_function
20 |
21 | import logging
22 | from argos.utils.defs import InvalidInputError
23 | from argos.qt import Qt, QtCore, QtWidgets
24 | from argos.qt.misc import widgetSubCheckBoxRect
25 | from argos.widgets.constants import TREE_CELL_SIZE_HINT
26 |
27 | logger = logging.getLogger(__name__)
28 |
29 | # Qt classes have many ancestors
30 | #pylint: disable=too-many-ancestors
31 |
32 | class ConfigItemDelegate(QtWidgets.QStyledItemDelegate):
33 | """ Provides editing facilities for config tree items.
34 | Creates an editor kd on the underlying config tree item at an index.
35 |
36 | We don't use a QItemEditorFactory since that is typically registered for a type of
37 | QVariant. We then would have to make a new UserType QVariant for (each?) CTI.
38 | This is cumbersome and possibly unPyQTtonic :-)
39 | """
40 | def __init__(self, parent=None):
41 | super(ConfigItemDelegate, self).__init__(parent=parent)
42 |
43 | self.commitData.connect(self._prepareCommit)
44 |
45 |
46 | def createEditor(self, parent, option, index):
47 | """ Returns the widget used to change data from the model and can be reimplemented to
48 | customize editing behavior.
49 |
50 | Reimplemented from QStyledItemDelegate.
51 | """
52 | logger.debug("ConfigItemDelegate.createEditor, parent: {!r}".format(parent.objectName()))
53 | assert index.isValid(), "sanity check failed: invalid index"
54 |
55 | cti = index.model().getItem(index)
56 | editor = cti.createEditor(self, parent, option)
57 | return editor
58 |
59 |
60 | def finalizeEditor(self, editor):
61 | """ Calls editor.finalize().
62 |
63 | Not part of the QAbstractItemView interface but added to be able to free resources.
64 |
65 | Note that, unlike the other methods of this class, finalizeEditor does not have an
66 | index parameter. We cannot derive this since indexForEditor is a private method in Qt.
67 | Therefore a AbstractCtiEditor maintains a reference to its config tree item (cti).
68 | """
69 | editor.finalize()
70 |
71 |
72 | def _prepareCommit(self, editor):
73 | """ Called when commitData signal is emitted. Calls the prepareCommit of the editor.
74 | """
75 | editor.prepareCommit()
76 |
77 |
78 | def setEditorData(self, editor, index):
79 | """ Provides the widget with data to manipulate.
80 | Calls the setEditorValue of the config tree item at the index.
81 |
82 | :type editor: QWidget
83 | :type index: QModelIndex
84 |
85 | Reimplemented from QStyledItemDelegate.
86 | """
87 | # We take the config value via the model to be consistent with setModelData
88 | data = index.model().data(index, Qt.EditRole)
89 | editor.setData(data)
90 |
91 |
92 | def setModelData(self, editor, model, index):
93 | """ Gets data from the editor widget and stores it in the specified model at the item index.
94 | Does this by calling getEditorValue of the config tree item at the index.
95 |
96 | :type editor: QWidget
97 | :type model: ConfigTreeModel
98 | :type index: QModelIndex
99 |
100 | Reimplemented from QStyledItemDelegate.
101 | """
102 | try:
103 | data = editor.getData()
104 | except InvalidInputError as ex:
105 | logger.warning(ex)
106 | else:
107 | # The value is set via the model so that signals are emitted
108 | logger.debug("ConfigItemDelegate.setModelData: {}".format(data))
109 | model.setData(index, data, Qt.EditRole)
110 |
111 |
112 | def updateEditorGeometry(self, editor, option, index):
113 | """ Ensures that the editor is displayed correctly with respect to the item view.
114 | """
115 | cti = index.model().getItem(index)
116 | if cti.checkState is None:
117 | displayRect = option.rect
118 | else:
119 | checkBoxRect = widgetSubCheckBoxRect(editor, option)
120 | offset = checkBoxRect.x() + checkBoxRect.width()
121 | displayRect = option.rect
122 | displayRect.adjust(offset, 0, 0, 0)
123 |
124 | editor.setGeometry(displayRect)
125 |
126 |
127 |
--------------------------------------------------------------------------------
/argos/config/groupcti.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # This file is part of Argos.
4 | #
5 | # Argos is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # Argos is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with Argos. If not, see .
17 |
18 | """ Contains the Config and GroupCtiEditor classes
19 | """
20 | import logging
21 |
22 | from argos.config.abstractcti import AbstractCti, AbstractCtiEditor
23 | from argos.qt import Qt, QtGui, QtWidgets
24 |
25 | logger = logging.getLogger(__name__)
26 |
27 |
28 |
29 |
30 | class GroupCti(AbstractCti):
31 | """ Read only config Tree Item that only stores None. It can be used to group CTIs
32 | """
33 | def __init__(self, nodeName, defaultData=None, expanded=True):
34 | """ Constructor. For the parameters see the AbstractCti constructor documentation.
35 | """
36 | super(GroupCti, self).__init__(nodeName, defaultData, expanded=expanded)
37 |
38 |
39 | def _enforceDataType(self, data):
40 | """ Passes the data as is; no conversion.
41 | """
42 | return data
43 |
44 |
45 | def _dataToString(self, data):
46 | """ Conversion function used to convert the (default)data to the display value.
47 | Returns an empty string.
48 | """
49 | return ""
50 |
51 |
52 | def createEditor(self, delegate, parent, _option):
53 | """ Creates a hidden widget so that only the reset button is visible during editing.
54 | :type option: QStyleOptionViewItem
55 | """
56 | return GroupCtiEditor(self, delegate, parent=parent)
57 |
58 |
59 | class GroupCtiEditor(AbstractCtiEditor):
60 | """ A CtiEditor which contains a hidden widget.
61 | If the item is editable, the reset button is shown.
62 | """
63 | def __init__(self, cti, delegate, parent=None):
64 | """ See the AbstractCtiEditor for more info on the parameters
65 | """
66 | super(GroupCtiEditor, self).__init__(cti, delegate, parent=parent)
67 |
68 | # Add hidden widget to store editor value
69 | self.widget = self.addSubEditor(QtWidgets.QWidget())
70 | self.widget.hide()
71 |
72 |
73 | def setData(self, data):
74 | """ Provides the main editor widget with a data to manipulate.
75 | """
76 | # Set the data in the 'editor_data' property of the widget to that getData
77 | # can pass the same value back into the CTI.
78 | self.widget.setProperty("editor_data", data)
79 |
80 |
81 | def getData(self):
82 | """ Gets data from the editor widget.
83 | """
84 | return self.widget.property("editor_data")
85 |
86 |
87 |
88 | class MainGroupCti(GroupCti):
89 | """ Read only config Tree Item that only stores None.
90 | To be used as a high level group (e.g. the inspector group)
91 | Is the same as a groupCti but drawn as light text on a dark grey back ground
92 | """
93 | _backgroundBrush = QtGui.QBrush(QtGui.QColor("#606060")) # create only once
94 | _foregroundBrush = QtGui.QBrush(QtGui.QColor(Qt.white)) # create only once
95 | _font = QtGui.QFont()
96 | _font.setWeight(QtGui.QFont.Bold)
97 |
98 | def __init__(self, nodeName, defaultData=None):
99 | """ Constructor. For the parameters see the AbstractCti constructor documentation.
100 | """
101 | super(MainGroupCti, self).__init__(nodeName, defaultData, expanded=True) # always expand
102 |
103 |
104 | @property
105 | def font(self):
106 | """ Returns a font for displaying this item's text in the tree.
107 | """
108 | return self._font
109 |
110 |
111 | @property
112 | def backgroundBrush(self):
113 | """ Returns a (dark gray) brush for drawing the background role in the tree.
114 | """
115 | return self._backgroundBrush
116 |
117 |
118 | @property
119 | def foregroundBrush(self):
120 | """ Returns a (white) brush for drawing the foreground role in the tree.
121 | """
122 | return self._foregroundBrush
123 |
124 |
125 | def resetRangesToDefault(self):
126 | """ Resets range settings to the default data.
127 |
128 | The base implementation does nothing. Descendants should override
129 | """
130 | pass
131 |
--------------------------------------------------------------------------------
/argos/config/stringcti.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # This file is part of Argos.
4 | #
5 | # Argos is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # Argos is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with Argos. If not, see .
17 |
18 | """ Contains the StringCti and StringCtiEditor classes
19 | """
20 | import logging
21 |
22 | from argos.config.abstractcti import AbstractCti, AbstractCtiEditor
23 | from argos.qt import QtWidgets
24 |
25 | logger = logging.getLogger(__name__)
26 |
27 |
28 | class StringCti(AbstractCti):
29 | """ Config Tree Item to store a string. It can be edited with a QLineEdit.
30 | The string can have an optional maximum length.
31 | """
32 | def __init__(self, nodeName, defaultData='', maxLength=None):
33 | """ For the (other) parameters see the AbstractCti constructor documentation.
34 |
35 | :param maxLength: maximum length of the string
36 | """
37 | super(StringCti, self).__init__(nodeName, defaultData)
38 |
39 | # We could define a mask here as well but since that very likely will be rarely used,
40 | # we don't want to store it for each cti. You can make a subclass if you need it.
41 | self.maxLength = maxLength
42 |
43 | def _enforceDataType(self, data):
44 | """ Converts to str so that this CTI always stores that type.
45 | """
46 | return str(data)
47 |
48 | @property
49 | def debugInfo(self):
50 | """ Returns the string with debugging information
51 | """
52 | return "maxLength = {}".format(self.maxLength)
53 |
54 | def createEditor(self, delegate, parent, option):
55 | """ Creates a StringCtiEditor.
56 | For the parameters see the AbstractCti constructor documentation.
57 | """
58 | return StringCtiEditor(self, delegate, parent=parent)
59 |
60 |
61 |
62 | class StringCtiEditor(AbstractCtiEditor):
63 | """ A CtiEditor which contains a QLineEdit for editing StringCti objects.
64 | """
65 | def __init__(self, cti, delegate, parent=None):
66 | """ See the AbstractCtiEditor for more info on the parameters
67 | """
68 | super(StringCtiEditor, self).__init__(cti, delegate, parent=parent)
69 | self.lineEditor = self.addSubEditor(QtWidgets.QLineEdit(), isFocusProxy=True)
70 |
71 | if cti.maxLength is not None:
72 | self.lineEditor.setMaxLength(cti.maxLength)
73 |
74 |
75 | def setData(self, data):
76 | """ Provides the main editor widget with a data to manipulate.
77 | """
78 | self.lineEditor.setText(str(data))
79 |
80 |
81 | def getData(self):
82 | """ Gets data from the editor widget.
83 | """
84 | return self.lineEditor.text()
85 |
86 |
--------------------------------------------------------------------------------
/argos/config/untypedcti.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # This file is part of Argos.
4 | #
5 | # Argos is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # Argos is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with Argos. If not, see .
17 |
18 | """ Contains the UntypedCti and UntypedCtiEditor classes
19 | """
20 | import logging
21 |
22 | from argos.config.abstractcti import AbstractCti, AbstractCtiEditor
23 | from argos.qt import QtWidgets, Qt
24 |
25 | logger = logging.getLogger(__name__)
26 |
27 |
28 |
29 | class UntypedCti(AbstractCti):
30 | """ Config Tree Item to store a any type of data as long as it can be edited with a QLineEdit.
31 |
32 | Typically it's better to use 'typed' CTIs, where the data is always internally stored in
33 | the same type (enforced by _enforceDataType).
34 |
35 | This item is non-editable and can, for instance, be used for introspection.
36 | """
37 | def __init__(self, nodeName, defaultData='', doc=''):
38 | """ Constructor. For the parameters see the AbstractCti constructor documentation.
39 | """
40 | super(UntypedCti, self).__init__(nodeName, defaultData)
41 | self.doc = doc
42 |
43 | def _enforceDataType(self, value):
44 | """ Since UntypedCti can store any type of data no conversion will be done.
45 | """
46 | return value
47 |
48 | def createEditor(self, delegate, parent, option):
49 | """ Creates an UntypedCtiEditor.
50 | For the parameters see the AbstractCti constructor documentation.
51 | Note: since the item is not editable this will never be called.
52 | """
53 | return UntypedCtiEditor(self, delegate, parent=parent)
54 |
55 | @property
56 | def valueColumnItemFlags(self):
57 | """ Returns the flags determine how the user can interact with the value column.
58 | Returns Qt.NoItemFlags so that the item is not editable
59 | """
60 | return Qt.NoItemFlags
61 |
62 |
63 | class UntypedCtiEditor(AbstractCtiEditor):
64 | """ A CtiEditor which contains a QLineEdit for editing UntypedCti objects.
65 |
66 | Only
67 | """
68 | def __init__(self, cti, delegate, parent=None):
69 | """ See the AbstractCtiEditor for more info on the parameters
70 | """
71 | super(UntypedCtiEditor, self).__init__(cti, delegate, parent=parent)
72 | self.lineEditor = self.addSubEditor(QtWidgets.QLineEdit(), isFocusProxy=True)
73 |
74 | def setData(self, data):
75 | """ Provides the main editor widget with a data to manipulate.
76 | """
77 | self.lineEditor.setText(str(data))
78 |
79 | def getData(self):
80 | """ Gets data from the editor widget.
81 | """
82 | return self.lineEditor.text()
83 |
84 |
85 |
--------------------------------------------------------------------------------
/argos/external/__init__.py:
--------------------------------------------------------------------------------
1 | """ 3rd party modules and packages included in argos.
2 | """
3 | import logging
4 | import os
5 |
6 | logger = logging.getLogger(__name__)
7 | #
8 | # # Set the ARGOS_CMLIB_PATH to the path of the CmLib if you want to use another version of this
9 | # # package, for instance during development. E.g export ARGOS_CMLIB_PATH=/Users/kenter/prog/py/cmlib/
10 | # cmLibPath = os.environ.get("ARGOS_CMLIB_PATH")
11 | #
12 | # if cmLibPath is not None:
13 | # logger.debug("Prepending to external package path: {}".format(cmLibPath))
14 | # __path__.insert(0, cmLibPath)
15 |
16 |
17 |
--------------------------------------------------------------------------------
/argos/img/argos.css:
--------------------------------------------------------------------------------
1 | /* Qt Cascading Style Sheet (qss)
2 |
3 |
4 | background: palette(Mid); # Old value of the QDockWidget
5 |
6 | */
7 |
8 |
9 | QDockWidget::title {
10 | background: #B8B8B8;
11 | color: pallete(Link);
12 | text-align: center;
13 | }
14 |
15 | /*
16 | QDockWidget::title:hover {
17 | background: red;
18 | border: 1px solid red;
19 | color: yellow;
20 | }*/
21 |
22 | BasePanel {
23 | margin: 5px;
24 | }
25 |
26 | InspectorSelectionPane {
27 | margin: 5px;
28 | margin-bottom: 0px;
29 | }
30 |
31 | QFrame#message_label {
32 | color: #FF0066;
33 | }
34 |
35 | QMainWindow::separator {
36 | background: black;
37 | width: 1px;
38 | height: 1px;
39 | }
40 |
41 | ToggleColumnTableWidget::item {
42 | margin: 3px;
43 | }
44 |
45 | /*
46 | QMainWindow::separator {
47 | background: palette(Midlight);
48 | }
49 |
50 | QMainWindow::separator:hover {
51 | background: palette(Mid);
52 | }*/
53 | /*
54 | DockWidgetTitleBar {
55 | background: yellow;
56 | border: 1px solid black;
57 | }*/
58 |
--------------------------------------------------------------------------------
/argos/img/snipicons/README.txt:
--------------------------------------------------------------------------------
1 | The original snip icons. See http://snipicons.com/
2 |
--------------------------------------------------------------------------------
/argos/img/snipicons/align-left.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
38 |
--------------------------------------------------------------------------------
/argos/img/snipicons/arrow-down-l.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
22 |
--------------------------------------------------------------------------------
/argos/img/snipicons/arrow-left-l.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
22 |
--------------------------------------------------------------------------------
/argos/img/snipicons/arrow-right-l.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
22 |
--------------------------------------------------------------------------------
/argos/img/snipicons/arrow-up-l.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
22 |
--------------------------------------------------------------------------------
/argos/img/snipicons/asterisk.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
24 |
--------------------------------------------------------------------------------
/argos/img/snipicons/circle-arrow-down-l.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
26 |
--------------------------------------------------------------------------------
/argos/img/snipicons/circle-arrow-left-l.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
26 |
--------------------------------------------------------------------------------
/argos/img/snipicons/circle-arrow-right-l.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
26 |
--------------------------------------------------------------------------------
/argos/img/snipicons/circle-arrow-up-l.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
26 |
--------------------------------------------------------------------------------
/argos/img/snipicons/exclamation-sign.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
36 |
--------------------------------------------------------------------------------
/argos/img/snipicons/file-inverse.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
22 |
--------------------------------------------------------------------------------
/argos/img/snipicons/file.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
21 |
--------------------------------------------------------------------------------
/argos/img/snipicons/folder-close.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
35 |
--------------------------------------------------------------------------------
/argos/img/snipicons/folder-open.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
35 |
--------------------------------------------------------------------------------
/argos/img/snipicons/leaf.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
24 |
--------------------------------------------------------------------------------
/argos/img/snipicons/list.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
34 |
--------------------------------------------------------------------------------
/argos/img/snipicons/minus-l.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
20 |
--------------------------------------------------------------------------------
/argos/img/snipicons/minus-sign-l.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
22 |
--------------------------------------------------------------------------------
/argos/img/snipicons/move.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
24 |
--------------------------------------------------------------------------------
/argos/img/snipicons/plus-sign-l.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
23 |
--------------------------------------------------------------------------------
/argos/img/snipicons/remove-circle-l.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
23 |
--------------------------------------------------------------------------------
/argos/img/snipicons/remove-sign-l.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
14 |
--------------------------------------------------------------------------------
/argos/img/snipicons/reset-l.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/argos/img/snipicons/th-large.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
18 |
--------------------------------------------------------------------------------
/argos/img/snipicons/th-list.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
12 |
--------------------------------------------------------------------------------
/argos/img/snipicons/transparent_1x1.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/argos/img/snipicons/warning-sign.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
23 |
--------------------------------------------------------------------------------
/argos/info.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # This file is part of Argos.
4 | #
5 | # Argos is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # Argos is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with Argos. If not, see .
17 |
18 | """ Version and other info for this program
19 | """
20 | import sys
21 |
22 | # We bypass the argparse mechanism in main.py because this import is executed before main.main()
23 | DEBUGGING = ('-d' in sys.argv or '--debug' in sys.argv)
24 | TESTING = True # add some test menu options
25 | PROFILING = False# and DEBUGGING
26 |
27 | VERSION = '0.4.4'
28 | REPO_NAME = "argos"
29 | SCRIPT_NAME = "argos"
30 | PACKAGE_NAME = "argos"
31 | PROJECT_NAME = "Argos"
32 | SHORT_DESCRIPTION = "Argos HDF5/NetCDF/scientific data viewer."
33 | PROJECT_URL = "https://github.com/titusjan/argos"
34 | AUTHOR = "Pepijn Kenter"
35 | EMAIL = "titusjan@gmail.com"
36 | ORGANIZATION_NAME = "titusjan"
37 | ORGANIZATION_DOMAIN = "titusjan.nl"
38 |
39 | EXIT_CODE_SUCCESS = 0
40 | EXIT_CODE_ERROR = 1
41 | EXIT_CODE_COMMAND_ARGS = 2
42 | EXIT_CODE_RESTART = 66 # Indicates the program is being 'restarted'
43 |
44 | KEY_PROGRAM = '_program'
45 | KEY_VERSION = '_version'
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/argos/inspector/__init__.py:
--------------------------------------------------------------------------------
1 | """ Inspector package.
2 |
3 | The default plugins are created (on registry reset) by InspectorRegistry.getDefaultItems()
4 | """
5 |
--------------------------------------------------------------------------------
/argos/inspector/debug.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # This file is part of Argos.
4 | #
5 | # Argos is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # Argos is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with Argos. If not, see .
17 |
18 | """ Debug inspector.
19 | """
20 | import logging
21 |
22 | from argos.info import DEBUGGING
23 | from argos.config.groupcti import MainGroupCti, GroupCti
24 | from argos.inspector.abstract import AbstractInspector
25 | from argos.qt import Qt, QtGui, QtWidgets
26 |
27 | logger = logging.getLogger(__name__)
28 |
29 |
30 | class DebugInspector(AbstractInspector):
31 | """ Empty inspector, mainly for debugging purposes.
32 | """
33 | def __init__(self, collector, parent=None):
34 |
35 | super(DebugInspector, self).__init__(collector, parent=parent)
36 |
37 | self.label = QtWidgets.QLabel()
38 | self.label.setWordWrap(True)
39 | self.contentsLayout.addWidget(self.label)
40 |
41 | self._config = self._createConfig()
42 |
43 |
44 | @classmethod
45 | def axesNames(cls):
46 | """ The names of the axes that this inspector visualizes.
47 | See the parent class documentation for a more detailed explanation.
48 | """
49 | return tuple()
50 |
51 |
52 | def _createConfig(self):
53 | """ Creates a config tree item (CTI) hierarchy containing default children.
54 | """
55 | rootItem = MainGroupCti('debug inspector')
56 |
57 | if DEBUGGING:
58 | # Some test config items.
59 | import numpy as np
60 | from argos.config.untypedcti import UntypedCti
61 | from argos.config.stringcti import StringCti
62 | from argos.config.intcti import IntCti
63 | from argos.config.floatcti import FloatCti, SnFloatCti
64 | from argos.config.boolcti import BoolCti, BoolGroupCti
65 | from argos.config.choicecti import ChoiceCti
66 | from argos.config.qtctis import PenCti
67 |
68 | grpItem = GroupCti("group")
69 | rootItem.insertChild(grpItem)
70 |
71 | lcItem = UntypedCti('line color', 123)
72 | grpItem.insertChild(lcItem)
73 |
74 | disabledItem = rootItem.insertChild(StringCti('disabled', "Can't touch me"))
75 | disabledItem.enabled=False
76 |
77 | grpItem.insertChild(IntCti('line-1 color', 7, minValue = -5, stepSize=2,
78 | prefix="@", suffix="%", specialValueText="I'm special"))
79 | rootItem.insertChild(StringCti('letter', 'aa', maxLength = 1))
80 | grpItem.insertChild(FloatCti('width', 2, minValue =5, stepSize=0.45, decimals=3,
81 | prefix="@", suffix="%", specialValueText="so very special"))
82 | grpItem.insertChild(SnFloatCti('scientific', defaultData=-np.inf))
83 |
84 | gridItem = rootItem.insertChild(BoolGroupCti('grid', True))
85 | gridItem.insertChild(BoolCti('X-Axis', True))
86 | gridItem.insertChild(BoolCti('Y-Axis', False))
87 |
88 | rootItem.insertChild(ChoiceCti('hobbit', 2, editable=True,
89 | configValues=['Frodo', 'Sam', 'Pippin', 'Merry']))
90 | myPen = QtGui.QPen(QtGui.QColor('#1C8857'))
91 | myPen.setWidth(2)
92 | myPen.setStyle(Qt.DashDotDotLine)
93 | rootItem.insertChild(PenCti('line', False, resetTo=myPen))
94 |
95 | return rootItem
96 |
97 |
98 | def _drawContents(self, reason=None, initiator=None):
99 | """ Draws the table contents from the sliced array of the collected repo tree item.
100 |
101 | The reason and initiator parameters are ignored.
102 | See AbstractInspector.updateContents for their description.
103 | """
104 | logger.debug("DebugInspector._drawContents: {}".format(self))
105 |
106 | slicedArray = self.collector.getSlicedArray()
107 | if slicedArray is None:
108 | text = ""
109 | else:
110 | text = ("data = {!r}, masked = {!r}, fill_value = {!r} (?= {}: {})"
111 | .format(slicedArray.data, slicedArray.mask, slicedArray.fill_value,
112 | slicedArray.data.item(),
113 | slicedArray.data.item() == slicedArray.fill_value))
114 |
115 | logger.debug("_drawContents: {}".format(text))
116 | logger.debug("_drawContents: {!r}".format(slicedArray))
117 |
118 | if DEBUGGING:
119 | self.label.setText(text)
120 |
121 |
122 | def _clearContents(self):
123 | """ Override to clear the widget contents.
124 |
125 | Is called to empty the inspector when an exception occurs during _drawContents,
126 | to prevent further errors.
127 | """
128 | logger.debug("DebugInspector._clearContents called")
129 |
130 |
--------------------------------------------------------------------------------
/argos/inspector/dialog.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # This file is part of Argos.
3 | #
4 | # Argos is free software: you can redistribute it and/or modify
5 | # it under the terms of the GNU General Public License as published by
6 | # the Free Software Foundation, either version 3 of the License, or
7 | # (at your option) any later version.
8 | #
9 | # Argos is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | # GNU General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public License
15 | # along with Argos. If not, see .
16 |
17 | """
18 | OpenInspectorDialog dialog window that lets the user pick a new inspector.
19 | """
20 | from __future__ import print_function
21 |
22 | import logging
23 |
24 | from argos.qt import Qt, QtCore, QtWidgets
25 | from argos.inspector.registry import InspectorRegItem
26 | from argos.utils.cls import checkType
27 | from argos.widgets.pluginsdialog import RegistryTab
28 |
29 |
30 | logger = logging.getLogger(__name__)
31 |
32 |
33 | # The main window inherits from a Qt class, therefore it has many
34 | # ancestors public methods and attributes.
35 | # pylint: disable=too-many-ancestors, too-many-instance-attributes, too-many-public-methods, attribute-defined-outside-init
36 |
37 | class OpenInspectorDialog(QtWidgets.QDialog):
38 | """ Dialog window that allows the user to open an inspector plugins.
39 |
40 | SIDE EFFECT: will try to import all underlying inspector classes.
41 | This is done so that help and number of dimensions can be displayed.
42 | """
43 |
44 | def __init__(self, registry, onlyShowImported=False, parent=None):
45 | """ Constructor
46 | """
47 | super(OpenInspectorDialog, self).__init__(parent=parent)
48 |
49 | self._registry = registry
50 |
51 | self.setWindowTitle('Open Inspector')
52 | self.setModal(True)
53 | layout = QtWidgets.QVBoxLayout(self)
54 |
55 | attrNames = ['name', 'library', 'nDims']
56 | headerSizes = [250, 250, None]
57 |
58 | self.inspectorTab = RegistryTab(registry, attrNames=attrNames, headerSizes=headerSizes,
59 | onlyShowImported=onlyShowImported)
60 | self.inspectorTab.tableView.sortByColumn(1, Qt.AscendingOrder) # sort by library
61 | layout.addWidget(self.inspectorTab)
62 |
63 | # Buttons
64 | buttonBox = QtWidgets.QDialogButtonBox(QtWidgets.QDialogButtonBox.Ok |
65 | QtWidgets.QDialogButtonBox.Cancel)
66 | buttonBox.accepted.connect(self.accept)
67 | buttonBox.rejected.connect(self.reject)
68 | layout.addWidget(buttonBox)
69 |
70 | # Double clicking is equivalent to selecting it and clicking Ok.
71 | self.inspectorTab.tableView.doubleClicked.connect(self.accept)
72 |
73 | self.resize(QtCore.QSize(800, 600))
74 |
75 | #self.inspectorTab.tryImportAllPlugins()
76 |
77 |
78 | def getCurrentInspectorRegItem(self):
79 | """ Returns the inspector that is currently selected in the table.
80 | Can return None if there is no data in the table
81 | """
82 | return self.inspectorTab.getCurrentRegItem()
83 |
84 |
85 | def setCurrentInspectorRegItem(self, regItem):
86 | """ Sets the current inspector given an InspectorRegItem
87 | """
88 | checkType(regItem, InspectorRegItem, allowNone=True)
89 | self.inspectorTab.setCurrentRegItem(regItem)
90 |
91 |
92 |
93 |
--------------------------------------------------------------------------------
/argos/inspector/errormsg.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # This file is part of Argos.
4 | #
5 | # Argos is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # Argos is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with Argos. If not, see .
17 |
18 | """ Inspector that only shows an (error) message
19 | """
20 | import logging
21 |
22 | from argos.config.groupcti import MainGroupCti
23 |
24 | from argos.inspector.abstract import AbstractInspector
25 | from argos.utils.cls import checkIsAString
26 |
27 |
28 | logger = logging.getLogger(__name__)
29 |
30 |
31 | class ErrorMsgInspector(AbstractInspector):
32 | """ Inspector that shows an error message
33 |
34 | :param str msg: the string that will be
35 | """
36 | def __init__(self, collector, msg, parent=None):
37 |
38 | super(ErrorMsgInspector, self).__init__(collector, parent=parent)
39 |
40 | checkIsAString(msg)
41 | self.msg = msg
42 |
43 | self._config = self._createConfig()
44 |
45 | self.setCurrentIndex(self.ERROR_PAGE_IDX)
46 |
47 |
48 | @classmethod
49 | def axesNames(cls):
50 | """ The names of the axes that this inspector visualizes.
51 | See the parent class documentation for a more detailed explanation.
52 | """
53 | return tuple()
54 |
55 |
56 | def _createConfig(self):
57 | """ Creates a config tree item (CTI) hierarchy containing default children.
58 | """
59 | rootItem = MainGroupCti('message inspector')
60 | return rootItem
61 |
62 |
63 | def updateContents(self, reason=None, initiator=None):
64 | """ Override updateContents. Shows the error message
65 | """
66 | self.setCurrentIndex(self.ERROR_PAGE_IDX)
67 | self._showError(msg=self.msg, title="Error")
68 |
69 |
--------------------------------------------------------------------------------
/argos/inspector/pgplugins/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # This file is part of Argos.
4 | #
5 | # Argos is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # Argos is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with Argos. If not, see .
17 |
18 | """ Plugins that use the PyQtGraph plot library.
19 |
20 | The default plugins are created (on registry reset) by InspectorRegistry.getDefaultItems()
21 | """
22 | import logging
23 |
24 | logger = logging.getLogger(__name__)
25 |
26 | try:
27 | import pyqtgraph as pg
28 | except Exception as ex:
29 | logger.exception(ex)
30 | raise ImportError("PyQtGraph 0.10.0 or higher required")
31 |
32 |
33 | logger.debug("Imported PyQtGraph: {}".format(pg.__version__))
34 |
35 |
36 | def setPgConfigOptions(**kwargs):
37 | """ Sets the PyQtGraph config options and emits a log message
38 | """
39 | for key, value in kwargs.items():
40 | logger.debug("Setting PyQtGraph config option: {} = {}".format(key, value))
41 |
42 | pg.setConfigOptions(**kwargs)
43 |
44 |
45 | # Sets some config options
46 | setPgConfigOptions(exitCleanup=False, crashWarning=True,
47 | antialias=False, # Anti aliasing of lines having width > 1 may be slow (OS-X)
48 | leftButtonPan=True, # If False, left button drags a rubber band for zooming
49 | foreground='k', # Default foreground color for axes, labels, etc.
50 | background='w') # Default background for GraphicsWidget
51 |
52 |
53 |
--------------------------------------------------------------------------------
/argos/inspector/pgplugins/colorbar.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # This file is part of Argos.
4 | #
5 | # Argos is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # Argos is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with Argos. If not, see .
17 |
18 | """ Module that contains ArgosPgColorBar.
19 | """
20 | from __future__ import division, print_function
21 |
22 | import logging
23 | import warnings
24 |
25 | import pyqtgraph as pg
26 |
27 | from argos.qt import QtCore, QtWidgets, QtSignal
28 |
29 |
30 | from argos.utils.cls import checkType
31 | from pgcolorbar.colorlegend import ColorLegendItem
32 |
33 | # from argos.info import DEBUGGING
34 | # from argos.qt import Qt, QtCore, QtWidgets, QtSignal
35 |
36 | logger = logging.getLogger(__name__)
37 |
38 | warnings.filterwarnings(action='default', category=RuntimeWarning, module='pgcolorbar.colorlegend') # Show once
39 |
40 |
41 | class ArgosColorLegendItem(ColorLegendItem):
42 | """ Wrapper around pgcolorbar.colorlegend.ColorLegendItem.
43 |
44 | Suppresses the FutureWarning of PyQtGraph in _updateHistogram.
45 | Overrides the _imageItemHasIntegerData method.
46 | Adds context menu with reset color scale action.
47 | Middle mouse click resets the axis with the settings in the config tree.
48 | """
49 | # Use a QueuedConnection to connect to sigResetColorScale so that the reset is scheduled after
50 | # all current events have been processed. Otherwise the mouseReleaseEvent may look for a
51 | # PlotCurveItem that is no longer present after the reset, which results in a RuntimeError:
52 | # wrapped C/C++ object of type PlotCurveItem has been deleted.
53 | sigResetColorScale = QtSignal() # Signal the inspectors to reset the color scale
54 |
55 |
56 | def __init__(self, *args, histHeightPercentile=99.0, **kwargs):
57 | """ Constructor
58 | """
59 | super(ArgosColorLegendItem, self).__init__(
60 | *args, histHeightPercentile=histHeightPercentile, **kwargs)
61 | self.resetColorScaleAction = QtWidgets.QAction("Reset Color Range", self)
62 | self.resetColorScaleAction.triggered.connect(self.emitResetColorScaleSignal)
63 | self.resetColorScaleAction.setToolTip("Reset the range of the color scale.")
64 | self.addAction(self.resetColorScaleAction)
65 |
66 |
67 | @classmethod
68 | def _imageItemHasIntegerData(cls, imageItem):
69 | """ Returns True if the imageItem contains integer data.
70 |
71 | Overriden so that the ImagePlotItem can replace integer arrays with float arrays (to
72 | plot masked values as NaNs) while it still calculates the histogram bins as if it
73 | where integers (to prevent aliasing)
74 | """
75 | checkType(imageItem, pg.ImageItem, allowNone=True)
76 |
77 | if hasattr(imageItem, '_wasIntegerData'):
78 | return imageItem._wasIntegerData
79 | else:
80 | return super(ArgosColorLegendItem, cls)._imageItemHasIntegerData(imageItem)
81 |
82 |
83 | def emitResetColorScaleSignal(self):
84 | """ Emits the sigColorScaleReset to request the inspectors to reset the color scale
85 | """
86 | logger.debug("Emitting sigColorScaleReset() for {!r}".format(self))
87 | self.sigResetColorScale.emit()
88 |
89 |
90 | def mouseClickEvent(self, mouseClickEvent):
91 | """ Handles (PyQtGraph) mouse click events.
92 |
93 | Overrides the middle mouse click to reset using the settings in the config tree.
94 |
95 | Opens the context menu if a right mouse button was clicked. (We can't simply use
96 | setContextMenuPolicy(Qt.ActionsContextMenu because the PlotItem class does not derive
97 | from QWidget).
98 |
99 | :param mouseClickEvent: pyqtgraph.GraphicsScene.mouseEvents.MouseClickEvent
100 | """
101 | if mouseClickEvent.button() in self.resetRangeMouseButtons:
102 | self.emitResetColorScaleSignal()
103 | mouseClickEvent.accept()
104 |
105 | elif mouseClickEvent.button() == QtCore.Qt.RightButton:
106 | contextMenu = QtWidgets.QMenu()
107 | for action in self.actions():
108 | contextMenu.addAction(action)
109 |
110 | screenPos = mouseClickEvent.screenPos() # Screenpos is a QPointF, convert to QPoint.
111 | screenX = round(screenPos.x())
112 | screenY = round(screenPos.y())
113 | contextMenu.exec_(QtCore.QPoint(screenX, screenY))
114 |
115 | else:
116 | super(ArgosColorLegendItem, self).mouseClickEvent(mouseClickEvent)
117 |
118 |
--------------------------------------------------------------------------------
/argos/inspector/pgplugins/uml.dia:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/titusjan/argos/5a334a5d5625300dab5f4e98a36e924600b6e55e/argos/inspector/pgplugins/uml.dia
--------------------------------------------------------------------------------
/argos/inspector/pgplugins/uml.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/titusjan/argos/5a334a5d5625300dab5f4e98a36e924600b6e55e/argos/inspector/pgplugins/uml.pdf
--------------------------------------------------------------------------------
/argos/inspector/pgplugins/uml.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/titusjan/argos/5a334a5d5625300dab5f4e98a36e924600b6e55e/argos/inspector/pgplugins/uml.png
--------------------------------------------------------------------------------
/argos/inspector/qtplugins/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # This file is part of Argos.
4 | #
5 | # Argos is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # Argos is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with Argos. If not, see .
17 |
18 | """ Plugins that use Qt widgets.
19 |
20 | The default plugins are created (on registry reset) by InspectorRegistry.getDefaultItems()
21 | """
22 |
--------------------------------------------------------------------------------
/argos/inspector/registry.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # This file is part of Argos.
3 | #
4 | # Argos is free software: you can redistribute it and/or modify
5 | # it under the terms of the GNU General Public License as published by
6 | # the Free Software Foundation, either version 3 of the License, or
7 | # (at your option) any later version.
8 | #
9 | # Argos is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | # GNU General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public License
15 | # along with Argos. If not, see .
16 |
17 | """ Defines a global Inspector registry to register inspector plugins.
18 | """
19 |
20 | import logging
21 |
22 | from argos.info import DEBUGGING
23 | from argos.reg.basereg import BaseRegItem, BaseRegistry, RegType
24 |
25 | logger = logging.getLogger(__name__)
26 |
27 | DEFAULT_INSPECTOR = 'Table'
28 |
29 | class InspectorRegItem(BaseRegItem):
30 | """ Class to keep track of a registered Inspector.
31 | Has a create() method that functions as an Inspector factory.
32 | """
33 | FIELDS = BaseRegItem.FIELDS[:1] + ['shortCut'] + BaseRegItem.FIELDS[1:]
34 | TYPES = BaseRegItem.TYPES[:1] + [RegType.ShortCut] + BaseRegItem.TYPES[1:]
35 | LABELS = BaseRegItem.LABELS[:1] + ['Shortcut'] + BaseRegItem.LABELS[1:]
36 | STRETCH = BaseRegItem.STRETCH[:1] + [False] + BaseRegItem.STRETCH[1:]
37 |
38 |
39 | def __init__(self, name='', absClassName='', pythonPath='', shortCut=''):
40 | """ Constructor. See the ClassRegItem class doc string for the parameter help.
41 | """
42 | super(InspectorRegItem, self).__init__(name=name, absClassName=absClassName,
43 | pythonPath=pythonPath)
44 | self._data['shortCut'] = shortCut
45 |
46 |
47 | @property
48 | def shortCut(self):
49 | """ Keyboard short cut
50 | """
51 | return self._data['shortCut']
52 |
53 |
54 | @property
55 | def axesNames(self):
56 | """ The axes names of the inspector.
57 | """
58 | return [] if self.cls is None else self.cls.axesNames()
59 |
60 | @property
61 | def nDims(self):
62 | """ The number of axes of this inspector.
63 | """
64 | return len(self.axesNames)
65 |
66 |
67 | def create(self, collector, tryImport=True):
68 | """ Creates an inspector of the registered and passes the collector to the constructor.
69 | Tries to import the class if tryImport is True.
70 | Raises ImportError if the class could not be imported.
71 | """
72 | cls = self.getClass(tryImport=tryImport)
73 | if not self.successfullyImported:
74 | raise ImportError("Class not successfully imported: {}".format(self.exception))
75 | return cls(collector)
76 |
77 |
78 |
79 | class InspectorRegistry(BaseRegistry):
80 | """ Class that maintains the collection of registered inspector classes.
81 | See the base class documentation for more info.
82 | """
83 | ITEM_CLASS = InspectorRegItem
84 |
85 | def __init__(self):
86 | """ Constructor
87 | """
88 | super(InspectorRegistry, self).__init__()
89 |
90 |
91 | @property
92 | def registryName(self):
93 | """ Human readable name for this registry.
94 | """
95 | return "Inspector"
96 |
97 |
98 | def getDefaultItems(self):
99 | """ Returns a list with the default plugins in the inspector registry.
100 | """
101 | plugins = [
102 | InspectorRegItem('Line Plot',
103 | 'argos.inspector.pgplugins.lineplot1d.PgLinePlot1d',
104 | shortCut='Ctrl+1'),
105 | InspectorRegItem('Image Plot',
106 | 'argos.inspector.pgplugins.imageplot2d.PgImagePlot2d',
107 | shortCut='Ctrl+2'),
108 | InspectorRegItem(DEFAULT_INSPECTOR,
109 | 'argos.inspector.qtplugins.table.TableInspector',
110 | shortCut='Ctrl+3'),
111 | InspectorRegItem('Text',
112 | 'argos.inspector.qtplugins.text.TextInspector',
113 | shortCut='Ctrl+4'),
114 | ]
115 | if DEBUGGING:
116 | plugins.append(InspectorRegItem('Image Plot (Old)',
117 | 'argos.inspector.pgplugins.old_imageplot2d.PgImagePlot2d'))
118 | plugins.append(InspectorRegItem('Debug Inspector',
119 | 'argos.inspector.debug.DebugInspector'))
120 | return plugins
121 |
--------------------------------------------------------------------------------
/argos/inspector/selectionpane.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # This file is part of Argos.
4 | #
5 | # Argos is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # Argos is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with Argos. If not, see .
17 |
18 | """ Inspector Selection Pane
19 | """
20 | import logging
21 |
22 | from argos.qt import QtWidgets, QtSlot, Qt
23 |
24 | from argos.inspector.registry import InspectorRegItem
25 | from argos.widgets.constants import DOCK_MARGIN, DOCK_SPACING
26 | from argos.widgets.misc import setWidgetSizePolicy
27 |
28 | logger = logging.getLogger(__name__)
29 |
30 | NO_INSPECTOR_LABEL = ''
31 |
32 |
33 | class InspectorSelectionPane(QtWidgets.QFrame):
34 | """ Shows the attributes of the selected repo tree item
35 | """
36 |
37 | def __init__(self, inspectorActionGroup, parent=None):
38 | super(InspectorSelectionPane, self).__init__(parent=parent)
39 |
40 | # self.setFrameShape(QtWidgets.QFrame.Box)
41 | self.menuButton = QtWidgets.QPushButton(NO_INSPECTOR_LABEL)
42 | self.menuButton.setMinimumWidth(10)
43 |
44 | inspectorMenu = QtWidgets.QMenu("Change Inspector", parent=self.menuButton)
45 | for action in inspectorActionGroup.actions():
46 | inspectorMenu.addAction(action)
47 | self.menuButton.setMenu(inspectorMenu)
48 |
49 | self.messageLabel = QtWidgets.QLabel("")
50 | self.messageLabel.setObjectName("message_label")
51 | self.messageLabel.setFrameStyle(QtWidgets.QFrame.Panel)
52 | self.messageLabel.setFrameShadow(QtWidgets.QFrame.Sunken)
53 | self.messageLabel.setTextInteractionFlags(Qt.TextSelectableByMouse)
54 |
55 | self.mainLayout = QtWidgets.QHBoxLayout()
56 | self.mainLayout.setContentsMargins(DOCK_MARGIN, DOCK_MARGIN, DOCK_MARGIN, DOCK_MARGIN)
57 | self.setLayout(self.mainLayout)
58 |
59 | self.mainLayout.addWidget(self.menuButton, stretch=0)
60 | self.mainLayout.addWidget(self.messageLabel, stretch=1)
61 |
62 | setWidgetSizePolicy(self.menuButton, hor=QtWidgets.QSizePolicy.Minimum)
63 | setWidgetSizePolicy(self.messageLabel, hor=QtWidgets.QSizePolicy.Ignored)
64 | setWidgetSizePolicy(
65 | self, hor=QtWidgets.QSizePolicy.MinimumExpanding, ver=QtWidgets.QSizePolicy.Fixed)
66 |
67 |
68 | def showMessage(self, msg):
69 | """ Sets the message label to a message text
70 | """
71 | logger.debug("Show message to user: '{}'".format(msg))
72 | self.messageLabel.setText(msg)
73 | self.messageLabel.setToolTip(msg)
74 |
75 |
76 | @QtSlot(InspectorRegItem)
77 | def updateFromInspectorRegItem(self, inspectorRegItem):
78 | """ Updates the label from the full name of the InspectorRegItem
79 | """
80 | label = NO_INSPECTOR_LABEL if inspectorRegItem is None else inspectorRegItem.name
81 | self.menuButton.setText(label)
82 |
--------------------------------------------------------------------------------
/argos/mytest.py:
--------------------------------------------------------------------------------
1 | """ Small test program to check documentation in sphinx
2 | """
3 | from typing import List
4 |
5 | from argos.repo.baserti import BaseRti
6 |
7 | # Args:
8 | # rtis: candidates
9 |
10 |
11 | class MyClass():
12 | """ My class documentation
13 | """
14 |
15 | def __init__(self, rti: BaseRti):
16 | """ Constructor documentation
17 |
18 | Args:
19 | rti: Repo Tree Item
20 | """
21 | pass
22 |
23 |
24 | def yourMethod(self, a:int):
25 | """ Fine, do it your way then
26 |
27 | :param a: some parameter with a bad name.
28 | """
29 |
30 |
31 |
32 | def bestRti(rtis: List[BaseRti], fna: float) -> BaseRti:
33 | """ Test function to see how parameters are documented
34 |
35 | Selects the best RTI in the whole of Utoxeter.
36 |
37 | Args:
38 | rtis:
39 | The list of candidates
40 | fna: specifies a fnaggel to assist in the search
41 |
42 | Returns:
43 | The best we've found
44 | """
45 | pass
46 |
47 |
--------------------------------------------------------------------------------
/argos/qt/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # This file is part of Argos.
4 | #
5 | # Argos is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # Argos is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with Argos. If not, see .
17 |
18 | """ Contains classes and functions based on Qt. These could in principle be useful for
19 | other projects but would then require minor tweaking (e.g. imports).
20 | """
21 | # Import commonly used function into the package name space for convenience.
22 |
23 | from argos.qt.bindings import Qt, QUrl, QtCore, QtGui, QtWidgets, QtSvg, QtSignal, QtSlot
24 | from argos.qt.misc import initQApplication #, initQCoreApplication
25 |
--------------------------------------------------------------------------------
/argos/qt/bindings.py:
--------------------------------------------------------------------------------
1 | """ Harmonize PyQt5 and PySide2 bindings
2 |
3 | If the QT_API environment variable is set to pyside2 or pyqt5 that library is imported.
4 | Else it looks if one of those two is already imported.
5 |
6 | Then it will try first PyQt5 and then PySide2.
7 |
8 | Note that PySide2 at the moment does not fully work yet. There are problems with the color
9 | picker. Also (for now) is not officially supported! There are already enough dependencies
10 | that can vary (Python 2 & 3, Windows & Linux & OS-X, etc) so I don't want to support even more
11 | combinations.
12 | """
13 | import logging
14 | import os
15 | import sys
16 |
17 | logger = logging.getLogger(__name__)
18 |
19 | API_PYSIDE2 = 'pyside2'
20 | API_PYQT5 = 'pyqt5'
21 | ALL_API = [API_PYQT5, API_PYSIDE2]
22 |
23 | # Determine from environment variable
24 | QT_API_NAME = os.environ.get('QT_API')
25 |
26 | # Discard unsupported bindings (e.g. pyqt4)
27 | if QT_API_NAME not in ALL_API:
28 | QT_API_NAME = None
29 |
30 | # Else see if module is already imported.
31 | if QT_API_NAME is None:
32 | if 'PyQt5' in sys.modules:
33 | QT_API_NAME = API_PYQT5
34 |
35 | if QT_API_NAME is None:
36 | if 'PySide2' in sys.modules:
37 | QT_API_NAME = API_PYSIDE2
38 |
39 |
40 | # Finally, try the modules.
41 | if QT_API_NAME is None:
42 | try:
43 | import PyQt5
44 | except ModuleNotFoundError as ex:
45 | logger.debug("Tried but failed to import PyQt5: {}".format(ex))
46 | pass
47 | else:
48 | QT_API_NAME = API_PYQT5
49 |
50 | if QT_API_NAME is None:
51 | try:
52 | import PySide2
53 | except ModuleNotFoundError as ex:
54 | logger.debug("Tried but failed to import PySide2: {}".format(ex))
55 | else:
56 | QT_API_NAME = API_PYSIDE2
57 |
58 |
59 | # Do imports.
60 |
61 | if QT_API_NAME == API_PYQT5:
62 | from PyQt5 import QtCore, QtGui, QtWidgets, QtSvg
63 | from PyQt5.QtCore import Qt, QUrl
64 | from PyQt5.QtCore import pyqtSignal as QtSignal
65 | from PyQt5.QtCore import pyqtSlot as QtSlot
66 | from PyQt5.Qt import PYQT_VERSION_STR as PYQT_VERSION
67 | from PyQt5.Qt import QT_VERSION_STR as QT_VERSION
68 |
69 | elif QT_API_NAME == API_PYSIDE2:
70 |
71 | from PySide2 import QtCore, QtGui, QtWidgets, QtSvg
72 | from PySide2.QtCore import Qt, QUrl
73 | from PySide2.QtCore import Signal as QtSignal
74 | from PySide2.QtCore import Slot as QtSlot
75 | from PySide2 import __version__ as PYQT_VERSION
76 | from PySide2.QtCore import __version__ as QT_VERSION
77 |
78 | else:
79 |
80 | raise RuntimeError(
81 | "Unable to import one of the Qt bindings: {}. The specified binding "
82 | "(QT_API environment variable) is: {!r})".format(ALL_API, QT_API_NAME))
83 |
84 |
85 |
--------------------------------------------------------------------------------
/argos/qt/colorselect.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # This file is part of Argos.
4 | #
5 | # Argos is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # Argos is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with Argos. If not, see .
17 |
18 | """ Color selector functionality. E.g. for use in the plugin config dialog
19 | """
20 | import logging
21 | from argos.qt import QtCore, QtGui, QtWidgets, Qt
22 | from argos.utils.defs import InvalidInputError
23 |
24 | logger = logging.getLogger(__name__)
25 |
26 | class ColorSelectWidget(QtWidgets.QWidget):
27 |
28 | def __init__(self, parent=None):
29 | super(ColorSelectWidget, self).__init__(parent=parent)
30 |
31 | self.mainLayout = QtWidgets.QHBoxLayout()
32 | self.mainLayout.setContentsMargins(0, 0, 0, 0)
33 | self.setLayout(self.mainLayout)
34 |
35 | self.lineEditor = QtWidgets.QLineEdit()
36 | self.lineEditor.setToolTip("Color hex code: a '#' followed by 6 hex-digits.")
37 | self.mainLayout.addWidget(self.lineEditor)
38 | self.setFocusProxy(self.lineEditor)
39 |
40 | regExp = QtCore.QRegExp(r'#[0-9A-F]{6}', Qt.CaseInsensitive)
41 | validator = QtGui.QRegExpValidator(regExp, parent=self)
42 | self.lineEditor.setValidator(validator)
43 |
44 | self.pickButton = QtWidgets.QPushButton()
45 | self.pickButton.setText("Color Picker...")
46 | self.pickButton.setToolTip("Open color dialog.")
47 | self.pickButton.setFocusPolicy(Qt.NoFocus)
48 | self.pickButton.clicked.connect(self.openColorDialog)
49 |
50 | self.mainLayout.addWidget(self.pickButton)
51 |
52 |
53 | def setData(self, qColor):
54 | """ Provides the main editor widget with a data to manipulate.
55 | """
56 | self.lineEditor.setText(qColor.name().upper())
57 | self.lineEditor.setFocus(Qt.OtherFocusReason) #
58 |
59 |
60 | def getData(self):
61 | """ Gets data from the editor widget.
62 | """
63 | text = self.lineEditor.text()
64 | if not text.startswith('#'):
65 | text = '#' + text
66 |
67 | validator = self.lineEditor.validator()
68 | if validator is not None:
69 | state, text, _ = validator.validate(text, 0)
70 | if state != QtGui.QValidator.Acceptable:
71 | raise InvalidInputError("Invalid input: {!r}".format(text))
72 |
73 | return text
74 |
75 |
76 | def openColorDialog(self):
77 | """ Opens a QColorDialog for the user
78 | """
79 | try:
80 | currentColor = self.getData()
81 | except InvalidInputError:
82 | currentColor = "#FFFFFF"
83 |
84 | qColor = QtWidgets.QColorDialog.getColor(QtGui.QColor(currentColor), self)
85 |
86 | if qColor.isValid():
87 | self.setData(qColor)
88 |
--------------------------------------------------------------------------------
/argos/qt/shortcutedit.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # This file is part of Argos.
4 | #
5 | # Argos is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # Argos is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with Argos. If not, see .
17 |
18 | """ Color selector functionality. E.g. for use in the plugin config dialog
19 | """
20 | import logging
21 | from argos.qt import QtCore, QtGui, QtWidgets, Qt
22 | from argos.utils.defs import InvalidInputError
23 |
24 | logger = logging.getLogger(__name__)
25 |
26 |
27 | class ShortCutEditor(QtWidgets.QLineEdit):
28 |
29 | def __init__(self, parent=None):
30 | super(ShortCutEditor, self).__init__(parent=parent)
31 |
32 |
33 | def keyPressEvent(self, event):
34 |
35 | key = event.key()
36 |
37 | if (Qt.Key_A <= key <= Qt.Key_Z or Qt.Key_0 <= key <= Qt.Key_9 or
38 | Qt.Key_F1 <= key <= Qt.Key_F35):
39 |
40 | seq = QtGui.QKeySequence(event.modifiers()|event.key()).toString()
41 |
42 | logger.debug("Key press event: {}: {}".format(event.key(), seq))
43 | self.setText(seq)
44 | else:
45 | super(ShortCutEditor, self).keyPressEvent(event)
46 | return
47 |
--------------------------------------------------------------------------------
/argos/reg/__init__.py:
--------------------------------------------------------------------------------
1 | """ Plugin registry
2 |
3 | The default plugins are created (on registry reset) by RtiRegistry.getDefaultItems() abd
4 | InspectorRegistry.getDefaultItems()
5 | """
6 |
7 |
8 |
--------------------------------------------------------------------------------
/argos/repo/__init__.py:
--------------------------------------------------------------------------------
1 | """ Data repository package.
2 |
3 | The default plugins are created (on registry reset) by RtiRegistry.getDefaultItems()
4 | """
5 |
--------------------------------------------------------------------------------
/argos/repo/colors.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # This file is part of Argos.
4 | #
5 | # Argos is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # Argos is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with Argos. If not, see .
17 |
18 | """ Argos Color Library
19 | """
20 | from __future__ import print_function
21 |
22 | import logging
23 | import os.path
24 |
25 | from os import listdir
26 |
27 | from cmlib import CmLib, CmLibModel, DATA_DIR
28 |
29 | from argos.utils.cls import SingletonMixin
30 |
31 | logger = logging.getLogger(__name__)
32 |
33 | # The color maps that are favorites then the program is started for the first time or reset.
34 | DEF_FAV_COLOR_MAPS = [
35 | 'SciColMaps/Oleron', 'SciColMaps/Nuuk', 'SciColMaps/Acton', 'CET/CET-CBL2', 'MatPlotLib/Gray',
36 | 'CET/CET-C2', 'CET/CET-R2', 'MatPlotLib/BrBG', 'MatPlotLib/Tab20', 'MatPlotLib/Magma',
37 | 'MatPlotLib/Tab10', 'MatPlotLib/Cubehelix', 'MatPlotLib/Viridis', 'MatPlotLib/Coolwarm']
38 |
39 | DEFAULT_COLOR_MAP = "MatPlotLib/Magma"
40 | assert DEFAULT_COLOR_MAP in DEF_FAV_COLOR_MAPS, "Default color map not in default favorites."
41 |
42 |
43 | class CmLibSingleton(CmLib, SingletonMixin):
44 |
45 | def __init__(self, **kwargs):
46 | super(CmLibSingleton, self).__init__(**kwargs)
47 |
48 | logger.debug("CmLib singleton: {}".format(self))
49 |
50 | cmDataDir = DATA_DIR
51 | logger.info("Importing color map library from: {}".format(cmDataDir))
52 |
53 | # Don't import from Color Brewer since those are already included in MatPlotLib.
54 | # With sub-sampling the color maps similar maps can be achieved as the Color Brewer maps.
55 | excludeList = ['ColorBrewer2']
56 | for path in listdir(cmDataDir):
57 | if path in excludeList:
58 | logger.debug("Not importing catalogue from exlude list: {}".format(excludeList))
59 | continue
60 |
61 | fullPath = os.path.join(cmDataDir, path)
62 | if os.path.isdir(fullPath):
63 | self.load_catalog(fullPath)
64 |
65 | logger.debug("Number of color maps: {}".format(len(self.color_maps)))
66 |
67 |
68 | class CmLibModelSingleton(CmLibModel, SingletonMixin):
69 |
70 | def __init__(self, **kwargs):
71 | super(CmLibModelSingleton, self).__init__(CmLibSingleton.instance(), **kwargs)
72 |
73 |
--------------------------------------------------------------------------------
/argos/repo/detailplugins/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # This file is part of Argos.
4 | #
5 | # Argos is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # Argos is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with Argos. If not, see .
17 |
18 | """ Repository details plugins package.
19 |
20 | The default plugins are created (on registry reset) by RtiRegistry.getDefaultItems()
21 | """
22 |
--------------------------------------------------------------------------------
/argos/repo/detailplugins/attr.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # This file is part of Argos.
4 | #
5 | # Argos is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # Argos is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with Argos. If not, see .
17 |
18 | """ RTI attributes inspector.
19 | """
20 | import logging
21 |
22 | from argos.qt import QtWidgets
23 | from argos.repo.detailpanes import DetailTablePane
24 | from argos.utils.cls import toString, typeName
25 | from argos.widgets.constants import COL_SUMMARY_WIDTH
26 |
27 | logger = logging.getLogger(__name__)
28 |
29 |
30 |
31 | class AttributesPane(DetailTablePane):
32 | """ Shows the attributes of the selected repo tree item
33 | """
34 | _label = "Attributes"
35 |
36 | HEADERS = ["Name", "Value", "Type"]
37 | (COL_ATTR_NAME, COL_VALUE, COL_ELEM_TYPE) = range(len(HEADERS))
38 |
39 | def __init__(self, repoTreeView, parent=None):
40 | super(AttributesPane, self).__init__(repoTreeView, parent=parent)
41 | self.table.setWordWrap(True)
42 | self.table.addHeaderContextMenu(enabled = {'Name': False, 'Value': False},
43 | checked = {'Type': False})
44 |
45 | tableHeader = self.table.horizontalHeader()
46 | tableHeader.resizeSection(self.COL_ATTR_NAME, 125)
47 | tableHeader.resizeSection(self.COL_VALUE, 150)
48 | tableHeader.resizeSection(self.COL_ELEM_TYPE, COL_SUMMARY_WIDTH)
49 |
50 |
51 | def _drawContents(self, currentRti=None):
52 | """ Draws the attributes of the currentRTI
53 | """
54 | #logger.debug("_drawContents: {}".format(currentRti))
55 | table = self.table
56 | table.setUpdatesEnabled(False)
57 | try:
58 | table.clearContents()
59 | verticalHeader = table.verticalHeader()
60 | verticalHeader.setSectionResizeMode(QtWidgets.QHeaderView.Fixed)
61 |
62 | attributes = currentRti.attributes if currentRti is not None else {}
63 | table.setRowCount(len(attributes))
64 |
65 | for row, (attrName, attrValue) in enumerate(sorted(attributes.items())):
66 | attrStr = toString(attrValue, decodeBytesAs='utf-8')
67 |
68 | try:
69 | type_str = typeName(attrValue)
70 | except Exception as ex:
71 | logger.exception(ex)
72 | type_str = "??>"
73 |
74 | nameItem = QtWidgets.QTableWidgetItem(attrName)
75 | nameItem.setToolTip(attrName)
76 | table.setItem(row, self.COL_ATTR_NAME, nameItem)
77 | valItem = QtWidgets.QTableWidgetItem(attrStr)
78 | valItem.setToolTip(attrStr)
79 | table.setItem(row, self.COL_VALUE, valItem)
80 | table.setItem(row, self.COL_ELEM_TYPE, QtWidgets.QTableWidgetItem(type_str))
81 | table.resizeRowToContents(row)
82 |
83 | verticalHeader.setSectionResizeMode(QtWidgets.QHeaderView.ResizeToContents)
84 |
85 | finally:
86 | table.setUpdatesEnabled(True)
87 |
88 |
89 |
90 |
91 |
--------------------------------------------------------------------------------
/argos/repo/detailplugins/prop.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # This file is part of Argos.
4 | #
5 | # Argos is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # Argos is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with Argos. If not, see .
17 |
18 | """ RTI properties inspector.
19 | """
20 | import logging
21 |
22 | from argos.qt import QtWidgets, QtCore, Qt
23 | from argos.repo.detailpanes import DetailTablePane
24 | from argos.repo.repotreemodel import RepoTreeModel
25 | from argos.utils.misc import replaceEolChars
26 |
27 | logger = logging.getLogger(__name__)
28 |
29 |
30 | class PropertiesPane(DetailTablePane):
31 | """ Shows the properties of the selected repo tree item.
32 |
33 | The properties correspond to a column in the repository tree view but typically those
34 | columns are hidden to save screen space. That's why this details pane is useful.
35 | """
36 | _label = "Properties"
37 |
38 | HEADERS = ["Name", "Value"]
39 | (COL_PROP_NAME, COL_VALUE) = range(len(HEADERS))
40 |
41 | def __init__(self, repoTreeView, parent=None):
42 | super(PropertiesPane, self).__init__(repoTreeView, parent=parent)
43 | self.table.addHeaderContextMenu(enabled = {'Name': False, 'Value': False}) # disable action
44 |
45 | self.table.setTextElideMode(QtCore.Qt.ElideMiddle)
46 |
47 | tableHeader = self.table.horizontalHeader()
48 | tableHeader.resizeSection(self.COL_PROP_NAME, 125)
49 | tableHeader.resizeSection(self.COL_VALUE, 150)
50 |
51 |
52 | def _drawContents(self, currentRti=None):
53 | """ Draws the attributes of the currentRTI
54 | """
55 | table = self.table
56 | table.setUpdatesEnabled(False)
57 | try:
58 | table.clearContents()
59 | verticalHeader = table.verticalHeader()
60 | verticalHeader.setSectionResizeMode(QtWidgets.QHeaderView.Fixed)
61 |
62 | if currentRti is None:
63 | return
64 |
65 | # Each column in the repo tree corresponds to a row in this detail pane.
66 | repoModel = self._repoTreeView.model()
67 | propNames = RepoTreeModel.HEADERS
68 | table.setRowCount(len(propNames))
69 |
70 | for row, propName in enumerate(propNames):
71 | nameItem = QtWidgets.QTableWidgetItem(propName)
72 | nameItem.setToolTip(propName)
73 | table.setItem(row, self.COL_PROP_NAME, nameItem)
74 | itemDataText = replaceEolChars(repoModel.itemData(currentRti, row))
75 | propItem = QtWidgets.QTableWidgetItem(itemDataText)
76 | propItem.setToolTip(repoModel.itemData(currentRti, row, role=Qt.ToolTipRole))
77 | table.setItem(row, self.COL_VALUE, propItem)
78 | table.resizeRowToContents(row)
79 |
80 | verticalHeader.setSectionResizeMode(QtWidgets.QHeaderView.ResizeToContents)
81 |
82 | finally:
83 | table.setUpdatesEnabled(True)
84 |
85 |
86 |
87 |
--------------------------------------------------------------------------------
/argos/repo/detailplugins/quicklook.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # This file is part of Argos.
4 | #
5 | # Argos is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # Argos is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with Argos. If not, see .
17 |
18 | """ RTI quicklook.
19 | """
20 | import logging
21 |
22 | import numpy as np
23 |
24 | from argos.qt import QtGui, QtWidgets
25 | from argos.repo.detailpanes import DetailBasePane
26 | from argos.widgets.constants import MONO_FONT, FONT_SIZE
27 |
28 | logger = logging.getLogger(__name__)
29 |
30 |
31 | class QuickLookPane(DetailBasePane):
32 | """ Shows a string representation of the RTI contents.
33 | """
34 | _label = "Quick Look"
35 |
36 | def __init__(self, repoTreeView, parent=None):
37 | super(QuickLookPane, self).__init__(repoTreeView, parent=parent)
38 |
39 | self._currentRti = None
40 |
41 | self.editor = QtWidgets.QPlainTextEdit()
42 | self.editor.setReadOnly(True)
43 | self.editor.setWordWrapMode(QtGui.QTextOption.NoWrap)
44 | self.editor.setFont(QtGui.QFont(MONO_FONT, FONT_SIZE))
45 |
46 | self.contentsLayout.addWidget(self.editor)
47 |
48 |
49 | def _drawContents(self, currentRti=None):
50 | """ Draws the attributes of the currentRTI
51 | """
52 | self._currentRti = currentRti
53 |
54 | if self._currentRti is None:
55 | self.editor.clear()
56 | else:
57 | editorCharWidth = self.editor.width() / self.editor.fontMetrics().averageCharWidth()
58 | oldLineWidth = np.get_printoptions()['linewidth']
59 | np.set_printoptions(linewidth=editorCharWidth)
60 | try:
61 | self.editor.setPlainText(self._currentRti.quickLook(editorCharWidth))
62 | finally:
63 | np.set_printoptions(linewidth=oldLineWidth)
64 |
65 |
66 | def resizeEvent(self, event):
67 | """ Called when the panel is resized. Will update the line length of the editor.
68 | """
69 | self.repoItemChanged(self._currentRti) # call repoItemChanged so it handles exceptions
70 | super(QuickLookPane, self).resizeEvent(event)
71 |
72 |
--------------------------------------------------------------------------------
/argos/repo/rtiplugins/__init__.py:
--------------------------------------------------------------------------------
1 | """ Repository Tree Items (RTI) plugins package.
2 |
3 | The default plugins are created (on registry reset) by RtiRegistry.getDefaultItems()
4 | """
5 |
6 |
--------------------------------------------------------------------------------
/argos/utils/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # This file is part of Argos.
4 | #
5 | # Argos is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # Argos is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with Argos. If not, see .
17 |
18 | """ Stand-alone utility functions.
19 |
20 | Since these functions are potentially useful outside this project,
21 | they conform to the pep8 naming conventions.
22 | """
23 |
--------------------------------------------------------------------------------
/argos/utils/config.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """ Various function related to config files.
4 | """
5 | import logging
6 | from typing import Any, Dict, List
7 |
8 | logger = logging.getLogger(__name__)
9 |
10 | ConfigDict = Dict[str, Any]
11 |
12 | def _findConfigParameter(cfg: ConfigDict, parts: List[str], orgPath: str) -> Any:
13 | """ Recursively finds a parameter in a (config) dict.
14 | """
15 | if len(parts) == 0:
16 | return cfg # Trailing slash
17 |
18 | head, tail = parts[0], parts[1:]
19 |
20 | if head == "": # A leading slash or double orgPath
21 | return _findConfigParameter(cfg, tail, orgPath)
22 |
23 | if head not in cfg:
24 | msg = "Path {!r} not found in dict. {!r} not in: {}".format(orgPath, head, list(cfg.keys()))
25 | raise KeyError(msg)
26 |
27 | if len(tail) == 0:
28 | return cfg[head]
29 | else:
30 | return _findConfigParameter(cfg[head], tail, orgPath)
31 |
32 |
33 | def findConfigParameter(cfg: ConfigDict, path: str) -> Any:
34 | """ Recursively finds a parameter in a config dict.
35 |
36 | Raises:
37 | KeyError: if the path cannot be found.
38 |
39 | Args:
40 | cfg: config dictionary. Can be a recursive dict (a dict of dicts, etc.)
41 | path: Slash-separated parameter path. E.g.: '/dict1/dict2/parameter'
42 | """
43 | if not path:
44 | # An empty path is most likely a programming error
45 | raise KeyError("Empty path given to findConfigParameter: {}".format(path))
46 | else:
47 | parts = path.split('/')
48 | return _findConfigParameter(cfg, parts, path)
49 |
50 |
51 | def getConfigParameter(cfg: ConfigDict, path: str, alt: Any = None) -> Any:
52 | """ Finds the findConfigParameter. Returns alternative value if not found.
53 |
54 | Will still log a warning if the parameter is not found.
55 |
56 | Args:
57 | cfg: config dictionary. Can be a recursive dict (a dict of dicts, etc.)
58 | path: Slash separated parameter path. E.g.: '/dict1/dict2/parameter'
59 | alt: Alternative value returned when the value is not found in the dictionary.
60 | """
61 | try:
62 | return findConfigParameter(cfg, path)
63 | except KeyError:
64 | logger.warning("Could not find path in config: {!r}".format(path))
65 | return alt
66 |
67 |
68 | def deleteParameter(cfg: ConfigDict, parentPath: str, parName: str) -> None:
69 | """ Deletes a parameter from the config dictionary.
70 |
71 | Args:
72 | cfg: config dictionary
73 | parentPath: the path of the parent dict that contains the paremeter
74 | parName: name of the element that will be removed
75 | """
76 | logger.debug("Deleting {!r} from parent dict: {!r}".format(parName, parentPath))
77 | try:
78 | parentCfg = findConfigParameter(cfg, parentPath)
79 | except KeyError:
80 | logger.warning("Could not find path in configur: {!r}".format(parentPath))
81 | return
82 |
83 | try:
84 | del parentCfg[parName]
85 | except KeyError:
86 | logger.warning("Could not find {!r} in parent dict: {!r}".format(parName, parentPath))
87 | return
88 | except Exception as ex:
89 | # The element might not be a dict
90 | logger.warning("Error while deleting {!r} from parent dict {!r}: {}"
91 | .format(parName, parentPath, ex))
92 | return
93 |
--------------------------------------------------------------------------------
/argos/utils/default_logging.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 1,
3 | "disable_existing_loggers": false,
4 |
5 | "formatters": {
6 | "fileFormatter": {
7 | "format": "%(asctime)s.%(msecs)03d : pid = %(process)5d : %(filename)25s:%(lineno)-4d : %(levelname)-8s: %(message)s",
8 | "datefmt": "%Y-%m-%d %H:%M:%S"
9 | },
10 | "screenFormatter": {
11 | "format": "%(asctime)s.%(msecs)03d : %(filename)25s:%(lineno)-4d : %(levelname)-8s: %(message)s",
12 | "datefmt": "%Y-%m-%d %H:%M:%S"
13 | }
14 | },
15 | "handlers": {
16 | "streamHandler": {
17 | "class": "logging.StreamHandler",
18 | "formatter": "screenFormatter",
19 | "stream": "ext://sys.stderr",
20 | "level": "WARNING"
21 | },
22 | "currentRunHandler": {
23 | "class": "logging.FileHandler",
24 | "formatter": "fileFormatter",
25 | "filename": "@logDir@/last_run.log",
26 | "encoding": "utf-8",
27 | "delay": "true",
28 | "mode": "w",
29 | "level": "DEBUG"
30 | },
31 | "rotatingFileHandler": {
32 | "class": "logging.handlers.TimedRotatingFileHandler",
33 | "formatter": "fileFormatter",
34 | "filename": "@logDir@/argos.log",
35 | "backupCount": 5,
36 | "when": "d",
37 | "interval": 1,
38 | "encoding": "utf-8",
39 | "delay": "true",
40 | "level": "DEBUG"
41 | }
42 | },
43 | "root": {
44 | "level": "WARNING",
45 | "handlers": [
46 | "streamHandler",
47 | "currentRunHandler",
48 | "rotatingFileHandler"
49 | ]
50 | },
51 | "loggers": {
52 | "argos": {
53 | "level": "DEBUG"
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/argos/utils/defs.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # This file is part of Argos.
4 | #
5 | # Argos is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # Argos is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with Argos. If not, see .
17 |
18 |
19 | """ Various definitions, errors and constants that can be used throughout the program.
20 |
21 | """
22 | import sys
23 |
24 | CONTIGUOUS = 'contiguous' # contiguous chunking
25 |
26 | # String formatting template for numbered dimension names
27 | DIM_TEMPLATE = "dim-{}"
28 | SUB_DIM_TEMPLATE = "subdim-{}"
29 |
30 | # Use different unicode character per platform as it looks better.
31 | if sys.platform == 'linux':
32 | RIGHT_ARROW = "\u2794"
33 | elif sys.platform == 'win32' or sys.platform == 'cygwin':
34 | RIGHT_ARROW = "\u2794"
35 | elif sys.platform == 'darwin':
36 | RIGHT_ARROW = "\u279E"
37 | else:
38 | RIGHT_ARROW = "\u2794"
39 |
40 |
41 | class InvalidInputError(Exception):
42 | """ Exception raised when the input is invalid after editing.z
43 | """
44 | pass
45 |
--------------------------------------------------------------------------------
/argos/utils/misc.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # This file is part of Argos.
4 | #
5 | # Argos is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # Argos is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with Argos. If not, see .
17 |
18 | """ Miscellaneous routines.
19 | """
20 | import logging
21 | import pprint
22 | import re
23 |
24 | from html import escape
25 | from typing import TypeVar, List, Any, Dict
26 |
27 | from argos.utils.cls import isAString
28 |
29 | logger = logging.getLogger(__name__)
30 |
31 | class NotSpecified():
32 | """ Class for the NOT_SPECIFIED constant.
33 | Is used so that a parameter can have a default value other than None.
34 |
35 | Evaluates to False when converted to boolean.
36 | """
37 | def __bool__(self) -> bool:
38 | """ Always returns False. Called when converting to bool in Python 3.
39 | """
40 | return False
41 |
42 | NOT_SPECIFIED = NotSpecified()
43 |
44 |
45 | def isQuoted(s: str) -> bool:
46 | """ Returns True if the string begins and ends with quotes (single or double).
47 | """
48 | return (s.startswith("'") and s.endswith("'")) or (s.startswith('"') and s.endswith('"'))
49 |
50 |
51 | def stringToIdentifier(s: str, white_space_becomes: str = '_') -> str:
52 | """ Takes a string and makes it suitable for use as an identifier.
53 |
54 | Translates to lower case.
55 | Replaces white space by the white_space_becomes character (default=underscore).
56 | Removes and punctuation.
57 | """
58 | s = s.lower()
59 | s = re.sub(r"\s+", white_space_becomes, s) # replace whitespace with underscores
60 | s = re.sub(r"-", "_", s) # replace hyphens with underscores
61 | s = re.sub(r"[^A-Za-z0-9_]", "", s) # remove everything that's not a character, a digit or a _
62 | return s
63 |
64 |
65 | T = TypeVar('T', Dict[Any, Any], List[Any], str)
66 | def replaceStringsInDict(obj: T, old: str, new: str) -> T:
67 | """ Recursively searches for a string in a dict and replaces a string by another.
68 | """
69 | if isinstance(obj, dict):
70 | return {key: replaceStringsInDict(value, old, new) for key, value in obj.items()}
71 | elif isinstance(obj, list):
72 | return [replaceStringsInDict(value, old, new) for value in obj]
73 | elif isAString(obj):
74 | return obj.replace(old, new)
75 | else:
76 | return obj
77 |
78 |
79 | def removeProcessSerialNumber(argList: List[str]) -> List[str]:
80 | """ Creates a copy of a list (typically sys.argv) where the strings that
81 | start with ``-psn_0_`` are removed.
82 |
83 | These are the process serial number used by the OS-X open command
84 | to bring applications to the front. They clash with argparse.
85 | See: http://hintsforums.macworld.com/showthread.php?t=11978
86 | """
87 | return [arg for arg in argList if not arg.startswith("-psn_0_")]
88 |
89 |
90 | def replaceEolChars(attr: str) -> str:
91 | """ Replace end-of-line characters with unicode glyphs so that all table rows fit on one line.
92 | """
93 | return (attr.replace('\r\n', chr(0x21B5))
94 | .replace('\n', chr(0x21B5))
95 | .replace('\r', chr(0x21B5)))
96 |
97 |
98 | def pformat(obj: Any, width: int) -> str:
99 | """ Pretty print format with Argos default parameter values.
100 | """
101 | return pprint.pformat(obj, width=width, depth=2, sort_dicts=False)
102 |
103 |
104 | def wrapHtmlColor(html: str, color: str) -> str:
105 | """ Wraps HTML in a span with a certain color
106 | """
107 | return '{}'\
108 | .format(color, html)
109 |
110 |
--------------------------------------------------------------------------------
/argos/widgets/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # This file is part of Argos.
4 | #
5 | # Argos is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # Argos is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with Argos. If not, see .
17 |
18 | """ Qt widgets specific to this project.
19 |
20 | Note that some widgets are locate elsewhere if it makes sense to place them near related
21 | classes. For example the ConfigTreeView widgets can be found in argos.config.configtreeview.
22 | """
23 |
--------------------------------------------------------------------------------
/argos/widgets/aboutdialog.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # This file is part of Argos.
3 | #
4 | # Argos is free software: you can redistribute it and/or modify
5 | # it under the terms of the GNU General Public License as published by
6 | # the Free Software Foundation, either version 3 of the License, or
7 | # (at your option) any later version.
8 | #
9 | # Argos is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | # GNU General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public License
15 | # along with Argos. If not, see .
16 |
17 | import logging
18 |
19 | from argos.info import PROJECT_NAME, VERSION, DEBUGGING
20 | from argos.qt import QtCore, QtGui, QtWidgets
21 | from argos.utils.cls import isAString
22 | from argos.utils import moduleinfo as mi
23 | from argos.widgets.constants import MONO_FONT, FONT_SIZE
24 |
25 | logger = logging.getLogger(__name__)
26 |
27 |
28 | # The main window inherits from a Qt class, therefore it has many
29 | # ancestors public methods and attributes.
30 | # pylint: disable=too-many-ancestors, too-many-instance-attributes, too-many-public-methods, attribute-defined-outside-init
31 |
32 |
33 | class AboutDialog(QtWidgets.QDialog):
34 | """ Dialog window that shows dependency information.
35 | """
36 | def __init__(self, parent=None):
37 | """ Constructor
38 | """
39 | super(AboutDialog, self).__init__(parent=parent)
40 | self.setModal(True)
41 |
42 | mainLayout = QtWidgets.QVBoxLayout()
43 | self.setLayout(mainLayout)
44 |
45 | progVersionLabel = QtWidgets.QLabel()
46 | progVersionLabel.setText("{} {}{}".format(PROJECT_NAME, VERSION,
47 | ' (debugging-mode)' if DEBUGGING else ''))
48 | progVersionLabel.setTextInteractionFlags(QtCore.Qt.TextSelectableByMouse)
49 | mainLayout.addWidget(progVersionLabel)
50 |
51 | font = QtGui.QFont()
52 | font.setFamily(MONO_FONT)
53 | font.setFixedPitch(True)
54 | font.setPointSize(FONT_SIZE)
55 |
56 | self.editor = QtWidgets.QPlainTextEdit()
57 | self.editor.setReadOnly(True)
58 | self.editor.setFont(font)
59 | self.editor.setWordWrapMode(QtGui.QTextOption.NoWrap)
60 | self.editor.clear()
61 | self.editor.setPlainText("Retrieving package info...")
62 | mainLayout.addWidget(self.editor)
63 |
64 | self.progressLabel = QtWidgets.QLabel()
65 | mainLayout.addWidget(self.progressLabel)
66 |
67 | buttonBox = QtWidgets.QDialogButtonBox(QtWidgets.QDialogButtonBox.Ok)
68 | buttonBox.accepted.connect(self.accept)
69 | mainLayout.addWidget(buttonBox)
70 |
71 | self.resize(QtCore.QSize(800, 400))
72 |
73 |
74 | def _addModuleInfo(self, moduleInfo):
75 | """ Adds a line with module info to the editor
76 | :param moduleInfo: can either be a string or a module info class.
77 | In the first case, an object is instantiated as ImportedModuleInfo(moduleInfo).
78 | """
79 | if isAString(moduleInfo):
80 | moduleInfo = mi.ImportedModuleInfo(moduleInfo)
81 |
82 | line = "{:15s}: {}".format(moduleInfo.name, moduleInfo.verboseVersion)
83 | logger.info(" {}".format(line))
84 | self.editor.appendPlainText(line)
85 | QtWidgets.QApplication.instance().processEvents()
86 |
87 |
88 | def addDependencyInfo(self):
89 | """ Adds version info about the installed dependencies
90 | """
91 | logger.debug("Adding dependency info to the AboutDialog")
92 | self.progressLabel.setText("Retrieving package info...")
93 | self.editor.clear()
94 |
95 | logger.info("Used packages:")
96 | self._addModuleInfo(mi.PythonModuleInfo())
97 | self._addModuleInfo(mi.QtModuleInfo())
98 |
99 | modules = ['numpy', 'scipy', 'pandas', 'pyqtgraph', 'exdir', 'pgcolorbar', 'cmlib']
100 | for module in modules:
101 | self._addModuleInfo(module)
102 |
103 | self._addModuleInfo(mi.H5pyModuleInfo())
104 | self._addModuleInfo(mi.NetCDF4ModuleInfo())
105 | self._addModuleInfo(mi.PillowInfo())
106 |
107 | self._addModuleInfo(mi.ArgosModuleInfo())
108 |
109 | self.progressLabel.setText("")
110 | logger.debug("Finished adding dependency info to the AboutDialog")
111 |
112 |
--------------------------------------------------------------------------------
/argos/widgets/argostableview.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # This file is part of Argos.
3 | #
4 | # Argos is free software: you can redistribute it and/or modify
5 | # it under the terms of the GNU General Public License as published by
6 | # the Free Software Foundation, either version 3 of the License, or
7 | # (at your option) any later version.
8 | #
9 | # Argos is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | # GNU General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public License
15 | # along with Argos. If not, see .
16 |
17 | """ Common functionality, look and feel for all table views in Argos.
18 |
19 | Control-C copies all data.
20 | """
21 | from __future__ import print_function
22 |
23 | import logging
24 | from argos.qt import QtWidgets, Qt
25 |
26 |
27 | logger = logging.getLogger(__name__)
28 |
29 | # Qt classes have many ancestors
30 | #pylint: disable=too-many-ancestors
31 |
32 | class ArgosTableView(QtWidgets.QTableView):
33 | """ QTableView that defines common functionality for argos
34 | """
35 | def __init__(self, *args, **kwargs):
36 | """ Constructor
37 | """
38 | super(ArgosTableView, self).__init__(*args, **kwargs)
39 |
40 |
41 | def keyPressEvent(self, event):
42 | """ Overrides key press events to capture Ctrl-C
43 | """
44 | if event.key() == Qt.Key_C and event.modifiers() == Qt.ControlModifier:
45 | self.copySelectionToClipboard()
46 | else:
47 | super(ArgosTableView, self).keyPressEvent(event)
48 | return
49 |
50 |
51 | def copySelectionToClipboard(self):
52 | """ Copies selected cells to clipboard.
53 |
54 | Only works for ContiguousSelection
55 | """
56 | if not self.model():
57 | logger.warning("Table contains no data. Copy to clipboard aborted.")
58 | return
59 |
60 | if self.selectionMode() not in [QtWidgets.QTableView.SingleSelection,
61 | QtWidgets.QTableView.ContiguousSelection]:
62 | logger.warning("Copy to clipboard does not work for current selection mode: {}"
63 | .format(self.selectionMode()))
64 | return
65 |
66 | selectedIndices = self.selectionModel().selectedIndexes()
67 | logger.info("Copying {} selected cells to clipboard.".format(len(selectedIndices)))
68 |
69 | # selectedIndexes() can return unsorted list so we sort it here to be sure.
70 | selectedIndices.sort(key=lambda idx: (idx.row(), idx.column()))
71 |
72 | # Unflatten indices into a list of list of indicides
73 | allIndices = []
74 | allLines = []
75 | lineIndices = [] # indices of current line
76 | prevRow = None
77 | for selIdx in selectedIndices:
78 | if prevRow != selIdx.row() and prevRow is not None: # new line
79 | allIndices.append(lineIndices)
80 | lineIndices = []
81 | lineIndices.append(selIdx)
82 | prevRow = selIdx.row()
83 | allIndices.append(lineIndices)
84 | del lineIndices
85 |
86 | # Convert to tab-separated lines so it can be pasted in Excel.
87 | lines = []
88 | for lineIndices in allIndices:
89 | line = '\t'.join([str(idx.data()) for idx in lineIndices])
90 | lines.append(line)
91 | txt = '\n'.join(lines)
92 | #print(txt)
93 | QtWidgets.QApplication.clipboard().setText(txt)
94 |
95 |
96 |
--------------------------------------------------------------------------------
/argos/widgets/constants.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # This file is part of Argos.
4 | #
5 | # Argos is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # Argos is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with Argos. If not, see .
17 |
18 | """ Constants related to the layout and other widget properties.
19 | """
20 | import sys
21 | from argos.qt import QtCore, QtGui, QtWidgets
22 |
23 | # Use large numpy line length in the Quicklook panel
24 | NUMPY_LINE_WIDTH = sys.maxsize
25 |
26 | #TREE_ROW_HEIGHT = 20 # pixels
27 | TREE_CELL_SIZE_HINT = QtCore.QSize(100, 20)
28 | TREE_ICON_SIZE = QtCore.QSize(16, 16)
29 |
30 | #COLLECTOR_TREE_CELL_SIZE_HINT = QtCore.QSize(100, 24) not used (yet?)
31 | COLLECTOR_TREE_ICON_SIZE = QtCore.QSize(20, 20)
32 |
33 | # Initial dock widths of the main window
34 | LEFT_DOCK_WIDTH = 440 # needs room for scroll bars
35 | RIGHT_DOCK_WIDTH = 320
36 | TOP_DOCK_HEIGHT = 75
37 |
38 | COL_NODE_NAME_WIDTH = 180
39 | COL_KIND_WIDTH = 50
40 | COL_ELEM_TYPE_WIDTH = 80
41 | COL_SUMMARY_WIDTH = 110 # will be stretched
42 |
43 |
44 | # Spacing and margin in central widgets in pixels
45 | CENTRAL_SPACING = 0
46 | CENTRAL_MARGIN = 0
47 |
48 | # Spacing and margin in dock widgets in pixels. Is now set in style sheet margin.
49 |
50 | DOCK_SPACING = 0
51 | DOCK_MARGIN = 0
52 |
53 | if sys.platform == 'linux':
54 | MONO_FONT = 'Monospace'
55 | FONT_SIZE = 10
56 | elif sys.platform == 'win32' or sys.platform == 'cygwin':
57 | MONO_FONT = 'Courier'
58 | FONT_SIZE = 10
59 | elif sys.platform == 'darwin':
60 | MONO_FONT = 'Courier'
61 | FONT_SIZE = 13
62 | else:
63 | MONO_FONT = 'Courier'
64 | FONT_SIZE = 13
65 |
66 | COLOR_ERROR = '#FF0000' # red
67 |
68 | QCOLOR_REGULAR = QtGui.QColor('black')
69 | QCOLOR_NOT_IMPORTED = QtGui.QColor('grey')
70 | QCOLOR_ERROR = QtGui.QColor(COLOR_ERROR)
71 |
72 |
--------------------------------------------------------------------------------
/argos/widgets/display.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # This file is part of Argos.
4 | #
5 | # Argos is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # Argos is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with Argos. If not, see .
17 |
18 | """ Widgets for displaying messages
19 | """
20 | from __future__ import print_function
21 |
22 | import logging
23 |
24 | from argos.qt import Qt, QtGui, QtWidgets
25 | from argos.widgets.constants import MONO_FONT, FONT_SIZE
26 | from argos.widgets.misc import BasePanel
27 |
28 | logger = logging.getLogger(__name__)
29 |
30 |
31 | # The main window inherits from a Qt class, therefore it has many
32 | # ancestors public methods and attributes.
33 | # pylint: disable=too-many-ancestors, too-many-instance-attributes, too-many-public-methods, attribute-defined-outside-init
34 |
35 |
36 | class MessageDisplay(BasePanel):
37 | """ Widget that shows a label and title.
38 | Consists of a title label and a larger message label for details.
39 | """
40 | def __init__(self, parent=None, msg="", title="Error"):
41 | super(MessageDisplay, self).__init__(parent=parent)
42 |
43 | self.layout = QtWidgets.QVBoxLayout()
44 | self.setLayout(self.layout)
45 |
46 | self.titleLabel = QtWidgets.QLabel(title)
47 | self.titleLabel.setTextFormat(Qt.PlainText)
48 | self.titleLabel.setTextInteractionFlags(Qt.TextSelectableByMouse)
49 | self.titleLabel.setAlignment(Qt.AlignHCenter)
50 | self.layout.addWidget(self.titleLabel, stretch=0)
51 |
52 | font = QtGui.QFont()
53 | font.setFamily(MONO_FONT)
54 | font.setFixedPitch(True)
55 | font.setPointSize(FONT_SIZE)
56 |
57 | self.messageLabel = QtWidgets.QLabel(msg)
58 | self.messageLabel.setFont(font)
59 | self.messageLabel.setTextFormat(Qt.PlainText)
60 | self.messageLabel.setTextInteractionFlags(Qt.TextSelectableByMouse)
61 | self.messageLabel.setWordWrap(True)
62 | self.messageLabel.setAlignment(Qt.AlignTop)
63 | self.messageLabel.setFrameStyle(QtWidgets.QFrame.Panel | QtWidgets.QFrame.Plain)
64 | self.layout.addWidget(self.messageLabel, stretch=1)
65 |
66 |
67 | def setError(self, msg=None, title=None):
68 | """ Shows and error message
69 | """
70 | if msg is not None:
71 | self.messageLabel.setText(msg)
72 |
73 | if title is not None:
74 | self.titleLabel.setText(title)
75 |
76 |
77 |
--------------------------------------------------------------------------------
/argos/widgets/misc.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # This file is part of Argos.
4 | #
5 | # Argos is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # Argos is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with Argos. If not, see .
17 |
18 | """ Miscellaneous functions and classes using Qt
19 | """
20 | from __future__ import print_function
21 |
22 | import logging
23 | import os.path
24 |
25 | from argos.qt import QtWidgets
26 |
27 | logger = logging.getLogger(__name__)
28 |
29 |
30 | def setWidgetSizePolicy(widget, hor=None, ver=None):
31 | """ Sets horizontal and/or vertical size policy on a widget
32 | """
33 | sizePolicy = widget.sizePolicy()
34 | logger.debug("widget {} size policy Befor: {} {}"
35 | .format(widget, sizePolicy.horizontalPolicy(), sizePolicy.verticalPolicy()))
36 |
37 | if hor is not None:
38 | sizePolicy.setHorizontalPolicy(hor)
39 |
40 | if ver is not None:
41 | sizePolicy.setVerticalPolicy(ver)
42 |
43 | widget.setSizePolicy(sizePolicy)
44 |
45 | sizePolicy = widget.sizePolicy()
46 | logger.debug("widget {} size policy AFTER: {} {}"
47 | .format(widget, sizePolicy.horizontalPolicy(), sizePolicy.verticalPolicy()))
48 |
49 |
50 | def processEvents():
51 | """ Processes all pending events for the calling thread until there are no more events to
52 | process.
53 | """
54 | QtWidgets.QApplication.instance().processEvents()
55 |
56 |
57 | def setApplicationQtStyle(styleName):
58 | """ Sets the Qt style (e.g. to 'fusion')
59 | """
60 | qApp = QtWidgets.QApplication.instance()
61 | logger.debug("Setting Qt style to: {}".format(styleName))
62 | qApp.setStyle(QtWidgets.QStyleFactory.create(styleName))
63 | if qApp.style().objectName().lower() != styleName.lower():
64 | logger.warning(
65 | "Setting style failed: actual style {!r} is not the specified style {!r}"
66 | .format(qApp.style().objectName(), styleName))
67 |
68 |
69 | def setApplicationStyleSheet(fileName):
70 | """ Reads the style sheet from file and set it as application style sheet.
71 | """
72 | fileName = os.path.abspath(fileName)
73 | logger.debug("Reading qss from: {}".format(fileName))
74 | try:
75 | with open(fileName) as input:
76 | qss = input.read()
77 | except Exception as ex:
78 | logger.warning("Unable to read style sheet from '{}'. Reason: {}".format(fileName, ex))
79 | return
80 |
81 | QtWidgets.QApplication.instance().setStyleSheet(qss)
82 |
83 |
84 |
85 | class BasePanel(QtWidgets.QFrame):
86 | """ Base panel from which others are derived.
87 |
88 | Define shape
89 | """
90 | def __init__(self, **kwargs):
91 | super(BasePanel, self).__init__(**kwargs)
92 | #self.setFrameShape(QtWidgets.QFrame.StyledPanel)
93 | #self.setFrameShadow(QtWidgets.QFrame.Sunken)
94 |
95 |
96 |
--------------------------------------------------------------------------------
/development/3rd_party_data.txt:
--------------------------------------------------------------------------------
1 | URLs to 3rd party data that could be used to test and demonstrate Argos capabilities
2 | ====================================================================================
3 |
4 |
5 | http://www.kdnuggets.com/datasets/index.html
6 | http://pds.jpl.nasa.gov/tools/data-search/search.jsp?q=temperature&start=50
7 |
8 | https://datamarket.com/data/set/22r4/annual-diameter-of-skirt-at-hem-1866-to-1911#!ds=22r4&display=line
9 | https://datamarket.com/data/set/22xi/tree-wulan-arstan-jusp-location-pr-china-juniper-3600m-3700-10000-author-local-met-bur-rz-zu-years-1163-1986#!ds=22xi&display=line&e=-4qsa&s=-5a8a
10 | https://datamarket.com/data/set/1cfl/total-population#!ds=1cfl!r3d=55.66.l.6d&display=line
11 |
12 | http://tylervigen.com/sources
13 |
14 | http://www.gdngeoservices.nl/wms/dinomap/M11M0802
15 |
16 | https://scihub.esa.int/
17 |
18 | http://www.virtualobservatory.org/use/
19 |
20 | https://arrow.apache.org/ A cross-language development platform for in-memory data
21 |
22 | # NOAA
23 | http://www.ngdc.noaa.gov/mgg/topo/gltiles.html
24 |
25 | # NASA data
26 | http://mirador.gsfc.nasa.gov/cgi-bin/mirador/presentNavigation.pl?tree=project&CGISESSID=09380e8a7f3c544a347205a800ad7014
27 | http://data.giss.nasa.gov/gistemp/
28 | http://archive.stsci.edu/genlinks_search.php?target=&resolver=SIMBAD&Submit=Search
29 |
30 | # OMI L3
31 | http://mirador.gsfc.nasa.gov/cgi-bin/mirador/granlist.pl?page=1&location=%28-90,-180%29,%2890,180%29&dataSet=OMDOAO3e&version=003&allversion=003&startTime=2010-06-02T00:00:01Z&endTime=2010-06-02T23:59:59Z&keyword=OMDOAO3e&longname=OMI/Aura%20Ozone%20%28O3%29%20DOAS%20Total%20Column%20Daily%20L3%20Global%200.25deg%20Lat/Lon%20Grid&CGISESSID=3a77ae56c199ee231e300480e6fc5edc&prodpg=http://mirador.gsfc.nasa.gov/collections/OMDOAO3e__003.shtml
32 | http://www.temis.nl/airpollution/no2.html
33 | http://www.temis.nl/airpollution/no2col/data/omi/overpass/De_Bilt_domino.dat
34 | http://www.temis.nl/airpollution/no2col/no2regioomi_v2.php?Region=9&Year=2015&Month=10&Day=07
35 |
36 | # US Census
37 | http://www.census.gov/population/international/data/countryrank/rank.php
38 |
39 | # voxel data
40 | http://www.gris.uni-tuebingen.de/edu/areas/scivis/volren/datasets/datasets.html
41 | http://www.gris.uni-tuebingen.de/edu/areas/scivis/volren/datasets/new.html
42 | http://yt-project.org/doc/index.html
43 | https://datascience.dsscale.org/data/
44 |
45 | # USGC/NASA
46 | http://nsidc.org/data/modis/index.html
47 | https://earthdata.nasa.gov/
48 | https://lpdaac.usgs.gov/dataset_discovery/aster/aster_products_table
49 | https://lpdaac.usgs.gov/data_access
50 |
51 | # HDF-EOS
52 | http://www.hdfeos.org/zoo/index_openGESDISC_Examples.php#MERRA
53 |
54 | # NASA GES
55 | https://disc.gsfc.nasa.gov/datasets?project=OCO
56 |
57 |
58 | # NASA NARR:
59 | ftp://ftp.cdc.noaa.gov/Datasets/NARR/Dailies/monolevel/
60 |
61 | # NASDAQ
62 | https://www.kaggle.com/jacksoncrow/stock-market-dataset
63 |
--------------------------------------------------------------------------------
/development/3rd_party_packages.txt:
--------------------------------------------------------------------------------
1 | URLs to 3rd party packages that might be interesting for Argos
2 | ===============================================================
3 |
4 | # Misc
5 | http://pandas-datareader.readthedocs.org/en/latest/remote_data.html#google-finance
6 | http://www.reddit.com/r/Python/comments/2vn1yz/what_is_the_best_way_to_create_deb_package_from/
7 |
8 | fits format http://fits.gsfc.nasa.gov/ (astropy)
9 | geotiff format (attributes). (https://aws.amazon.com/public-data-sets/landsat/)
10 | grib format: https://github.com/jswhit/pygrib
11 | bufr format: https://en.wikipedia.org/wiki/BUFR
12 | http://scitools.org.uk/iris/ ((CF-)netCDF, GRIB, and PP files.
13 | Cartopy: http://scitools.org.uk/cartopy/docs/latest/ (shape files)
14 | Holoviews: https://holoviews.org/
15 |
16 |
17 | # Tagged tiff files
18 | https://pypi.org/project/tifffile/
19 |
20 | # Selectors plugins
21 | http://blaze.pydata.org/en/latest/ (# seems dead since 2018)
22 | BColz (# seems dead since 2017)
23 |
24 | # Plot and Mapping libs:
25 | https://wiki.python.org/moin/NumericAndScientific/Plotting
26 | http://pygal.org/chart_types/#idworldmap-charts
27 | http://docs.enthought.com/chaco/quickstart.html
28 | http://scikit-image.org/docs/dev/auto_examples/plot_canny.html
29 | http://vispy.org/
30 | https://www.getdatajoy.com/examples/python-plots/vector-fields
31 |
32 | # color palette
33 | http://tools.medialab.sciences-po.fr/iwanthue/tutorial.php
34 | http://vis4.net/blog/posts/avoid-equidistant-hsv-colors/
35 | http://soliton.vm.bytemark.co.uk/pub/cpt-city/
36 | https://vis4.net/blog/posts/mastering-multi-hued-color-scales/
37 | https://gka.github.io/palettes/#colors=lightyellow,orange,deeppink,darkred|steps=10|bez=1|coL=1 # create own scales, very nice
38 | http://stanford.edu/~mwaskom/software/seaborn/tutorial/color_palettes.html
39 |
40 | # Grib
41 | http://mirador.gsfc.nasa.gov/cgi-bin/mirador/granlist.pl?page=1&location=(-90,-180),(90,180)&dataSet=NLDAS_FOR0125_H&version=001&allversion=001&startTime=2005-08-03T00:00:01Z&endTime=2005-08-03T23:59:59Z&keyword=NLDAS_FOR0125_H&prodpg=http://disc.gsfc.nasa.gov/datacollection/NLDAS_FOR0125_H_V001.html&longname=NLDAS%20Forcing%20Data%20L4%20Hourly%200.125%20x%200.125%20degree%20V001&CGISESSID=18bd3c7fb47ceeb1e5c1e9f8139efd0c
42 | http://mirador.gsfc.nasa.gov/cgi-bin/mirador/presentNavigation.pl?tree=scienceArea&CGISESSID=18bd3c7fb47ceeb1e5c1e9f8139efd0c&CURRENT_CONTEXT=scienceArea
43 |
44 | # Grams SPC
45 | https://en.wikipedia.org/wiki/SPC_file_format
46 |
--------------------------------------------------------------------------------
/development/AUTHORS.rst:
--------------------------------------------------------------------------------
1 | =======
2 | Credits
3 | =======
4 |
5 | Development Lead
6 | ----------------
7 |
8 | * Pepijn Kenter
9 |
10 |
11 | Contributors
12 | ------------
13 |
14 | * Christoph Buchner (https://github.com/bilderbuchi)
15 | * Thomas Grainger (https://github.com/graingert)
16 | * Lui Habl (https://github.com/luihabl): Exdir Plugin
17 | * Sidney Cadot (https://github.com/sidneycadot/json_with_comments): JSON with comments module.
18 |
--------------------------------------------------------------------------------
/development/CONTRIBUTING.rst:
--------------------------------------------------------------------------------
1 | ============
2 | Contributing
3 | ============
4 |
5 | Contributions are welcome, and they are greatly appreciated! Every
6 | little bit helps, and credit will always be given.
7 |
8 | You can contribute in many ways:
9 |
10 | Types of Contributions
11 | ----------------------
12 |
13 | Report Bugs
14 | ~~~~~~~~~~~
15 |
16 | Report bugs at https://github.com/titusjan/argos/issues.
17 |
18 | If you are reporting a bug, please include:
19 |
20 | * Your operating system name and version.
21 | * Any details about your local setup that might be helpful in troubleshooting.
22 | * Detailed steps to reproduce the bug.
23 |
24 | Fix Bugs
25 | ~~~~~~~~
26 |
27 | Look through the GitHub issues for bugs. Anything tagged with "bug"
28 | is open to whoever wants to implement it.
29 |
30 | Implement Features
31 | ~~~~~~~~~~~~~~~~~~
32 |
33 | Look through the GitHub issues for features. Anything tagged with "feature"
34 | is open to whoever wants to implement it.
35 |
36 | Write Documentation
37 | ~~~~~~~~~~~~~~~~~~~
38 |
39 | Argos could always use more documentation, whether as part of the
40 | official Argos docs, in docstrings, or even on the web in blog posts,
41 | articles, and such.
42 |
43 | Submit Feedback
44 | ~~~~~~~~~~~~~~~
45 |
46 | The best way to send feedback is to file an issue at https://github.com/titusjan/argos/issues.
47 |
48 | If you are proposing a feature:
49 |
50 | * Explain in detail how it would work.
51 | * Keep the scope as narrow as possible, to make it easier to implement.
52 | * Remember that this is a volunteer-driven project and that there is not guarantee that your
53 | feature will be implemented.
54 |
55 |
56 | Get Started!
57 | ------------
58 |
59 | Ready to contribute? Here's how to set up `argos` for local development.
60 |
61 | 0. Keep in mind that there is no guarantee that your pull request will be accepted. When in doubt,
62 | send me an email or open an issue to discuss it before spending a lot of time on it.
63 | 1. Fork the `argos` repo on GitHub.
64 | 2. Clone your fork locally::
65 |
66 | $ git clone git@github.com:your_name_here/argos.git
67 |
68 | 3. Install your local copy into a virtualenv. Assuming you have virtualenvwrapper installed, this
69 | is how you set up your fork for local development::
70 |
71 | $ mkvirtualenv argos
72 | $ cd argos/
73 | $ python setup.py develop
74 |
75 | 4. Create a branch for local development. Please base it on de devel branch ::
76 |
77 | $ git checkout -b name-of-your-bugfix-or-feature devel
78 |
79 | Now you can make your changes locally.
80 |
81 | 5. Commit your changes and push your branch to GitHub::
82 |
83 | $ git add .
84 | $ git commit -m "Your detailed description of your changes."
85 | $ git push origin name-of-your-bugfix-or-feature
86 |
87 | 6. Submit a pull request through the GitHub website.
88 |
--------------------------------------------------------------------------------
/development/activate.sh:
--------------------------------------------------------------------------------
1 | # Adds the parent argos directory to beginning of the PYTHONPATH so that the tests can be executed
2 | # and the current directory to the PATH so that the argos.py script can be found from everywhere.
3 |
4 |
5 | CURRENT_DIR="$(dirname "${BASH_SOURCE[0]}")"
6 |
7 | SRC_DIR=$(realpath $CURRENT_DIR/..)
8 |
9 | echo ${SRC_DIR}
10 |
11 | export PATH="${CURRENT_DIR}:$PATH"
12 | export PYTHONPATH="${SRC_DIR}:$PYTHONPATH"
13 |
--------------------------------------------------------------------------------
/development/argos.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | """ Convenience script that starts argos in the development environment.
3 |
4 | Note that this is NOT the script that will be installed by setup.py. The setup.py install
5 | command will create and install a dedicated argos launcher script (as specified in the
6 | entry_points directive).
7 |
8 | An alternative is to use:
9 | cd argos # go to the same directory where setup.py is located.
10 | pip install -e .
11 |
12 | This installs the development version so you can call the the development version from the
13 | command line while working on it.
14 | """
15 | import sys, os.path
16 |
17 | # Add the parent directory to the system path so that the package can be imported.
18 | scriptDir = os.path.dirname(os.path.realpath(__file__))
19 | parentDir = os.path.realpath(os.path.join(scriptDir, '..'))
20 |
21 | sys.path.insert(0, parentDir)
22 | from argos.main import main
23 | main()
24 |
--------------------------------------------------------------------------------
/development/environment.yaml:
--------------------------------------------------------------------------------
1 | # Use this file to create an Anaconda environment with:
2 | # %> conda env create -f environment.yml [-n better_name]
3 |
4 | # It does NOT install argos itself, so you can use this as a development environment.
5 | # At the moment Python 3.10 doesn't work with PyQt 5.12, which is the most recent PyQt version
6 | # on Anaconda.
7 |
8 | name: argos
9 | dependencies:
10 | - python <3.10
11 | - numpy
12 | - pyqt
13 | - h5py
14 | - netcdf4
15 | - pillow
16 | - scipy
17 | - pandas
18 | - pyqtgraph >= 0.11.0
19 | - conda-forge::cmlib >= 1.1.2
20 | - conda-forge::pgcolorbar >= 1.1.1
21 |
22 |
23 | # The following dependencies are for development only and therefore have no version number
24 | - mypy
25 | - pylint
26 | - sphinx
27 | - sphinx_rtd_theme
28 |
--------------------------------------------------------------------------------
/development/environment_min.yaml:
--------------------------------------------------------------------------------
1 | # Use this file to create an Anaconda environment with:
2 | # %> conda env create -f environment.yml [-n better_name]
3 |
4 | # It does NOT install argos itself, so you can use this as a development environment.
5 |
6 | # It installs the minimal supported version of Python, Qt, Numpy and PyQtGraph.
7 | # See
8 | # https://numpy.org/neps/nep-0029-deprecation_policy.html#drop-schedule
9 | # https://github.com/pyqtgraph/pyqtgraph#qt-bindings-test-matrix
10 |
11 | name: argosmin
12 | dependencies:
13 | - python = 3.8
14 | - numpy = 1.20
15 | - pyqt = 5.15
16 | - h5py
17 | - netcdf4
18 | - pillow
19 | - scipy
20 | - pandas
21 | - pyqtgraph = 0.11.0
22 | - conda-forge::cmlib >= 1.1.2
23 | - conda-forge::pgcolorbar >= 1.1.1
24 |
25 |
26 | # The following dependencies are for development only and therefore have no version number
27 | - mypy
28 | - pylint
29 | - sphinx
30 | - sphinx_rtd_theme
31 |
--------------------------------------------------------------------------------
/development/int_coercion.py:
--------------------------------------------------------------------------------
1 | """ Program to test if PyQt 5.12 can work with Python 3.10
2 |
3 | Python 3.10, PyQt 5.12 -> flags:
4 | Python 3.10, PyQt 5.15 -> flags:
5 |
6 | """
7 | import sys
8 | from PyQt5.QtCore import Qt
9 | from PyQt5.QtCore import QT_VERSION_STR
10 | from PyQt5.Qt import PYQT_VERSION_STR
11 |
12 | print("Python version: {}".format(sys.version))
13 | print("Qt version: {}".format(QT_VERSION_STR))
14 | print("PyQt version: {}".format(PYQT_VERSION_STR))
15 | print()
16 |
17 | a = Qt.NoItemFlags | Qt.ItemIsEnabled
18 | b = Qt.ItemIsTristate | Qt.ItemIsUserCheckable
19 |
20 | print(a|b)
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/development/mypy.ini:
--------------------------------------------------------------------------------
1 | # Options for checking the code with mypy
2 | # Call with: mypy --config ~/prog/py/argos/development/mypy.ini
3 | #
4 | # Use the minimal supported Python version for checking with mypy! See environment_min.yaml
5 |
6 |
7 | [mypy]
8 | strict = True
9 | show_error_codes = True
10 |
11 | no_implicit_optional = False
12 |
13 | # Disable no-untyped-def warning so that we don't have to use '-> None' everywhere.
14 | #disable_error_code = no-untyped-def
15 |
--------------------------------------------------------------------------------
/development/paths.txt:
--------------------------------------------------------------------------------
1 | # Temporary files with interesting test data.
2 | ***
3 | /argos/gstp-rad/2014-04-29_FEL-350/calib_1050nm/2014-04-29T11_53_38Nothing m2.h5/scan_procedure/procedure
4 | /argos/mytests/string.hdf5/
5 |
6 | /argos/trop/ql_test_020_minutes-020-021/qlbd3eps.lx.nc/BAND3/ICID_04003_GROUP_00000/OBSERVATIONS
7 |
8 | /argos/fel_nist/nist_fel-xxx_irrad.txt
9 | /argos/sidney/SQUARE_WAVE.h5/datagrams
10 |
11 | /Users/kenter/work/sentinel-5/src/s5cal_runtask/external_data/s5-generate-aux/rad_cal/RA_IRR_FEL
12 | /argos/penguins.jpg/G
13 |
14 | * Empty groups.
15 | /argos/trop/2015_03_16T16_32_16_MonA/after_dccorr_l1bavg/gseDat.nc
16 |
17 | * fill values
18 | /argos/trop/ql_test_020_minutes-020-021/qlbd4raw.lx.nc/BAND4/ICID_04003_GROUP_00000/OBSERVATIONS/signal # msmt_idx = 0
19 | /argos/trop/ql_test_020_minutes-020-021/qlbd4raw.lx.nc/BAND4/ICID_04003_GROUP_00000/OBSERVATIONS/signal_msmt_quality
20 | /argos/hdf-eos/OMI-Aura_L3-OMTO3e_2005m1214_v002-2006m0929t143855.he5/HDFEOS/GRIDS/OMI Column Amount O3/Data Fields/ColumnAmountO3
21 |
22 | *** DEMO ***
23 | /argos/trop/2015_01_07T09_29_12_svn4465_wls_prnu/ql_test_020_minutes-020-021/qlbd1eps.lx.nc/BAND1/ICID_04003_GROUP_00000/OBSERVATIONS/signal
24 |
--------------------------------------------------------------------------------
/development/readme.txt:
--------------------------------------------------------------------------------
1 | This directory contains information regarding development.
2 | Its primary use is to have fewer files in the project root directory.
3 |
--------------------------------------------------------------------------------
/development/to_be_checked.txt:
--------------------------------------------------------------------------------
1 | # Files to be checked with
2 | # 1: mypy
3 | # 2: pylint
4 | # 3: docstrings
5 |
6 | __init__.py
7 | __main__.py
8 | application.py
9 | argos_make_wrappers.py
10 | collect/__init__.py
11 | collect/collector.py
12 | collect/collectortree.py
13 | config/__init__.py
14 | config/abstractcti.py
15 | config/boolcti.py
16 | config/choicecti.py
17 | config/configitemdelegate.py
18 | config/configtreemodel.py
19 | config/configtreeview.py
20 | config/floatcti.py
21 | config/groupcti.py
22 | config/intcti.py
23 | config/qtctis.py
24 | config/stringcti.py
25 | config/untypedcti.py
26 | external/__init__.py
27 | external/ez_setup.py
28 | external/json_with_comments.py
29 | external/six.py
30 | info.py
31 | inspector/__init__.py
32 | inspector/abstract.py
33 | inspector/debug.py
34 | inspector/dialog.py
35 | inspector/errormsg.py
36 | inspector/pgplugins/__init__.py
37 | inspector/pgplugins/colorbar.py
38 | inspector/pgplugins/imageplot2d.py
39 | inspector/pgplugins/lineplot1d.py
40 | inspector/pgplugins/old_imageplot2d.py
41 | inspector/pgplugins/pgctis.py
42 | inspector/pgplugins/pghistlutitem.py
43 | inspector/pgplugins/pgplotitem.py
44 | inspector/qtplugins/__init__.py
45 | inspector/qtplugins/table.py
46 | inspector/qtplugins/text.py
47 | inspector/registry.py
48 | inspector/selectionpane.py
49 | main.py
50 | mytest.py
51 | qt/__init__.py
52 | qt/bindings.py
53 | qt/colorselect.py
54 | qt/labeledwidget.py
55 | qt/misc.py
56 | qt/scientificspinbox.py
57 | qt/shortcutedit.py
58 | qt/togglecolumn.py
59 | qt/treeitems.py
60 | qt/treemodels.py
61 | reg/__init__.py
62 | reg/basereg.py
63 | reg/dialog.py
64 | reg/tabmodel.py
65 | reg/tabview.py
66 | repo/__init__.py
67 | repo/baserti.py
68 | repo/colors.py
69 | repo/detailpanes.py
70 | repo/detailplugins/__init__.py
71 | repo/detailplugins/attr.py
72 | repo/detailplugins/prop.py
73 | repo/detailplugins/quicklook.py
74 | repo/filesytemrtis.py
75 | repo/iconfactory.py
76 | repo/memoryrtis.py
77 | repo/registry.py
78 | repo/repotreemodel.py
79 | repo/repotreeview.py
80 | repo/rtiplugins/__init__.py
81 | repo/rtiplugins/exdir.py
82 | repo/rtiplugins/hdf5.py
83 | repo/rtiplugins/jsonio.py
84 | repo/rtiplugins/ncdf.py
85 | repo/rtiplugins/numpyio.py
86 | repo/rtiplugins/pandasio.py
87 | repo/rtiplugins/pillowio.py
88 | repo/rtiplugins/scipyio.py
89 | repo/testdata.py
90 | utils/__init__.py
91 | utils/masks.py
92 | utils/moduleinfo.py
93 | widgets/__init__.py
94 | widgets/aboutdialog.py
95 | widgets/argostableview.py
96 | widgets/argostreeview.py
97 | widgets/constants.py
98 | widgets/display.py
99 | widgets/mainwindow.py
100 | widgets/misc.py
101 | widgets/testwalkdialog.py
102 |
103 |
104 | # Done
105 | utils/cls.py
106 | utils/config.py
107 | utils/defs.py
108 | utils/dirs.py
109 | utils/logs.py
110 | utils/misc.py
111 |
112 |
--------------------------------------------------------------------------------
/docs/Makefile:
--------------------------------------------------------------------------------
1 | # Minimal makefile for Sphinx documentation
2 | #
3 |
4 | # You can set these variables from the command line, and also
5 | # from the environment for the first two.
6 | SPHINXOPTS ?=
7 | SPHINXBUILD ?= sphinx-build
8 | SOURCEDIR = source
9 | BUILDDIR = build
10 |
11 | # Put it first so that "make" without argument is like "make help".
12 | help:
13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
14 |
15 | .PHONY: help Makefile
16 |
17 | # Catch-all target: route all unknown targets to Sphinx using the new
18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
19 | %: Makefile
20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
21 |
--------------------------------------------------------------------------------
/docs/create_html_doc.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # Creates the HTML documentation, including the autogenerated API docs.
4 | # Autogenerates API documentation. Creates .rst files in the api directory from the doc strings.
5 |
6 | # Solved:
7 | # Where to put documentation for __init__ functions?
8 | # Used: autodoc_class_signature = "separated"
9 | # Did not use "special members == __init__" (as in pydwf_api/DwfLibrary.rst), which includes
10 | # dunder methods.
11 | # How to get rid of full module paths in table of contents?
12 | # add_module_names seems to do the trick, except for 'class in package.path.to.module)
13 | # How to get rid of full module path in parameter definitions?
14 | # Fixed in Sphinx 4.4 with autodoc_typehints_format. In Sphinx 5.0 the default is "short"
15 | # https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html#confval-autodoc_typehints_format
16 |
17 | # TODO:
18 |
19 | # Look at suppress_warnings to get rid of PyQt import warnings?
20 |
21 | # How to document type vars like Selection?
22 | # Perhaps with autodoc_type_aliases
23 | # https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html#confval-autodoc_type_aliases
24 |
25 | # Look at autodoc_preserve_defaults when using default arguments.
26 | # https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html#confval-autodoc_preserve_defaults
27 |
28 | # Look at autodoc_inherit_docstrings to reuse doc strings.
29 | # https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html#confval-autodoc_inherit_docstrings
30 |
31 | # Look at Napo options:
32 | # https://www.sphinx-doc.org/en/master/usage/extensions/napoleon.html#module-sphinx.ext.napoleon
33 |
34 |
35 | # Look at Intersphinx:
36 | # https://www.sphinx-doc.org/en/master/usage/extensions/intersphinx.html
37 |
38 |
39 |
40 | set -eux
41 |
42 | ROOT_DIR=..
43 | PACKAGE_DIR="${ROOT_DIR}/argos"
44 | OUTPUT_DIR="source/api"
45 |
46 | make clean
47 | rm -rf "${OUTPUT_DIR}"
48 |
49 | export PYTHONPATH="${ROOT_DIR}"
50 |
51 | EXCLUDE="${PACKAGE_DIR}/external"
52 | sphinx-apidoc --no-toc --module-first --separate -o "${OUTPUT_DIR}" "${PACKAGE_DIR}" "${EXCLUDE}"
53 | make html
54 |
--------------------------------------------------------------------------------
/docs/examples/buggy_inspector.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # This file is part of Argos.
4 | #
5 | # Argos is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # Argos is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with Argos. If not, see .
17 |
18 | """ Test inspector.
19 | """
20 | import logging, os, sys
21 |
22 |
23 | import argos
24 | from argos.qt import initQApplication
25 | from argos.config.groupcti import MainGroupCti
26 | from argos.inspector.abstract import AbstractInspector
27 | from argos.inspector.registry import InspectorRegistry
28 | from argos.qt import Qt, QtWidgets
29 |
30 | logger = logging.getLogger(__name__)
31 |
32 | SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__))
33 |
34 | class BuggyInspector(AbstractInspector):
35 | """ Inspector that deliberately will not install.
36 |
37 | For testing and debugging purposes.
38 | """
39 | def __init__(self, collector, parent=None):
40 |
41 | super(BuggyInspector, self).__init__(collector, parent=parent)
42 |
43 | self.label = QtWidgets.QLabel()
44 | self.contentsLayout.addWidget(self.label)
45 |
46 | self._config = self._createConfig()
47 |
48 |
49 | @classmethod
50 | def axesNames(cls):
51 | """ The names of the axes that this inspector visualizes.
52 | See the parent class documentation for a more detailed explanation.
53 | """
54 | return tuple()
55 |
56 |
57 | def _createConfig(self):
58 | """ Creates a config tree item (CTI) hierarchy containing default children.
59 | """
60 | rootItem = MainGroupCti('inspector')
61 | return rootItem
62 |
63 |
64 | def _drawContents(self, reason=None, initiator=None):
65 | """ Draws the table contents from the sliced array of the collected repo tree item.
66 |
67 | The reason and initiator parameters are ignored.
68 | See AbstractInspector.updateContents for their description.
69 | """
70 | logger.debug("BuggyInspector._drawContents: {}".format(self))
71 |
72 |
73 | def persistentRegisterInspector(fullName, fullClassName, pythonPath=''):
74 | """ Registers an inspector
75 |
76 | Loads or inits the inspector registry, register the inspector and saves the settings.
77 | Important: instantiate a Qt application first to use the correct settings file/winreg.
78 | """
79 | registry = InspectorRegistry()
80 | registry.loadOrInitSettings()
81 | registry.registerInspector(fullName, fullClassName, pythonPath=pythonPath)
82 | registry.saveSettings()
83 |
84 |
85 | if __name__ == "__main__":
86 | argos.configBasicLogging(level='DEBUG')
87 | initQApplication()
88 | persistentRegisterInspector('Buggy inspector', 'BuggyInspector')
89 |
90 |
91 |
--------------------------------------------------------------------------------
/docs/examples/example_plugin.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # This file is part of Argos.
4 | #
5 | # Argos is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # Argos is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with Argos. If not, see .
17 |
18 | """ Test Rti
19 | """
20 |
21 | import logging, os
22 |
23 |
24 | from argos.qt import QtGui
25 | from argos.repo.baserti import BaseRti
26 | from argos.repo.iconfactory import RtiIconFactory
27 |
28 |
29 | ICONS_DIRECTORY = RtiIconFactory.ICONS_DIRECTORY
30 | logger = logging.getLogger(__name__)
31 |
32 |
33 | class TestFileRti(BaseRti):
34 | """ Repository tree item for testing
35 | """
36 | _label = "HDF File"
37 | _iconOpen = QtGui.QIcon(os.path.join(ICONS_DIRECTORY, 'memory.folder-open.svg'))
38 | _iconClosed = QtGui.QIcon(os.path.join(ICONS_DIRECTORY, 'memory.folder-closed.svg'))
39 |
40 | def __init__(self, nodeName='', fileName=''):
41 | """ Constructor
42 | """
43 | super(TestFileRti, self).__init__(nodeName=nodeName, fileName=fileName)
44 | self._checkFileExists()
45 |
46 |
47 | # def _openResources(self):
48 | # """ Opens the root Dataset.
49 | # """
50 | # logger.info("Opening: {}".format(self._fileName))
51 | # self._dataset = Dataset(self._fileName)
52 | #
53 | # def _closeResources(self):
54 | # """ Closes the root Dataset.
55 | # """
56 | # logger.info("Closing: {}".format(self._fileName))
57 | # self._dataset.close()
58 | # self._dataset = None
59 |
--------------------------------------------------------------------------------
/docs/examples/nonexisting.py:
--------------------------------------------------------------------------------
1 | """ Tries to install a non-existing RTI plugin
2 | """
3 | import sys
4 |
5 | # Uncomment the next line to run the example from within the distribution.
6 | # sys.path.append("../../")
7 |
8 | import os, logging
9 | import argos
10 | from argos.qt import initQApplication
11 | from argos.repo.registry import RtiRegistry
12 | from argos.inspector.registry import InspectorRegistry
13 |
14 | logger = logging.getLogger('argos')
15 |
16 | SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__))
17 |
18 |
19 |
20 | def registerRtis():
21 |
22 | registry = RtiRegistry()
23 | if '--reset-registry' in sys.argv:
24 | registry.deleteSettings()
25 | registry.loadOrInitSettings()
26 |
27 |
28 | registry.registerRti('Python File', 'example_plugin.TestFileRti', extensions=['.py'],
29 | pythonPath=SCRIPT_DIR)
30 | registry.registerRti('SVG File', 'does_not_exist.svg.SvgFile', extensions=['.svg'])
31 | registry.saveSettings()
32 |
33 |
34 | def registerInspectors():
35 |
36 | registry = InspectorRegistry()
37 | if '--reset-registry' in sys.argv:
38 | registry.deleteSettings()
39 | registry.loadOrInitSettings()
40 |
41 | registry.registerInspector('Non Existing', 'does_not_exist.Inspector')
42 | registry.saveSettings()
43 |
44 |
45 | def main():
46 | logger.info("Start...")
47 | logger.debug("current dir: {}".format(SCRIPT_DIR))
48 |
49 | # Important: instantiate a Qt application first to use the correct settings file/winreg.
50 | _app = initQApplication()
51 |
52 | registerRtis()
53 | registerInspectors()
54 | logger.info("Done...")
55 |
56 |
57 | if __name__ == "__main__":
58 | argos.configBasicLogging(level='DEBUG')
59 | main()
60 |
61 |
62 |
--------------------------------------------------------------------------------
/docs/examples/print_plugins.py:
--------------------------------------------------------------------------------
1 | """ Print the plutings from the registry
2 | """
3 | from __future__ import print_function
4 | import sys
5 |
6 | # Uncomment the next line to run the example from within the distribution
7 | # sys.path.append("../../")
8 |
9 | import argos
10 | from argos.qt import initQApplication
11 | from argos.inspector.registry import InspectorRegistry
12 | from argos.repo.registry import RtiRegistry
13 |
14 | def printReg(name, registry):
15 | print("{} registry....".format(name))
16 | for nr, regItem in enumerate(registry.items):
17 | print(" {0:03d}: {1.identifier:25} {1.fullClassName:30} ".format(nr, regItem))
18 | print()
19 |
20 | def main():
21 | # Important: instantiate a Qt application first to use the correct settings file/winreg.
22 | _app = initQApplication()
23 |
24 | inspectorRegistry = InspectorRegistry()
25 | inspectorRegistry.loadOrInitSettings()
26 | printReg("Inspector", inspectorRegistry)
27 |
28 | rtiRegistry = RtiRegistry()
29 | rtiRegistry.loadOrInitSettings()
30 | printReg("File format", rtiRegistry)
31 |
32 |
33 | if __name__ == "__main__":
34 | argos.configBasicLogging(level='WARN')
35 | main()
36 |
37 |
--------------------------------------------------------------------------------
/docs/make.bat:
--------------------------------------------------------------------------------
1 | @ECHO OFF
2 |
3 | pushd %~dp0
4 |
5 | REM Command file for Sphinx documentation
6 |
7 | if "%SPHINXBUILD%" == "" (
8 | set SPHINXBUILD=sphinx-build
9 | )
10 | set SOURCEDIR=source
11 | set BUILDDIR=build
12 |
13 | %SPHINXBUILD% >NUL 2>NUL
14 | if errorlevel 9009 (
15 | echo.
16 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
17 | echo.installed, then set the SPHINXBUILD environment variable to point
18 | echo.to the full path of the 'sphinx-build' executable. Alternatively you
19 | echo.may add the Sphinx directory to PATH.
20 | echo.
21 | echo.If you don't have Sphinx installed, grab it from
22 | echo.https://www.sphinx-doc.org/
23 | exit /b 1
24 | )
25 |
26 | if "%1" == "" goto help
27 |
28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
29 | goto end
30 |
31 | :help
32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
33 |
34 | :end
35 | popd
36 |
--------------------------------------------------------------------------------
/docs/screen_shots/argos_gui.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/titusjan/argos/5a334a5d5625300dab5f4e98a36e924600b6e55e/docs/screen_shots/argos_gui.png
--------------------------------------------------------------------------------
/docs/screen_shots/collector_1d.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/titusjan/argos/5a334a5d5625300dab5f4e98a36e924600b6e55e/docs/screen_shots/collector_1d.png
--------------------------------------------------------------------------------
/docs/screen_shots/collector_2d.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/titusjan/argos/5a334a5d5625300dab5f4e98a36e924600b6e55e/docs/screen_shots/collector_2d.png
--------------------------------------------------------------------------------
/docs/screen_shots/color_legend.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/titusjan/argos/5a334a5d5625300dab5f4e98a36e924600b6e55e/docs/screen_shots/color_legend.png
--------------------------------------------------------------------------------
/docs/screen_shots/reset_menu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/titusjan/argos/5a334a5d5625300dab5f4e98a36e924600b6e55e/docs/screen_shots/reset_menu.png
--------------------------------------------------------------------------------
/docs/screen_shots/settings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/titusjan/argos/5a334a5d5625300dab5f4e98a36e924600b6e55e/docs/screen_shots/settings.png
--------------------------------------------------------------------------------
/docs/source/conf.py:
--------------------------------------------------------------------------------
1 | # Configuration file for the Sphinx documentation builder.
2 | #
3 | # For the full list of built-in configuration values, see the documentation:
4 | # https://www.sphinx-doc.org/en/master/usage/configuration.html
5 |
6 | # -- Project information -----------------------------------------------------
7 | # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
8 |
9 | project = 'Argos'
10 | copyright = '2022, Pepijn Kenter'
11 | author = 'Pepijn Kenter'
12 | release = '0.4.3'
13 |
14 | # -- General configuration ---------------------------------------------------
15 | # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
16 |
17 | extensions = [
18 | 'sphinx.ext.autodoc',
19 | 'sphinx_rtd_theme', # Read-the-docs theme
20 | 'sphinx.ext.napoleon', # Support for Google style docstrings
21 | ]
22 |
23 | templates_path = ['_templates']
24 | exclude_patterns = []
25 |
26 | add_module_names = False
27 | # python_use_unqualified_type_names = True # Doesn't seem to make a difference.
28 | modindex_common_prefix = ['argos.']
29 |
30 | autodoc_class_signature = "separated"
31 | autodoc_typehints = "description"
32 |
33 | # Doesn't work nicely with: autodoc_typehints = "both". It duplicates parameters, first with
34 | # description, then with teyp
35 | #napoleon_use_param = False
36 |
37 | # -- Options for HTML output -------------------------------------------------
38 | # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
39 |
40 | html_static_path = ['_static']
41 |
42 | html_theme = 'sphinx_rtd_theme'
43 | html_theme_options = {
44 | 'style_external_links': True}
45 |
--------------------------------------------------------------------------------
/docs/source/index.rst:
--------------------------------------------------------------------------------
1 | .. Argos documentation master file, created by
2 | sphinx-quickstart on Sat Aug 13 10:32:59 2022.
3 | You can adapt this file completely to your liking, but it should at least
4 | contain the root `toctree` directive.
5 |
6 | Welcome to Argos's documentation!
7 | =================================
8 |
9 | .. toctree::
10 | :maxdepth: 2
11 | :caption: Contents:
12 |
13 |
14 |
15 | Indices and tables
16 | ==================
17 |
18 | * :ref:`genindex`
19 | * :ref:`modindex`
20 | * :ref:`search`
21 |
--------------------------------------------------------------------------------
/docs_new.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/titusjan/argos/5a334a5d5625300dab5f4e98a36e924600b6e55e/docs_new.zip
--------------------------------------------------------------------------------
/pylint_check.sh:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 |
3 | # Performs pylint check on the given files. If no files are given it will search in the project for all relevant source files.
4 |
5 | if [[ -z "$*" ]] ; then
6 | input_files=$(find . -type f \( -iname "*.py" ! -iname "__init__.py" ! -iname "setup.py" ! -iname "conf.py" ! -wholename "./tests/*" \) )
7 | else
8 | input_files="$*"
9 | fi
10 |
11 | #echo ${input_files}
12 | pylint-3.4 --rcfile=.pylintrc '--msg-template={path}:{line}: [{msg_id} ({symbol}), {obj}] {msg}' ${input_files}
13 |
14 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | from __future__ import absolute_import
4 |
5 | # To make a release follow these steps:
6 | # python setup.py sdist
7 | # twine upload dist/argos-0.2.0rc1.tar.gz
8 | # or better
9 | # rm -rf build dist
10 | # python setup.py bdist_wheel
11 | # twine check dist/*
12 | # twine upload dist/argos-x.y.z-py3-none-any.whl
13 |
14 | # If you get invalid command 'bdist_wheel', you must install the 'wheel' package first.
15 |
16 | # See also https://packaging.python.org/en/latest/distributing.html
17 | # TODO: still can't make a wheel even following the instructions in the link below.
18 | # http://stackoverflow.com/questions/26664102/why-can-i-not-create-a-wheel-in-pyt
19 |
20 | import os
21 | import sys
22 |
23 | def err(*args, **kwargs):
24 | sys.stderr.write(*args, **kwargs)
25 | sys.stderr.write('\n')
26 |
27 |
28 | try:
29 | from setuptools import setup, find_packages
30 | except ImportError:
31 | err("Argos requires setuptools for intallation. (https://pythonhosted.org/an_example_pypi_project/setuptools.html)")
32 | err("You can download and install it simply with: python argos/external/ez_setup.py")
33 | sys.exit(1)
34 |
35 |
36 | from argos import info
37 |
38 | if sys.version_info < (3,7):
39 | err("Argos requires Python 3.7")
40 | sys.exit(1)
41 |
42 |
43 | readme = open('README.rst').read()
44 | history = open('HISTORY.rst').read().replace('.. :changelog:', '')
45 |
46 |
47 | # Don't require PyQt for two reasons. First users may use PySide2 (although at the moment PySide is
48 | # not yet working). Second, users may use Anaconda to install PyQt. Anaconda uses a different
49 | # package name (pyqt) than pip (PyQt5) and the tools can't detect correctly if PyQt has been
50 | # installed. This leads to trouble. See:
51 | # https://www.anaconda.com/using-pip-in-a-conda-environment/
52 | # https://github.com/ContinuumIO/anaconda-issues/issues/1554
53 | #
54 | # In Debian pip will ignored installed system packages (e.g. use --ignore-installed).
55 | # To override this use: export PIP_IGNORE_INSTALLED=0
56 | # See https://github.com/pypa/pip/issues/4222
57 |
58 | install_requires = [
59 | #"PyQt5 >= 5.6.0", # Don't require PyQt. See comment above
60 | "cmlib >= 1.1.2", # Needed, even if no plugins are installed.
61 | "numpy >= 1.11",
62 | # Argos will technically work without pyqtgraph and h5py, but with very limited functionality.
63 | "pgcolorbar >= 1.1.1",
64 | "pyqtgraph >= 0.11",
65 | # "h5py >= 2.6"
66 | ]
67 |
68 | long_description = readme + '\n\n' + history
69 | print(long_description)
70 |
71 | setup(
72 | name = info.REPO_NAME,
73 | version = info.VERSION,
74 | description = info.SHORT_DESCRIPTION,
75 | long_description = readme + '\n\n' + history,
76 | long_description_content_type = 'text/x-rst',
77 | author = info.AUTHOR,
78 | author_email = info.EMAIL,
79 | license = "GPLv3",
80 | url=info.PROJECT_URL,
81 | packages = find_packages(),
82 | package_data = {'': ['HISTORY.rst'],
83 | info.PACKAGE_NAME: ['img/argos.css', 'img/snipicons/*', 'utils/default_logging.json']},
84 | entry_points={'gui_scripts': ['argosw = argos.main:main'],
85 | 'console_scripts': ['argos = argos.main:main',
86 | 'argos_make_wrappers = argos.argos_make_wrappers:main']},
87 | install_requires = install_requires,
88 | zip_safe = False,
89 | classifiers = [
90 | 'Development Status :: 4 - Beta',
91 | 'Environment :: X11 Applications :: Qt',
92 | 'Intended Audience :: Developers',
93 | 'Intended Audience :: End Users/Desktop',
94 | 'Intended Audience :: Science/Research',
95 | 'License :: OSI Approved :: GNU General Public License v3 (GPLv3)',
96 | 'Natural Language :: English',
97 | 'Operating System :: MacOS :: MacOS X',
98 | 'Operating System :: Microsoft :: Windows',
99 | 'Operating System :: POSIX :: Linux',
100 | 'Programming Language :: Python :: 3.7',
101 | 'Programming Language :: Python :: 3.8',
102 | 'Programming Language :: Python :: 3.9',
103 | 'Programming Language :: Python :: 3.10',
104 | 'Topic :: Software Development',
105 | 'Topic :: Scientific/Engineering',
106 | 'Topic :: Utilities',
107 | ],
108 | keywords = 'NetCDF HDF5 plotting graphs',
109 | #test_suite='tests',
110 | #tests_require=test_requirements
111 | )
112 |
--------------------------------------------------------------------------------
/tests/create_test_data/hdf5/make_null_vars.py:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | # I only implemented the Python 2 variant of
5 | # See: http://docs.h5py.org/en/latest/strings.html
6 | # http://docs.h5py.org/en/latest/high/dataset.html#creating-and-reading-empty-or-null-datasets-and-attributes
7 |
8 | import h5py
9 | import numpy
10 |
11 |
12 | # Doesn't seem to work, perhaps this is new. Let's try later
13 | def make_empty_attr():
14 | """ Makes HDF-5 file with special cases
15 | """
16 | with h5py.File("empty.hdf5", "w") as hdf:
17 | #Gives: AttributeError: 'module' object has no attribute 'Empty'
18 | ds_ints = hdf.create_dataset("emtpy attributes", (15,), dtype=int)
19 | ds_ints[:] = 15
20 | ds_ints.attrs["description"] = "A normal dataset with an empty attribute"
21 | ds_ints.attrs["emptyattr"] = h5py.Empty("f")
22 |
23 |
24 |
25 | def make_empty_ds():
26 | """ Makes HDF-5 file emtpy dataset
27 | """
28 | with h5py.File("empty.hdf5", "w") as hdf:
29 | #Gives: AttributeError: 'module' object has no attribute 'Empty'
30 | ds_empty = hdf.create_dataset("EmptyDataset", data=h5py.Empty("f"))
31 | ds_empty.attrs["description"] = "An emtpy dataset"
32 | ds_empty.attrs["comments"] = "See http://docs.h5py.org/en/latest/high/dataset.html#creating-and-reading-empty-or-null-datasets-and-attributes"
33 |
34 |
35 | if __name__ == '__main__':
36 | #make_empty_attr()
37 | make_empty_ds()
38 |
39 |
--------------------------------------------------------------------------------
/tests/create_test_data/hdf5/read.py:
--------------------------------------------------------------------------------
1 | # -s "/argos/2014-04-29_FEL-350/2014-04-29T11_32_032 shutters m1.h5/scan_procedure/procedure"
2 |
3 | import numpy as np
4 | import h5py
5 |
6 |
7 | def main():
8 |
9 | with h5py.File("string.hdf5", "r") as root:
10 | ds = root['fixed_len_ascii_ds']
11 | print(type(ds[:]))
12 | print(type(ds[0]))
13 |
14 | ds = root['int dataset']
15 | print(type(ds[:]))
16 | print(type(ds[0]))
17 |
18 |
19 | if __name__ == "__main__":
20 | main()
21 |
22 |
--------------------------------------------------------------------------------
/tests/create_test_data/netcdf/create_test_data.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 |
4 | """ Create some data files that can be used for testing.
5 |
6 | Is by no means extensive perhaps in the future it will. For now it is updated when issues are encountered.
7 | """
8 | import logging
9 | import os.path
10 |
11 | import numpy as np
12 |
13 | from argos.utils.dirs import ensureDirectoryExists
14 | from argos.utils.logs import makeLogFormat
15 |
16 | logger = logging.getLogger("create_test_data")
17 |
18 | def createNetCdfTestData():
19 | """ Create files with the python netCDF4 module
20 |
21 | From netCDF4 tutorial:
22 | https://unidata.github.io/netcdf4-python/netCDF4/index.html
23 | https://github.com/Unidata/netcdf4-python/blob/master/examples/tutorial.py
24 | """
25 | from netCDF4 import Dataset, Group, Variable
26 |
27 | with Dataset("normal.nc", "w", format="NETCDF4") as rootgrp:
28 | rootgrp.description = "File with normal use cases"
29 |
30 | level = rootgrp.createDimension("level", None)
31 | time = rootgrp.createDimension("time", None)
32 | lat = rootgrp.createDimension("lat", 73)
33 | lon = rootgrp.createDimension("lon", 144)
34 |
35 | times = rootgrp.createVariable('time','f8',('time',))
36 | levels = rootgrp.createVariable('level','i4',('level',))
37 | latitudes = rootgrp.createVariable('lat','f4',('lat',))
38 | longitudes = rootgrp.createVariable('lon','f4',('lon',))
39 |
40 | latitudes[:] = np.arange(-90,91,2.5)
41 | longitudes[:] = np.arange(-180,180,2.5)
42 |
43 |
44 | with Dataset("dim_size_0.nc", "w", format="NETCDF4") as rootgrp:
45 | rootgrp.description = "Contains datasets with dimensions of size 0"
46 |
47 | rootgrp.createDimension("fixed", 0)
48 | rootgrp.createDimension("unlimited", None)
49 |
50 | rootgrp.createVariable('1d_fixed','i4',('fixed',))
51 | rootgrp.createVariable('1d_unlim','i4',('unlimited',))
52 | rootgrp.createVariable('2d','f4', ('fixed','unlimited'))
53 |
54 |
55 | with Dataset("scalars.nc", "w", format="NETCDF4") as rootgrp:
56 | rootgrp.description = "Contains datasets with scalars"
57 | scalar = rootgrp.createVariable('my_scalar','i4',)
58 | scalar[:] = 66
59 |
60 | maskedScalar = rootgrp.createVariable('masked_scalar','i4', fill_value=-99)
61 | maskedScalar.description = "A scalar set to the fill value"
62 | maskedScalar[:] = -99
63 |
64 |
65 |
66 |
67 | def createTestData(outputDir):
68 | """ Creates test data files in output directory
69 | """
70 | ensureDirectoryExists(outputDir)
71 | oldDir = os.getcwd()
72 | os.chdir(outputDir)
73 | try:
74 | createNetCdfTestData()
75 |
76 | finally:
77 | os.chdir(oldDir)
78 |
79 |
80 | if __name__ == "__main__":
81 | logging.basicConfig(level="DEBUG", format=makeLogFormat(loggerName=True, fileLine=False))
82 | createTestData("test_data")
83 |
--------------------------------------------------------------------------------
/tests/create_test_data/netcdf/make_large_dims.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 |
4 | /argos/mytests/large_dims.nc/my_var
5 | "/argos/Mini Scanner Output/acceptance1.h5/samples/re"
6 | """
7 |
8 | def main():
9 |
10 | from netCDF4 import Dataset
11 | rootgrp = Dataset("large_dims.nc", "w", format="NETCDF4")
12 |
13 | timeDim = rootgrp.createDimension("my_long_time_dim_name", 10000)
14 | lonDim = rootgrp.createDimension("lon", 2)
15 | latDim = rootgrp.createDimension("lat", 2)
16 |
17 |
18 | var = rootgrp.createVariable("my_var", "c",
19 | ("my_long_time_dim_name", "lon", "lat"))
20 |
21 | print(var.shape)
22 | var[:] = 'p'
23 |
24 | rootgrp.close()
25 |
26 |
27 |
28 |
29 | if __name__ == '__main__':
30 |
31 | main()
32 |
--------------------------------------------------------------------------------
/tests/create_test_data/numpy/numpyio.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 |
3 | def main():
4 | a = np.arange(24).reshape(3,8)
5 |
6 | np.save("range.3x8.npy", a)
7 |
8 | x = np.arange(10)
9 | y = np.sin(x)
10 | d = ['a', {5: '5'}]
11 | np.savez('sinx.npz', x=x, y=y, d=d)
12 | np.savez('d.npz', d)
13 |
14 | if __name__ == "__main__":
15 | main()
16 |
17 |
--------------------------------------------------------------------------------
/tests/create_test_data/readme.txt:
--------------------------------------------------------------------------------
1 | This directory contains some scripts that creates potential test data. To be refined.
2 |
3 | 2022-02-13, Pepijn Kenter
4 |
--------------------------------------------------------------------------------
/tests/plot_nans.py:
--------------------------------------------------------------------------------
1 | """ NaNs not plotted
2 |
3 | https://github.com/pyqtgraph/pyqtgraph/issues/1057
4 |
5 | Also
6 | https://github.com/pyqtgraph/pyqtgraph/issues/1011
7 |
8 |
9 | """
10 |
11 | import numpy as np
12 | import pyqtgraph as pg
13 |
14 | print("Qt Version: {}".format(pg.QtCore.QT_VERSION_STR))
15 | print("PyQt Version: {}".format(pg.QtCore.PYQT_VERSION_STR))
16 |
17 | data = np.random.normal(size=20)
18 | data2 = np.random.normal(size=20)
19 | data2 = data2.astype(np.float32)
20 | print("data2.dtype: {}".format(data2.dtype))
21 | #data2[0] = np.nan
22 |
23 | data2[10] = 1e14 # Works
24 |
25 | data2[10] = 4.17e14 # still works
26 | # data2[10] = 4.18e14 # Fails
27 |
28 | #data2[10] = 2e27
29 | data2[15] = np.nan
30 |
31 |
32 | #data2[100] = 4.5e14 # See pyqtgraph issue #1011
33 |
34 | #pg.plot(data, title="no NaN")
35 | pg.plot(data2, title="one NaN")
36 |
37 | pg.QtGui.QApplication.exec_()
38 |
--------------------------------------------------------------------------------
/tests/test_cti.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 |
4 |
5 | import unittest, copy
6 | from json import loads, dumps
7 |
8 |
9 | from argos.qt import QtWidgets, QtGui
10 | from argos.config.untypedcti import UntypedCti
11 | from argos.config.qtctis import ColorCti
12 |
13 |
14 |
15 | class TestUntypedCtis(unittest.TestCase):
16 |
17 | def setUp(self):
18 | self.invisibleRootItem = UntypedCti(nodeName='', defaultData=0.123456789012345678901234567890)
19 | self.invisibleRootItem.insertChild(UntypedCti(nodeName='kid', defaultData=-7))
20 |
21 |
22 | def test__eq__(self):
23 |
24 | ctiIn = UntypedCti('parent', defaultData=7)
25 | self.assertEqual(ctiIn, ctiIn)
26 | self.assertEqual(ctiIn, UntypedCti('parent', defaultData=7))
27 |
28 | self.assertNotEqual(ctiIn, UntypedCti('parent', defaultData=9))
29 |
30 | ctiOut = UntypedCti('parent', defaultData=7)
31 | ctiIn.insertChild(UntypedCti('kid', defaultData=23))
32 | self.assertNotEqual(ctiIn, ctiOut)
33 |
34 | ctiOut.insertChild(UntypedCti('kid', defaultData=23))
35 | self.assertEqual(ctiIn, ctiOut)
36 |
37 | ctiIn.childItems[0].data = 99
38 | self.assertNotEqual(ctiIn, ctiOut)
39 |
40 |
41 | def tearDown(self):
42 | pass
43 |
44 |
45 |
46 | class TestSimpleCtis(unittest.TestCase):
47 |
48 | def setUp(self):
49 | self.invisibleRootItem = UntypedCti(nodeName='', defaultData=0.123456789012345678901234567890)
50 | self.invisibleRootItem.insertChild(UntypedCti(nodeName='kid', defaultData=-7))
51 |
52 | def tearDown(self):
53 | pass
54 |
55 |
56 | def testColorCti(self):
57 |
58 | colorStr = '#FF33EE'
59 | cti = ColorCti('color', defaultData=colorStr)
60 | self.assertEqual(cti.data, QtGui.QColor(colorStr))
61 | self.assertEqual(cti.data, QtGui.QColor(colorStr))
62 | self.assertEqual(cti.displayValue, colorStr)
63 |
64 |
65 | def closedLoop(self, ctiIn):
66 | """ serialize cti default values to json and back
67 | """
68 | nonDefaults = ctiIn.getNonDefaultsDict()
69 | json = dumps(nonDefaults)
70 | valuesDict = loads(json)
71 |
72 | ctiOut = copy.deepcopy(ctiIn)
73 | ctiOut._data = None
74 | ctiOut.setValuesFromDict(valuesDict)
75 | return ctiOut
76 |
77 |
78 | def testClosedLoop(self):
79 | # A data different than its default should be restored
80 | ctiIn = ColorCti('parent', defaultData='#ABCDEF')
81 | ctiIn.data='#DEADBE' # works only if data is set, otherwise ctiOut.data will be None
82 | ctiOut = self.closedLoop(ctiIn)
83 | print("ctiIn {}".format(ctiIn.__dict__))
84 | print("ctiOut {}".format(ctiOut.__dict__))
85 | self.assertEqual(ctiIn, ctiOut)
86 |
87 |
88 | if __name__ == '__main__':
89 | unittest.main()
90 |
91 |
92 |
--------------------------------------------------------------------------------
/tests/test_qsettings.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 |
4 | """
5 | test_argos
6 | ----------------------------------
7 |
8 | Tests for `argos` module.
9 | """
10 | import sys
11 | import unittest
12 |
13 | from argos.qt import QtCore
14 | from argos.qt.misc import initQCoreApplication as getQApplicationInstance
15 |
16 |
17 | class TestArgos(unittest.TestCase):
18 |
19 | def setUp(self):
20 | self.app = getQApplicationInstance()
21 |
22 | self.groupName = '__test__'
23 | self.qs = QtCore.QSettings()
24 | self.qs.remove(self.groupName) # start with clean slate
25 | self.qs.beginGroup(self.groupName)
26 |
27 |
28 | def test_read_write(self):
29 |
30 | self.qs.setValue('int', -6)
31 | self.assertEqual(-6, self.qs.value('int'))
32 |
33 | self.qs.setValue('float', 7.7)
34 | self.assertEqual(7.7, self.qs.value('float'))
35 |
36 | self.qs.setValue('str', 'six')
37 | self.assertEqual('six', self.qs.value('str'))
38 |
39 | large_str = '0123456789' * 10000
40 | self.qs.setValue('large_str', large_str)
41 | self.assertEqual(large_str, self.qs.value('large_str'))
42 |
43 | for p in range(5):
44 | n = pow(10, p)
45 | arr = range(n)
46 | name = 'arr10pow{}'.format(p)
47 | self.qs.setValue(name, arr)
48 | print("\np={}, n={}".format(p, n))
49 | print(repr(arr))
50 | print(repr(self.qs.value(name)))
51 | self.assertEqual(arr, arr)
52 |
53 |
54 |
55 | def tearDown(self):
56 | self.qs.endGroup()
57 | self.qs.remove(self.groupName)
58 |
59 |
60 | if __name__ == '__main__':
61 | unittest.main()
62 |
63 |
--------------------------------------------------------------------------------
/tests/test_rti.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 |
4 |
5 | import unittest
6 | import numpy as np
7 |
8 | from numpy.testing import assert_array_equal
9 | from argos.repo.memoryrtis import ArrayRti
10 |
11 |
12 |
13 | class TestUntypedCtis(unittest.TestCase):
14 |
15 | def setUp(self):
16 | self.arr = np.arange(24).reshape(6, 4)
17 | self.rti = ArrayRti(self.arr, 'my array')
18 |
19 |
20 | def test_indexing(self):
21 | """ Test indexing and slicing
22 | """
23 |
24 | self.assertEqual(self.rti[2, 1], self.arr[2, 1])
25 | assert_array_equal(self.rti[:], self.arr[:])
26 | assert_array_equal(self.rti[2:5, :], self.arr[2:5, :])
27 | assert_array_equal(self.rti[-1], self.arr[-1])
28 |
29 | # Multidimensional indexing
30 | idx0 = [1, 1, 3]
31 | idx1 = [0, -1, 2]
32 | assert_array_equal(self.rti[idx0, idx1], self.arr[idx0, idx1])
33 |
34 | slices = [slice(2, None), 1]
35 | assert_array_equal(self.rti[slices], self.arr[slices])
36 |
37 | slices = tuple([slice(2), slice(1)])
38 | assert_array_equal(self.rti[slices], self.arr[slices])
39 |
40 |
41 |
42 | if __name__ == '__main__':
43 | unittest.main()
44 |
45 |
46 |
--------------------------------------------------------------------------------
/tests/test_treemodels.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 |
4 |
5 | import unittest, logging, sys
6 |
7 | from argos.qt.treemodels import BaseTreeModel
8 | from argos.qt.treeitems import BaseTreeItem
9 |
10 |
11 | class TestTreeItemGetByPath(unittest.TestCase):
12 |
13 |
14 | def setUp(self):
15 |
16 | self.rootItem = BaseTreeItem('root')
17 | self.item0 = self.rootItem.insertChild(BaseTreeItem('item0'))
18 | self.item1 = self.item0.insertChild(BaseTreeItem('item1'))
19 | self.item1a = self.item0.insertChild(BaseTreeItem('item1a'))
20 | self.item2 = self.item1.insertChild(BaseTreeItem('item2'))
21 | self.item3 = self.rootItem.insertChild(BaseTreeItem('item3'))
22 |
23 |
24 | def testStartAtItem(self):
25 |
26 | # Normal use
27 | checkItem = self.rootItem.findByNodePath('item3')
28 | self.assertIs(checkItem, self.item3)
29 |
30 | # Long path
31 | checkItem = self.rootItem.findByNodePath('item0/item1/item2')
32 | self.assertIs(checkItem, self.item2)
33 |
34 | # Leading slash
35 | self.assertRaises(AssertionError, self.rootItem.findByNodePath, '/item3')
36 |
37 | # Trailing slash
38 | checkItem = self.rootItem.findByNodePath('item3/')
39 | self.assertIs(checkItem, self.item3)
40 |
41 | # Long path with double slashes
42 | checkItem = self.rootItem.findByNodePath('item0///item1//')
43 | self.assertIs(checkItem, self.item1)
44 |
45 | # Item not found
46 | self.assertRaises(IndexError, self.rootItem.findByNodePath, 'item0/narf//')
47 |
48 | # Empty string
49 | self.assertRaises(IndexError, self.rootItem.findByNodePath, '')
50 |
51 | # None-strings
52 | self.assertRaises(TypeError, self.rootItem.findByNodePath, 444)
53 |
54 |
55 | class TestGetByPath(unittest.TestCase):
56 |
57 |
58 | def setUp(self):
59 |
60 | self.model = BaseTreeModel()
61 |
62 | self.item0, self.index0 = self.insertBaseTreeItem('item0')
63 | self.item1, self.index1 = self.insertBaseTreeItem('item1', self.index0)
64 | self.item1a, self.index1a = self.insertBaseTreeItem('item1a', self.index0)
65 | self.item2, self.index2 = self.insertBaseTreeItem('item2', self.index1)
66 |
67 | self.item3, self.index3 = self.insertBaseTreeItem('item3')
68 |
69 |
70 | def tearDown(self):
71 | pass
72 |
73 | def insertBaseTreeItem(self, nodeName, parentIndex=None):
74 | "Inserts a new BaseTreeItem item in the model"
75 | item = BaseTreeItem(nodeName=nodeName)
76 | index = self.model.insertItem(item, parentIndex=parentIndex)
77 | return item, index
78 |
79 | def getLastItem(self, path, startIndex=None):
80 | """ Gets the last item from the itemAndIndexPath
81 | """
82 | return self.model.findItemAndIndex(path, startIndex=startIndex)
83 |
84 |
85 | def testLastPathItemStartAtRoot(self):
86 |
87 | # Normal use
88 | checkItem, checkIndex = self.getLastItem('item3')
89 | self.assertIs(self.model.getItem(checkIndex), checkItem)
90 | self.assertIs(checkItem, self.item3)
91 |
92 | # Long path
93 | checkItem, checkIndex = self.getLastItem('item0/item1/item2')
94 | self.assertIs(self.model.getItem(checkIndex), checkItem)
95 | self.assertIs(checkItem, self.item2)
96 |
97 | # Leading slash
98 | checkItem, checkIndex = self.getLastItem('/item3')
99 | self.assertIs(self.model.getItem(checkIndex), checkItem)
100 | self.assertIs(checkItem, self.item3)
101 |
102 | # Trailing slash
103 | checkItem, checkIndex = self.getLastItem('item3/')
104 | self.assertIs(self.model.getItem(checkIndex), checkItem)
105 | self.assertIs(checkItem, self.item3)
106 |
107 | # Long path with double slashes
108 | checkItem, checkIndex = self.getLastItem('/item0///item1//')
109 | self.assertIs(self.model.getItem(checkIndex), checkItem)
110 | self.assertIs(checkItem, self.item1)
111 |
112 | # Item not found
113 | self.assertRaises(IndexError, self.getLastItem, '/item0/narf//')
114 |
115 | # Invisible root
116 | checkItem, checkIndex = self.getLastItem('/')
117 | self.assertFalse(checkIndex.isValid())
118 | self.assertIs(checkItem, self.model.invisibleRootTreeItem)
119 |
120 | # Empty string
121 | self.assertRaises(IndexError, self.getLastItem, '')
122 |
123 | # None-strings
124 | self.assertRaises(TypeError, self.getLastItem, 444)
125 |
126 |
127 | def testLastPathItemStartAtIndex(self):
128 |
129 | # Sanity check
130 | self.assertRaises(IndexError, self.getLastItem, 'item1/item2')
131 |
132 | # Normal use
133 | checkItem, checkIndex = self.getLastItem('item1/item2', startIndex=self.index0)
134 | self.assertIs(self.model.getItem(checkIndex), checkItem)
135 | self.assertIs(checkItem, self.item2)
136 |
137 | # Starting slash starts at root
138 | checkItem, checkIndex = self.getLastItem('/item0/item1/item2', startIndex=self.index0)
139 | self.assertIs(self.model.getItem(checkIndex), checkItem)
140 | self.assertIs(checkItem, self.item2)
141 |
142 |
143 | if __name__ == '__main__':
144 | logging.basicConfig(level='DEBUG', stream=sys.stderr,
145 | format='%(asctime)s %(filename)25s:%(lineno)-4d : %(levelname)-7s: %(message)s')
146 | unittest.main()
147 |
148 |
149 |
--------------------------------------------------------------------------------
/tests/test_utils.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | """
4 | Tests functionality from the utils package
5 |
6 | """
7 |
8 | import unittest
9 |
10 | from argos.utils.cls import isAString, isBinary
11 | import numpy as np
12 |
13 |
14 | class TestStringTypeDetection(unittest.TestCase):
15 | """ Tests if the is_a_string and comparable function work
16 |
17 | The test should work on both Python-2 and Python-3
18 |
19 | """
20 |
21 | def setUp(self):
22 | pass
23 | self.b_lit = b'bytes literal'
24 | self.s_lit = 'literal literal'
25 | self.u_lit = u'unicode literal'
26 |
27 | self.np_b_lit = np.bytes_('numpy bytes literal')
28 | self.np_s_lit = np.str_('numpy unicode literal')
29 | self.np_u_lit = np.unicode_('numpy unicode literal')
30 |
31 |
32 | def test_is_a_string(self):
33 | """
34 | Result py-2 py-3
35 | ----------------- ----- -----
36 | b'bytes literal' True False
37 | 'string literal' True True
38 | u'unicode literal' True True
39 | """
40 | self.assertFalse(isAString(self.b_lit))
41 | self.assertFalse(isAString(self.np_b_lit))
42 |
43 | self.assertTrue(isAString(self.s_lit))
44 | self.assertTrue(isAString(self.np_s_lit))
45 |
46 | self.assertTrue(isAString(self.u_lit))
47 | self.assertTrue(isAString(self.np_u_lit))
48 |
49 |
50 |
51 |
52 | def test_is_binary(self):
53 | """
54 | Result py-2 py-3
55 | ----------------- ----- -----
56 | b'bytes literal' True True
57 | 'string literal' True False
58 | u'unicode literal' False False
59 | """
60 |
61 | self.assertTrue(isBinary(self.b_lit))
62 | self.assertTrue(isBinary(self.np_b_lit))
63 |
64 | self.assertFalse(isBinary(self.s_lit))
65 | self.assertFalse(isBinary(self.np_s_lit))
66 |
67 | self.assertFalse(isBinary(self.u_lit))
68 | self.assertFalse(isBinary(self.np_u_lit))
69 |
70 |
71 |
72 |
73 | def tearDown(self):
74 | pass
75 |
76 |
77 | if __name__ == '__main__':
78 | unittest.main()
79 |
80 |
81 |
--------------------------------------------------------------------------------