├── .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 | 7 | 8 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /argos/img/snipicons/arrow-down-l.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /argos/img/snipicons/arrow-left-l.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /argos/img/snipicons/arrow-right-l.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /argos/img/snipicons/arrow-up-l.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /argos/img/snipicons/asterisk.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /argos/img/snipicons/circle-arrow-down-l.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /argos/img/snipicons/circle-arrow-left-l.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /argos/img/snipicons/circle-arrow-right-l.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /argos/img/snipicons/circle-arrow-up-l.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /argos/img/snipicons/exclamation-sign.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /argos/img/snipicons/file-inverse.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /argos/img/snipicons/file.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /argos/img/snipicons/folder-close.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /argos/img/snipicons/folder-open.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /argos/img/snipicons/leaf.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /argos/img/snipicons/list.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /argos/img/snipicons/minus-l.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /argos/img/snipicons/minus-sign-l.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /argos/img/snipicons/move.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /argos/img/snipicons/plus-sign-l.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /argos/img/snipicons/remove-circle-l.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /argos/img/snipicons/remove-sign-l.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /argos/img/snipicons/reset-l.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | image/svg+xml 43 | 45 | 54 | 55 | 58 | 60 | 66 | 71 | 72 | -------------------------------------------------------------------------------- /argos/img/snipicons/th-large.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /argos/img/snipicons/th-list.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /argos/img/snipicons/transparent_1x1.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /argos/img/snipicons/warning-sign.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 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 | --------------------------------------------------------------------------------