diff --git a/.github/workflows/ci_workflows.yml b/.github/workflows/ci_workflows.yml index ae3ad8d04..9d1d04c42 100644 --- a/.github/workflows/ci_workflows.yml +++ b/.github/workflows/ci_workflows.yml @@ -23,97 +23,40 @@ jobs: uses: OpenAstronomy/github-actions-workflows/.github/workflows/tox.yml@v1 with: coverage: codecov - display: true - # Linux PyQt 5.15 and 6.x installations require apt-getting xcb and EGL deps - # and headless X11 display; as of Python 3.11 Scipy and h5py also need their own deps. - libraries: | - apt: - - '^libxcb.*-dev' - - libxkbcommon-x11-dev - - libegl1-mesa - - libopenblas-dev - - libhdf5-dev - brew: - - enchant envs: | - # Standard tests - # Linux builds - test on all supported PyQt5/6 and PySide2/6 versions, - # and include all dependencies in some builds - - linux: py38-test-pyqt514-all - - linux: py39-test-pyqt515 - - linux: py310-test-pyqt63-all - - linux: py310-test-pyqt64-all - - linux: py311-test-pyqt514 + # Linux builds + - linux: py38-test-lts-all + - linux: py39-test + - linux: py310-test-all + - linux: py311-test + - linux: py311-test-all # Documentation build - - linux: py38-docs-pyqt514 + - linux: py38-docs coverage: false - - macos: py311-docs-pyqt64 + - macos: py311-docs coverage: false # Test a few configurations on macOS - - macos: py38-test-pyqt514-all - - macos: py310-test-pyqt515 - - macos: py310-test-pyqt64 - - macos: py311-test-pyqt515 + - macos: py38-test-all + - macos: py39-test + - macos: py310-test-all + - macos: py311-test # Test some configurations on Windows - - windows: py38-test-pyqt514 - - windows: py310-test-pyqt63 + - windows: py38-test + - windows: py310-test # Test against latest developer versions of some packages - - linux: py310-test-pyqt515-dev-all - - linux: py311-test-pyqt64-dev - - allowed_failures: - needs: initial_checks - uses: OpenAstronomy/github-actions-workflows/.github/workflows/tox.yml@v1 - with: - coverage: codecov - display: true - libraries: | - apt: - - '^libxcb.*-dev' - - libxkbcommon-x11-dev - - libegl1-mesa - - libopenblas-dev - - libhdf5-dev - brew: - - enchant - - hdf5 - envs: | - - # Non-deterministic QThread exceptions - - linux: py38-test-pyside514 - - linux: py39-test-pyside515 - - linux: py310-test-pyside63 - - # PySide6 6.4 failures due to https://github.com/spyder-ide/qtpy/issues/373 - # and https://github.com/matplotlib/matplotlib/issues/24155 - # Python 3.11.0 failing on Windows in test_image.py on - # > assert df.find_factory(fname) is df.img_data - - linux: py310-test-pyside64 - - linux: py311-test-pyside64 - - macos: py310-test-pyside63 - - macos: py311-test-pyside64 - - windows: py310-test-pyside64 - - windows: py311-test-pyqt515 - - # Windows docs build - - windows: py310-docs-pyqt515 - coverage: false - - # Failure in test_close_tab - - windows: py310-test-pyqt515-all + - linux: py310-test-dev-all + - linux: py311-test-dev publish: needs: tests uses: OpenAstronomy/github-actions-workflows/.github/workflows/publish_pure_python.yml@v1 with: - # Setup PyQt5 deps and headless X server as per pyvista/setup-headless-display-action@v1 - libraries: '^libxcb.*-dev libxkbcommon-x11-dev libgl1-mesa-glx libopenblas-dev libhdf5-dev xvfb' - test_extras: 'test,qt' - test_command: Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 & sleep 3; DISPLAY=:99.0 pytest --pyargs glue + test_extras: 'test' + test_command: pytest --pyargs glue secrets: pypi_token: ${{ secrets.pypi_token }} diff --git a/.gitignore b/.gitignore index e797cbaa7..37c6fda52 100644 --- a/.gitignore +++ b/.gitignore @@ -20,7 +20,6 @@ glue/_githash.py # Other .pylintrc *.ropeproject -glue/qt/glue_qt_resources.py *.__junk* *.orig *~ diff --git a/.readthedocs.yml b/.readthedocs.yml index 10d4fd023..309f6d1bf 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -3,6 +3,11 @@ version: 2 build: image: latest +sphinx: + builder: html + configuration: doc/conf.py + fail_on_warning: true + python: version: 3.8 install: @@ -10,7 +15,6 @@ python: path: . extra_requirements: - docs - - qt - all formats: [] diff --git a/.test-docs.sh b/.test-docs.sh deleted file mode 100755 index 489054272..000000000 --- a/.test-docs.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/bash -x - -cd doc - -make spelling -make clean - -make html linkcheck 2> warnings.log - -cat warnings.log -grep -v "numpy.dtype size changed" warnings.log \ - | grep -v "numpy.ufunc size changed" \ - | grep -v "return f(\*args, \*\*kwds)" \ - > warnings_filt.log - -# make sure stderr was empty, i.e. no warnings -cat warnings_filt.log -test ! -s warnings_filt.log - - # Check for any broken links, ignore 'redirected with Found' -grep -v "redirected with Found" _build/linkcheck/output.txt > _build/linkcheck/output_no_found_redirect.txt - -# Make sure file is empty, i.e. no warnings/errors -cat _build/linkcheck/output_no_found_redirect.txt -test ! -s _build/linkcheck/output_no_found_redirect.txt diff --git a/CHANGES.md b/CHANGES.md index ca170e14a..1c8b12b22 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,42 @@ # Full changelog +## v1.13.1 - 2023-08-17 + + +### What's Changed + +#### Bug Fixes + +- Add back parsing functions to qglue temporarily by @astrofrog in https://github.com/glue-viz/glue/pull/2435 + +**Full Changelog**: https://github.com/glue-viz/glue/compare/v1.13.0...v1.13.1 + +## v1.13.0 - 2023-08-15 + + +### What's Changed + +#### New Features + +- Add support for defining custom image stretches by @astrofrog in https://github.com/glue-viz/glue/pull/2423 + +#### Bug Fixes + +- Set better starting xlim and ylim for custom viewers by @jfoster17 in https://github.com/glue-viz/glue/pull/2422 + +#### Documentation + +- Update standalone application instructions by @astrofrog in https://github.com/glue-viz/glue/pull/2426 + +#### Other Changes + +- Skip `test_save_aggregate_slice` for Numpy 1.25.x and unpin numpy-dev by @dhomeier in https://github.com/glue-viz/glue/pull/2429 +- Move frontend-independent data/link parsing code from glue.qglue to glue.core by @astrofrog in https://github.com/glue-viz/glue/pull/2432 +- Remove Qt-specific code being split into glue-qt by @astrofrog in https://github.com/glue-viz/glue/pull/2431 +- Added glue-qt dependency by @astrofrog in https://github.com/glue-viz/glue/pull/2433 + +**Full Changelog**: https://github.com/glue-viz/glue/compare/v1.12.0...v1.13.0 + ## v1.12.0 - 2023-06-16 @@ -646,58 +683,87 @@ ### What's Changed -- Improved how we handle equal aspect ratio to not depend on -- Matplotlib. https://github.com/glue-viz/glue/pull/1894 -- -- Avoid showing a warning when closing an empty tab. https://github.com/glue-viz/glue/pull/1890 -- -- Fix bug that caused component arithmetic to not work if -- Numpy was imported in user's config.py file. https://github.com/glue-viz/glue/pull/1887 +- Improved how we handle equal aspect ratio to not depend on + +- Matplotlib. https://github.com/glue-viz/glue/pull/1894 + - -- Added the ability to define custom layer artist makers to -- override default layer artists in viewers. https://github.com/glue-viz/glue/pull/1850 +- Avoid showing a warning when closing an empty tab. https://github.com/glue-viz/glue/pull/1890 + - -- Fix Plot.ly exporter for categorical components and histogram -- viewer. https://github.com/glue-viz/glue/pull/1886 +- Fix bug that caused component arithmetic to not work if + +- Numpy was imported in user's config.py file. https://github.com/glue-viz/glue/pull/1887 + - -- Fix issues with reading very large FITS files on some systems. https://github.com/glue-viz/glue/pull/1884 +- Added the ability to define custom layer artist makers to + +- override default layer artists in viewers. https://github.com/glue-viz/glue/pull/1850 + - -- Added documentation about plugins. https://github.com/glue-viz/glue/pull/1837 +- Fix Plot.ly exporter for categorical components and histogram + +- viewer. https://github.com/glue-viz/glue/pull/1886 + - -- Better isolate code related to pixel selection tool in image -- viewer that depended on Qt. https://github.com/glue-viz/glue/pull/1763 +- Fix issues with reading very large FITS files on some systems. https://github.com/glue-viz/glue/pull/1884 + - -- Improve handling of units in FITS files. https://github.com/glue-viz/glue/pull/1723 +- Added documentation about plugins. https://github.com/glue-viz/glue/pull/1837 + - -- Added documentation about creating viewers for glue using the -- new state-based infrastructure. https://github.com/glue-viz/glue/pull/1740 +- Better isolate code related to pixel selection tool in image + +- viewer that depended on Qt. https://github.com/glue-viz/glue/pull/1763 + - -- Make it possible to pass the initial state of a viewer to an -- application's `new_data_viewer` method. https://github.com/glue-viz/glue/pull/1877 +- Improve handling of units in FITS files. https://github.com/glue-viz/glue/pull/1723 + - -- Ensure that glue can be imported if QtPy is installed but PyQt -- and PySide aren't. [#1865, #1836] +- Added documentation about creating viewers for glue using the + +- new state-based infrastructure. https://github.com/glue-viz/glue/pull/1740 + - -- Fix unit display for coordinates from WCS headers that don't have -- CTYPE but have CUNIT. https://github.com/glue-viz/glue/pull/1856 +- Make it possible to pass the initial state of a viewer to an + +- application's `new_data_viewer` method. https://github.com/glue-viz/glue/pull/1877 + - -- Enable tab completion on Data objects. https://github.com/glue-viz/glue/pull/1874 +- Ensure that glue can be imported if QtPy is installed but PyQt + +- and PySide aren't. [#1865, #1836] + - -- Automatically select datasets in link editor if there are only two. https://github.com/glue-viz/glue/pull/1837 +- Fix unit display for coordinates from WCS headers that don't have + +- CTYPE but have CUNIT. https://github.com/glue-viz/glue/pull/1856 + - -- Change 'Export Session' dialog to offer to save with relative paths to data -- by default instead of absolute paths. https://github.com/glue-viz/glue/pull/1803 +- Enable tab completion on Data objects. https://github.com/glue-viz/glue/pull/1874 + - -- Added a new method `screenshot` on `GlueApplication` to save a -- screenshot of the current view. https://github.com/glue-viz/glue/pull/1808 +- Automatically select datasets in link editor if there are only two. https://github.com/glue-viz/glue/pull/1837 + - -- Show the active subset in the toolbar. https://github.com/glue-viz/glue/pull/1797 +- Change 'Export Session' dialog to offer to save with relative paths to data + +- by default instead of absolute paths. https://github.com/glue-viz/glue/pull/1803 + - -- Refactored the viewer class base classes https://github.com/glue-viz/glue/pull/1746: +- Added a new method `screenshot` on `GlueApplication` to save a + +- screenshot of the current view. https://github.com/glue-viz/glue/pull/1808 + - -- - `glue.core.application_base.ViewerBase` has been removed in favor of +- Show the active subset in the toolbar. https://github.com/glue-viz/glue/pull/1797 + - +- Refactored the viewer class base classes https://github.com/glue-viz/glue/pull/1746: + - +- - `glue.core.application_base.ViewerBase` has been removed in favor of + - - - @@ -706,9 +772,10 @@ - - - -- - `glue.viewers.common.viewer.BaseViewer` and - - +- - `glue.viewers.common.viewer.BaseViewer` and + - - - @@ -717,9 +784,10 @@ - - - -- - `glue.viewers.common.viewer.Viewer`. - - +- - `glue.viewers.common.viewer.Viewer`. + - - - @@ -728,9 +796,10 @@ - - - -- - - - +- - + - - - @@ -739,9 +808,10 @@ - - - -- - `glue.viewers.common.viewer.Viewer` is now where the base logic is defined - - +- - `glue.viewers.common.viewer.Viewer` is now where the base logic is defined + - - - @@ -750,9 +820,10 @@ - - - -- - for using state classes in viewers (instead of - - +- - for using state classes in viewers (instead of + - - - @@ -761,9 +832,10 @@ - - - -- - `glue.viewers.common.qt.DataViewerWithState`). - - +- - `glue.viewers.common.qt.DataViewerWithState`). + - - - @@ -772,9 +844,10 @@ - - - -- - - - +- - + - - - @@ -783,9 +856,10 @@ - - - -- - `glue.viewers.common.qt.DataViewerWithState` is now deprecated. - - +- - `glue.viewers.common.qt.DataViewerWithState` is now deprecated. + - - - @@ -794,9 +868,10 @@ - - - -- - - - +- - + - - - @@ -806,65 +881,109 @@ - - - -- Make it so that the modest image only resamples the data when the -- mouse is no longer pressed - this avoids too many refreshes when -- panning/zooming. https://github.com/glue-viz/glue/pull/1866 - -- Make it possible to unglue multiple links in one go. https://github.com/glue-viz/glue/pull/1809 - -- Make it so that adding a subset to a viewer no longer adds the -- associated data, since in some cases the viewer can handle the -- subset size, but not the full data. https://github.com/glue-viz/glue/pull/1807 +- Make it so that the modest image only resamples the data when the + +- mouse is no longer pressed - this avoids too many refreshes when + +- panning/zooming. https://github.com/glue-viz/glue/pull/1866 + - -- Defined a new abstract base class for all datasets, `BaseData`, -- and a base class `BaseCartesianData`, -- which can be used to implement interfaces to datasets that may be -- remote or may not be stored as regular cartesian data. https://github.com/glue-viz/glue/pull/1768 +- Make it possible to unglue multiple links in one go. https://github.com/glue-viz/glue/pull/1809 + - -- Add a new method `Data.compute_statistic` which can be used -- to find scalar and array statistics on the data, and use for -- the profile viewer and the state limits helpers. https://github.com/glue-viz/glue/pull/1737 +- Make it so that adding a subset to a viewer no longer adds the + +- associated data, since in some cases the viewer can handle the + +- subset size, but not the full data. https://github.com/glue-viz/glue/pull/1807 + - -- Add a new method `Data.compute_histogram` which can be used -- to find histograms of specific components, with or without -- subsets applied. https://github.com/glue-viz/glue/pull/1739 +- Defined a new abstract base class for all datasets, `BaseData`, + +- and a base class `BaseCartesianData`, + +- which can be used to implement interfaces to datasets that may be + +- remote or may not be stored as regular cartesian data. https://github.com/glue-viz/glue/pull/1768 + - -- Removed `Data.get_pixel_component_ids` and `Data.get_world_component_ids` -- in favor of `Data.pixel_component_ids` and `Data.world_component_ids`. -- https://github.com/glue-viz/glue/pull/1784 +- Add a new method `Data.compute_statistic` which can be used + +- to find scalar and array statistics on the data, and use for + +- the profile viewer and the state limits helpers. https://github.com/glue-viz/glue/pull/1737 + - -- Deprecated `Data.visible_components` and `Data.primary_components`. https://github.com/glue-viz/glue/pull/1788 +- Add a new method `Data.compute_histogram` which can be used + +- to find histograms of specific components, with or without + +- subsets applied. https://github.com/glue-viz/glue/pull/1739 + - -- Speed up histogram calculations by using the fast-histogram package instead of -- np.histogram. https://github.com/glue-viz/glue/pull/1806 +- Removed `Data.get_pixel_component_ids` and `Data.get_world_component_ids` + +- in favor of `Data.pixel_component_ids` and `Data.world_component_ids`. + +- https://github.com/glue-viz/glue/pull/1784 + - -- In the case of categorical attributes, `Data[name]` now returns a -- `categorical_ndarray` object rather than the indices of the categories. You -- can access the indices with `Data[name].codes` and the unique categories -- with `Data[name].categories`. https://github.com/glue-viz/glue/pull/1784 +- Deprecated `Data.visible_components` and `Data.primary_components`. https://github.com/glue-viz/glue/pull/1788 + - -- Compute profiles and histograms asynchronously when dataset is large -- to avoid holding up the UI, and compute profiles in chunks to avoid -- excessive memory usage. [#1736, #1764] +- Speed up histogram calculations by using the fast-histogram package instead of + +- np.histogram. https://github.com/glue-viz/glue/pull/1806 + - -- Improved naming of components when merging datasets. https://github.com/glue-viz/glue/pull/1249 +- In the case of categorical attributes, `Data[name]` now returns a + +- `categorical_ndarray` object rather than the indices of the categories. You + +- can access the indices with `Data[name].codes` and the unique categories + +- with `Data[name].categories`. https://github.com/glue-viz/glue/pull/1784 + - -- Fixed an issue that caused residual references to viewers -- after they were closed if they were accessed through the -- IPython console. https://github.com/glue-viz/glue/pull/1770 +- Compute profiles and histograms asynchronously when dataset is large + +- to avoid holding up the UI, and compute profiles in chunks to avoid + +- excessive memory usage. [#1736, #1764] + - -- Don't show layer edit options if layer is not visible. https://github.com/glue-viz/glue/pull/1805 +- Improved naming of components when merging datasets. https://github.com/glue-viz/glue/pull/1249 + - -- Make the Matplotlib viewer code that doesn't depend on Qt accessible -- to non-Qt frontends. https://github.com/glue-viz/glue/pull/1841 +- Fixed an issue that caused residual references to viewers + +- after they were closed if they were accessed through the + +- IPython console. https://github.com/glue-viz/glue/pull/1770 + - -- Avoid repeated coordinate components in merged datasets. https://github.com/glue-viz/glue/pull/1792 +- Don't show layer edit options if layer is not visible. https://github.com/glue-viz/glue/pull/1805 + - -- Fix bug that caused new subset to be created when dragging an existing -- subset in an image viewer. https://github.com/glue-viz/glue/pull/1793 +- Make the Matplotlib viewer code that doesn't depend on Qt accessible + +- to non-Qt frontends. https://github.com/glue-viz/glue/pull/1841 + - -- Better preserve data types when exporting data/subsets to FITS -- and HDF5 formats. https://github.com/glue-viz/glue/pull/1800 +- Avoid repeated coordinate components in merged datasets. https://github.com/glue-viz/glue/pull/1792 + +- +- Fix bug that caused new subset to be created when dragging an existing + +- subset in an image viewer. https://github.com/glue-viz/glue/pull/1793 + +- +- Better preserve data types when exporting data/subsets to FITS + +- and HDF5 formats. https://github.com/glue-viz/glue/pull/1800 + - ## [v0.13.4](https://github.com/glue-viz/glue/compare/v0.13.3...v0.13.4) - 2018-10-19 diff --git a/README.rst b/README.rst index bfad80bbf..a0a9d020d 100644 --- a/README.rst +++ b/README.rst @@ -4,10 +4,20 @@ Glue ==== Glue is a python project to link visualizations of scientific datasets -across many files. Click on the image for a quick demo: +across many files. |Glue demo| +This repository contains the **glue-core** package which includes much of the core +functionality of glue that is used for the different front-ends, including the Qt-based +application and the Jupyter-based application. Other key repositories include: + +* `glue-qt `_: the original Qt/desktop application for glue +* `glue-jupyter `_: a Jupyter front-end for glue + +In addition to these, there are a number of plugin packages available. For a full list of repositories, +see https://github.com/glue-viz/. + Features -------- @@ -57,5 +67,5 @@ License `__. :target: https://groups.google.com/forum/#!forum/glue-viz .. |Developer mailing list| image:: http://img.shields.io/badge/mailing%20list-development-green.svg?style=flat :target: https://groups.google.com/forum/#!forum/glue-viz-dev -.. |Glue demo| image:: https://raw.githubusercontent.com/glue-viz/glue/master/doc/readme.gif +.. |Glue demo| image:: https://raw.githubusercontent.com/glue-viz/glue-qt/main/doc/readme.gif :target: http://vimeo.com/53378575 diff --git a/doc/Makefile b/doc/Makefile index 689f28b26..d6418922f 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -2,7 +2,7 @@ # # You can set these variables from the command line. -SPHINXOPTS = +SPHINXOPTS = -j8 SPHINXBUILD = sphinx-build -n PAPER = BUILDDIR = _build diff --git a/doc/_static/default.css b/doc/_static/default.css deleted file mode 100644 index 21f3f5098..000000000 --- a/doc/_static/default.css +++ /dev/null @@ -1,256 +0,0 @@ -/* - * default.css_t - * ~~~~~~~~~~~~~ - * - * Sphinx stylesheet -- default theme. - * - * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS. - * :license: BSD, see LICENSE for details. - * - */ - -@import url("basic.css"); - -/* -- page layout ----------------------------------------------------------- */ - -body { - font-family: sans-serif; - font-size: 100%; - background-color: #11303d; - color: #000; - margin: 0; - padding: 0; -} - -div.document { - background-color: #1c4e63; -} - -div.documentwrapper { - float: left; - width: 100%; -} - -div.bodywrapper { - margin: 0 0 0 230px; -} - -div.body { - background-color: #ffffff; - color: #000000; - padding: 0 20px 30px 20px; -} - -div.footer { - color: #ffffff; - width: 100%; - padding: 9px 0 9px 0; - text-align: center; - font-size: 75%; -} - -div.footer a { - color: #ffffff; - text-decoration: underline; -} - -div.related { - background-color: #133f52; - line-height: 30px; - color: #ffffff; -} - -div.related a { - color: #ffffff; -} - -div.sphinxsidebar { -} - -div.sphinxsidebar h3 { - font-family: 'Trebuchet MS', sans-serif; - color: #ffffff; - font-size: 1.4em; - font-weight: normal; - margin: 0; - padding: 0; -} - -div.sphinxsidebar h3 a { - color: #ffffff; -} - -div.sphinxsidebar h4 { - font-family: 'Trebuchet MS', sans-serif; - color: #ffffff; - font-size: 1.3em; - font-weight: normal; - margin: 5px 0 0 0; - padding: 0; -} - -div.sphinxsidebar p { - color: #ffffff; -} - -div.sphinxsidebar p.topless { - margin: 5px 10px 10px 10px; -} - -div.sphinxsidebar ul { - margin: 10px; - padding: 0; - color: #ffffff; -} - -div.sphinxsidebar a { - color: #98dbcc; -} - -div.sphinxsidebar input { - border: 1px solid #98dbcc; - font-family: sans-serif; - font-size: 1em; -} - - - -/* -- hyperlink styles ------------------------------------------------------ */ - -a { - color: #355f7c; - text-decoration: none; -} - -a:visited { - color: #355f7c; - text-decoration: none; -} - -a:hover { - text-decoration: underline; -} - - - -/* -- body styles ----------------------------------------------------------- */ - -div.body h1, -div.body h2, -div.body h3, -div.body h4, -div.body h5, -div.body h6 { - font-family: 'Trebuchet MS', sans-serif; - background-color: #f2f2f2; - font-weight: normal; - color: #20435c; - border-bottom: 1px solid #ccc; - margin: 20px -20px 10px -20px; - padding: 3px 0 3px 10px; -} - -div.body h1 { margin-top: 0; font-size: 200%; } -div.body h2 { font-size: 160%; } -div.body h3 { font-size: 140%; } -div.body h4 { font-size: 120%; } -div.body h5 { font-size: 110%; } -div.body h6 { font-size: 100%; } - -a.headerlink { - color: #c60f0f; - font-size: 0.8em; - padding: 0 4px 0 4px; - text-decoration: none; -} - -a.headerlink:hover { - background-color: #c60f0f; - color: white; -} - -div.body p, div.body dd, div.body li { - text-align: justify; - line-height: 130%; -} - -div.admonition p.admonition-title + p { - display: inline; -} - -div.admonition p { - margin-bottom: 5px; -} - -div.admonition pre { - margin-bottom: 5px; -} - -div.admonition ul, div.admonition ol { - margin-bottom: 5px; -} - -div.note { - background-color: #eee; - border: 1px solid #ccc; -} - -div.seealso { - background-color: #ffc; - border: 1px solid #ff6; -} - -div.topic { - background-color: #eee; -} - -div.warning { - background-color: #ffe4e4; - border: 1px solid #f66; -} - -p.admonition-title { - display: inline; -} - -p.admonition-title:after { - content: ":"; -} - -pre { - padding: 5px; - background-color: #eeffcc; - color: #333333; - line-height: 120%; - border: 1px solid #ac9; - border-left: none; - border-right: none; -} - -tt { - background-color: #ecf0f3; - padding: 0 1px 0 1px; - font-size: 0.95em; -} - -th { - background-color: #ede; -} - -.warning tt { - background: #efc2c2; -} - -.note tt { - background: #d6d6d6; -} - -.viewcode-back { - font-family: sans-serif; -} - -div.viewcode-block:target { - background-color: #f4debf; - border-top: 1px solid #ac9; - border-bottom: 1px solid #ac9; -} \ No newline at end of file diff --git a/doc/_static/logo.png.tmp$$ b/doc/_static/logo.png.tmp$$ deleted file mode 100644 index 3e0ef374f..000000000 Binary files a/doc/_static/logo.png.tmp$$ and /dev/null differ diff --git a/doc/_templates/autosummary/base.rst b/doc/_templates/autosummary/base.rst deleted file mode 100644 index 5f65cb954..000000000 --- a/doc/_templates/autosummary/base.rst +++ /dev/null @@ -1 +0,0 @@ -{% extends "autosummary_core/base.rst" %} diff --git a/doc/_templates/autosummary/class.rst b/doc/_templates/autosummary/class.rst deleted file mode 100644 index 16e30f804..000000000 --- a/doc/_templates/autosummary/class.rst +++ /dev/null @@ -1 +0,0 @@ -{% extends "autosummary_core/class.rst" %} diff --git a/doc/_templates/autosummary/module.rst b/doc/_templates/autosummary/module.rst deleted file mode 100644 index ad9d7d728..000000000 --- a/doc/_templates/autosummary/module.rst +++ /dev/null @@ -1 +0,0 @@ -{% extends "autosummary_core/module.rst" %} diff --git a/doc/_templates/autosummary_core/base.rst b/doc/_templates/autosummary_core/base.rst deleted file mode 100644 index a58aa35ff..000000000 --- a/doc/_templates/autosummary_core/base.rst +++ /dev/null @@ -1,10 +0,0 @@ -{% if referencefile %} -.. include:: {{ referencefile }} -{% endif %} - -{{ objname }} -{{ underline }} - -.. currentmodule:: {{ module }} - -.. auto{{ objtype }}:: {{ objname }} diff --git a/doc/_templates/autosummary_core/class.rst b/doc/_templates/autosummary_core/class.rst deleted file mode 100644 index 85105fa8f..000000000 --- a/doc/_templates/autosummary_core/class.rst +++ /dev/null @@ -1,65 +0,0 @@ -{% if referencefile %} -.. include:: {{ referencefile }} -{% endif %} - -{{ objname }} -{{ underline }} - -.. currentmodule:: {{ module }} - -.. autoclass:: {{ objname }} - :show-inheritance: - - {% if '__init__' in methods %} - {% set caught_result = methods.remove('__init__') %} - {% endif %} - - {% block attributes_summary %} - {% if attributes %} - - .. rubric:: Attributes Summary - - .. autosummary:: - {% for item in attributes %} - ~{{ name }}.{{ item }} - {%- endfor %} - - {% endif %} - {% endblock %} - - {% block methods_summary %} - {% if methods %} - - .. rubric:: Methods Summary - - .. autosummary:: - {% for item in methods %} - ~{{ name }}.{{ item }} - {%- endfor %} - - {% endif %} - {% endblock %} - - {% block attributes_documentation %} - {% if attributes %} - - .. rubric:: Attributes Documentation - - {% for item in attributes %} - .. autoattribute:: {{ item }} - {%- endfor %} - - {% endif %} - {% endblock %} - - {% block methods_documentation %} - {% if methods %} - - .. rubric:: Methods Documentation - - {% for item in methods %} - .. automethod:: {{ item }} - {%- endfor %} - - {% endif %} - {% endblock %} diff --git a/doc/_templates/autosummary_core/module.rst b/doc/_templates/autosummary_core/module.rst deleted file mode 100644 index 11208a25c..000000000 --- a/doc/_templates/autosummary_core/module.rst +++ /dev/null @@ -1,41 +0,0 @@ -{% if referencefile %} -.. include:: {{ referencefile }} -{% endif %} - -{{ objname }} -{{ underline }} - -.. automodule:: {{ fullname }} - - {% block functions %} - {% if functions %} - .. rubric:: Functions - - .. autosummary:: - {% for item in functions %} - {{ item }} - {%- endfor %} - {% endif %} - {% endblock %} - - {% block classes %} - {% if classes %} - .. rubric:: Classes - - .. autosummary:: - {% for item in classes %} - {{ item }} - {%- endfor %} - {% endif %} - {% endblock %} - - {% block exceptions %} - {% if exceptions %} - .. rubric:: Exceptions - - .. autosummary:: - {% for item in exceptions %} - {{ item }} - {%- endfor %} - {% endif %} - {% endblock %} diff --git a/doc/_templates/layout.html b/doc/_templates/layout.html deleted file mode 100644 index 631e09e4f..000000000 --- a/doc/_templates/layout.html +++ /dev/null @@ -1,13 +0,0 @@ -{% extends "!layout.html" %} - -{% block footer %} -{{ super() }} - -{% endblock %} \ No newline at end of file diff --git a/doc/developer_guide/api.rst b/doc/api.rst similarity index 73% rename from doc/developer_guide/api.rst rename to doc/api.rst index 252c71fb6..8ea4d3658 100644 --- a/doc/developer_guide/api.rst +++ b/doc/api.rst @@ -59,22 +59,12 @@ User Interface .. automodapi:: glue.viewers.common.viewer :no-inheritance-diagram: -.. automodapi:: glue.viewers.common.qt.data_viewer - :no-inheritance-diagram: - -.. automodapi:: glue.viewers.common.qt.base_widget - :no-inheritance-diagram: - :skip: BaseQtViewerWidget - .. automodapi:: glue.viewers.matplotlib.state :no-inheritance-diagram: .. automodapi:: glue.viewers.matplotlib.layer_artist :no-inheritance-diagram: -.. automodapi:: glue.viewers.matplotlib.qt.data_viewer - :no-inheritance-diagram: - .. automodapi:: glue.core.layer_artist :no-inheritance-diagram: @@ -98,15 +88,6 @@ User Interface :no-inheritance-diagram: :inherited-members: -.. automodapi:: glue.viewers.matplotlib.qt.toolbar_mode - :no-inheritance-diagram: - :inherited-members: - -.. automodapi:: glue.viewers.common.qt.toolbar - :no-inheritance-diagram: - :inherited-members: - :skip: BasicToolbar - .. automodapi:: glue.viewers.matplotlib.mpl_axes :no-inheritance-diagram: @@ -114,10 +95,6 @@ User Interface :no-inheritance-diagram: :inherited-members: -.. automodapi:: glue.viewers.matplotlib.qt.toolbar - :no-inheritance-diagram: - :inherited-members: - .. automodapi:: glue.viewers.scatter.viewer :no-inheritance-diagram: :inherited-members: @@ -126,9 +103,6 @@ User Interface :no-inheritance-diagram: :inherited-members: -.. automodapi:: glue.viewers.scatter.qt - :no-inheritance-diagram: - .. automodapi:: glue.viewers.profile.viewer :no-inheritance-diagram: :inherited-members: @@ -137,9 +111,6 @@ User Interface :no-inheritance-diagram: :inherited-members: -.. automodapi:: glue.viewers.profile.qt - :no-inheritance-diagram: - .. automodapi:: glue.viewers.image.viewer :no-inheritance-diagram: :inherited-members: @@ -148,10 +119,6 @@ User Interface :no-inheritance-diagram: :inherited-members: -.. automodapi:: glue.viewers.image.qt - :no-inheritance-diagram: - :skip: StandaloneImageViewer - .. automodapi:: glue.viewers.histogram.viewer :no-inheritance-diagram: @@ -159,21 +126,9 @@ User Interface :no-inheritance-diagram: :inherited-members: -.. automodapi:: glue.viewers.histogram.qt - :no-inheritance-diagram: - -.. automodapi:: glue.viewers.table.qt - :no-inheritance-diagram: - -.. automodapi:: glue.viewers.custom.qt.custom_viewer - :no-inheritance-diagram: - .. automodapi:: glue.core.application_base :no-inheritance-diagram: -.. automodapi:: glue.app.qt.application - :no-inheritance-diagram: - .. automodapi:: glue :no-inheritance-diagram: @@ -210,14 +165,6 @@ Utilities .. automodapi:: glue.utils.misc :no-inheritance-diagram: -.. automodapi:: glue.utils.qt - :no-inheritance-diagram: - :skip: QColorBox - :skip: Worker - -.. automodapi:: glue.utils.qt.widget_properties - :no-inheritance-diagram: - Plugins ======= diff --git a/doc/conf.py b/doc/conf.py index 8d8585599..bfac95830 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -1,169 +1,63 @@ -# -*- coding: utf-8 -*- -# -# Glue documentation build configuration file - -import os -import sys -from pkg_resources import get_distribution - -# -- General configuration ---------------------------------------------------- - -# If your documentation needs a minimal Sphinx version, state it here. -needs_sphinx = '1.6' - -# Add any Sphinx extension module names here, as strings. They can be extensions -# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = ['sphinx.ext.autodoc', - 'sphinx.ext.todo', - 'sphinx.ext.coverage', - 'sphinx.ext.mathjax', - 'sphinx.ext.viewcode', - 'sphinx.ext.intersphinx', - 'numpydoc', - 'sphinx_automodapi.automodapi', - 'sphinx_automodapi.smart_resolver', - 'sphinxcontrib.spelling'] - -# Add the redirect.py plugin which is in this directory -sys.path.insert(0, os.path.abspath('.')) -extensions.append('redirect') - -# Workaround for RTD where the default encoding is ASCII -if os.environ.get('READTHEDOCS') == 'True': - import locale - locale.setlocale(locale.LC_ALL, 'C.UTF-8') - -intersphinx_cache_limit = 10 # days to keep the cached inventories -intersphinx_mapping = { - 'python': ('https://docs.python.org/3.7', None), - 'matplotlib': ('https://matplotlib.org', None), - 'numpy': ('https://numpy.org/doc/stable/', None), - 'astropy': ('https://docs.astropy.org/en/stable/', None), - 'echo': ('https://echo.readthedocs.io/en/latest/', None), - 'pandas': ('https://pandas.pydata.org/pandas-docs/stable/', None), - 'PyQt5': ('https://www.riverbankcomputing.com/static/Docs/PyQt5/', None), -} - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -# The suffix of source filenames. -source_suffix = '.rst' - -# The master toctree document. -master_doc = 'index' - -# General information about the project. -project = u'Glue' -copyright = u'2012-2019, Chris Beaumont, Thomas Robitaille, Michelle Borkin' - -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -version = release = get_distribution('glue-core').version - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -exclude_patterns = ['_build', '_templates', '.eggs'] - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' - -# -- Options for HTML output -------------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -try: # use ReadTheDocs theme, if installed - import sphinx_rtd_theme - html_theme = 'sphinx_rtd_theme' - html_theme_path = [sphinx_rtd_theme.get_html_theme_path(), ] -except ImportError: - pass - -# The name of an image file (relative to this directory) to place at the top -# of the sidebar. -html_logo = '_static/logo.png' - -# The name of an image file (within the static path) to use as favicon of the -# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 -# pixels large. -# html_favicon = None - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] - -# Output file base name for HTML help builder. -htmlhelp_basename = 'Gluedoc' - -# -- Options for LaTeX output ------------------------------------------------- - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, author, documentclass [howto/manual]). -latex_documents = [ - ('index', 'Glue.tex', u'Glue Documentation', - u'Chris Beaumont, Thomas Robitaille, Michelle Borkin', 'manual'), +# Configuration file for the Sphinx documentation builder. + +# -- Project information ----------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information + +project = "Glue" +copyright = "2012-2023, Chris Beaumont, Thomas Robitaille, Michelle Borkin" +author = "Chris Beaumont, Thomas Robitaille, Michelle Borkin" + +# -- General configuration --------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration + +extensions = [ + "sphinx.ext.autodoc", + "sphinx.ext.todo", + "sphinx.ext.coverage", + "sphinx.ext.mathjax", + "sphinx.ext.viewcode", + "sphinx.ext.intersphinx", + "numpydoc", + "sphinx_automodapi.automodapi", + "sphinx_automodapi.smart_resolver", ] -# -- Options for manual page output ------------------------------------------- +templates_path = ["_templates"] +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] -# One entry per manual page. List of tuples -# (source start file, name, description, authors, manual section). -man_pages = [ - ('index', 'glue', u'Glue Documentation', - [u'Chris Beaumont, Thomas Robitaille, Michelle Borkin'], 1) -] - -# -- Options for Texinfo output ----------------------------------------------- +autoclass_content = "both" -# Grouping the document tree into Texinfo files. List of tuples -# (source start file, target name, title, author, -# dir menu entry, description, category) -texinfo_documents = [ - ('index', 'Glue', u'Glue Documentation', - u'Chris Beaumont, Thomas Robitaille, Michelle Borkin', - 'Glue', 'One line description of project.', 'Miscellaneous'), +nitpick_ignore = [ + ("py:class", "glue.viewers.histogram.layer_artist.HistogramLayerBase"), + ("py:class", "glue.viewers.scatter.layer_artist.ScatterLayerBase"), + ("py:class", "glue.viewers.image.layer_artist.ImageLayerBase"), + ("py:class", "glue.viewers.image.layer_artist.RGBImageLayerBase"), + ("py:class", "glue.viewers.image.state.BaseImageLayerState"), ] -# -- Additional options------- ------------------------------------------------ - -todo_include_todos = True -autoclass_content = 'both' - -nitpick_ignore = [('py:obj', 'glue.viewers.common.qt.toolbar.BasicToolbar.insertAction'), - ('py:obj', 'glue.viewers.common.qt.toolbar.BasicToolbar.setTabOrder'), - ('py:class', 'glue.viewers.histogram.layer_artist.HistogramLayerBase'), - ('py:class', 'glue.viewers.scatter.layer_artist.ScatterLayerBase'), - ('py:class', 'glue.viewers.image.layer_artist.ImageLayerBase'), - ('py:class', 'glue.viewers.image.layer_artist.RGBImageLayerBase'), - ('py:class', 'glue.viewers.image.state.BaseImageLayerState'), - ('py:class', 'glue.viewers.common.qt.toolbar.BasicToolbar'), - ('py:class', 'glue.viewers.common.qt.base_widget.BaseQtViewerWidget'), - ('py:class', 'sip.voidptr'), - ('py:class', 'PyQt5.sip.voidptr'), - ('py:class', 'PYQT_SLOT')] - -nitpick_ignore_regex = [('py:class', r'PyQt5\.QtCore\.Q[A-Z][a-zA-Z]+'), - ('py:class', r'PyQt5\.QtWidgets\.Q[A-Z][a-zA-Z]+'), - ('py:class', r'PyQt6\.QtCore\.Q[A-Z][a-zA-Z]+'), - ('py:class', r'PyQt6\.QtWidgets\.Q[A-Z][a-zA-Z]+'), - ('py:class', r'Qt\.[A-Z][a-zA-Z]+'), - ('py:class', r'QPalette\.[A-Z][a-zA-Z]+'), - ('py:class', r'QWidget\.[A-Z][a-zA-Z]+'), - ('py:class', r'Q[A-Z][a-zA-Z]+')] - -# coax Sphinx into treating descriptors as attributes -# see https://bitbucket.org/birkenfeld/sphinx/issue/1254/#comment-7587063 -from glue.utils.qt.widget_properties import WidgetProperty -WidgetProperty.__get__ = lambda self, *args, **kwargs: self - viewcode_follow_imported_members = False numpydoc_show_class_members = False autosummary_generate = True -automodapi_toctreedirnm = 'api' +automodapi_toctreedirnm = "api" -linkcheck_ignore = [r'https://s3.amazonaws.com'] +linkcheck_ignore = [r"https://s3.amazonaws.com"] linkcheck_retries = 5 linkcheck_timeout = 10 + + +intersphinx_mapping = { + "python": ("https://docs.python.org/3.7", None), + "matplotlib": ("https://matplotlib.org", None), + "numpy": ("https://numpy.org/doc/stable/", None), + "astropy": ("https://docs.astropy.org/en/stable/", None), + "echo": ("https://echo.readthedocs.io/en/latest/", None), + "pandas": ("https://pandas.pydata.org/pandas-docs/stable/", None), +} + +# -- Options for HTML output ------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output + +html_theme = "sphinx_book_theme" +html_static_path = ["_static"] +html_logo = "_static/logo.png" diff --git a/doc/customizing_guide/available_plugins.rst b/doc/customizing_guide/available_plugins.rst deleted file mode 100644 index 8bc38f241..000000000 --- a/doc/customizing_guide/available_plugins.rst +++ /dev/null @@ -1,222 +0,0 @@ -.. _available_plugins: - -List of available plugins -========================= - -This page lists available plugin packages, as well as information on installing -these. If you are interested in writing your own plugin package, see -:ref:`writing_plugin`. We rely on domain experts to help us develop plugins, so -if your domain is not represented below, or if you want to help us extend the -existing plugins, we'd love to hear from you! - -General plugins ---------------- - -glue-vispy-viewers: 3d viewers -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The **glue-vispy-viewers** plugin package adds a 3D scatter plot viewer and a 3D -volume rendering viewer to glue. This plugin package is installed by default -with glue (provided that you installed the **glueviz** package with pip or -conda). You can read up more about the functionality available in this plugin -in :ref:`viewers-3d`. You can check that you have this plugin installed by going -to the **Canvas** menu in glue and selecting **New Data Viewer**, or -alternatively by dragging a dataset onto the canvas area. If the 3D viewers -plugin is installed, you should see the 3D viewers in the list: - -.. image:: ../gui_guide/images/3d_viewers_select.png - :align: center - :width: 339 - -If you don't see these in the list, then, you can install the 3D viewers plugin -using:: - - conda install -c glueviz glue-vispy-viewers - -or if you don't use conda:: - - pip install glue-vispy-viewers - -If you run into issues or have requests related to this plugin, or if you would -like to contribute to the development, the GitHub repository for this plugin is -at https://github.com/glue-viz/glue-vispy-viewers. - -glue-jupyter: Jupyter notebook/lab viewers -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -We are currently developing data viewers for the Jupyter notebook and Jupyter -lab - this is not quite ready yet for general use, but if you are interested -in following on or helping with the development, the GitHub repository for this -plugin is at https://github.com/glue-viz/glue-jupyter. - -glue-plotly: plotly exporter for glue -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -We are currently developing a plugin to make it easy to export plots made in -glue to standalone interactive figures using `plotly `_. If -you are interested in trying this out, you can install it with:: - - conda install -c glueviz glue-plotly - -or if you don't use conda:: - - pip install glue-plotly - -Once installed, you will have the ability to export some of the viewers to -HTML pages powered by plotly by selecting **Save Plotly HTML page** from the -dropdown menu for the save icon: - -.. image:: images/plugin_plotly.png - :align: center - :width: 350px - -Note that at this time, not all viewers have a plotly exporter available. - -If you run into issues or have requests related to this plugin, or if you would -like to contribute to the development, the GitHub repository for this plugin is -at https://github.com/glue-viz/glue-plotly. - -Plugin for Medical imaging --------------------------- - -The **glue-medical** is a plugin under development to provide functionality -related to medical imaging in glue. At the moment, the plugin only includes a -`DICOM `_ reader, but we want to expand -this to other formats and as well as other functionality (e.g. colormaps) that -would be useful in medical imaging. You can install the current version of this -plugin with:: - - conda install -c glueviz glue-medical - -or if you don't use conda:: - - pip install glue-medical - -Once the plugin is installed, you should be able to read in DICOM files as you -would normally read other files. You can also read in DICOM directories -containing individual slices into a single dataset by going to the **File** -menu, then **Import data**, and **Import DICOM directory as a single array**. - -If you run into issues or have requests related to this plugin, or if you would -like to contribute to the development, the GitHub repository for this plugin is -at https://github.com/glue-viz/glue-medical. - -Plugin for Geosciences ----------------------- - -The **glue-geospatial** is a plugin under development to provide functionality -related to geospatial data in glue. At the moment, the plugin includes a -reader based on the `rasterio `_ package, -which allows e.g. GeoTIFF files to be loaded into glue. In addition, the glue -plugin tries to automatically set up the coordinate system for the datasets so -that you can link datasets based on longitude/latitude. You can install the -current version of this plugin with:: - - conda install -c glueviz glue-geospatial - -or if you don't use conda:: - - pip install glue-geospatial - -Once the plugin is installed, you should be able to read in e.g. GeoTIFF files -as you would normally read other files. - -If you run into issues or have requests related to this plugin, or if you would -like to contribute to the development, the GitHub repository for this plugin is -at https://github.com/glue-viz/glue-geospatial. - -Plugins for Astronomy ---------------------- - -For historical reasons, the core glue application already includes some -Astronomy-specific functionality, such as data readers for e.g. FITS and other -common astronomy formats, as well as linking functions that are aware of -Astronomy coordinate systems. In addition to this built-in functionality, there -are a number of plugins available and/or in development for Astronomy. - -glue-wwt: WorldWide Telescope viewer -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The **glue-wwt** adds a data viewer to glue that allows users to overplot data -onto maps of the sky, powered by `WorldWide Telescope -`_. You can install this plugin with:: - - conda install -c glueviz glue-wwt - -or if you don't use conda:: - - pip install glue-wwt - -Once the plugin is installed, you should see a new viewer named -**WorldWideTelescope (WWT)** in the list of available viewers when dragging a -dataset onto the main canvas in the glue application. Once you have added a -dataset to the viewer, you can select in the viewer options the columns that -give the Right Ascension and Declination of the data points (we will add support -for other coordinate systems in future). At the moment, only tables can be -shown using markers in WWT (and not images) and we don't recommend adding large -sets of points at this time (due to limitations in the way WWT deals with -annotations). - -If you run into issues or have requests related to this plugin, or if you would -like to contribute to the development, the GitHub repository for this plugin is -at https://github.com/glue-viz/glue-wwt. - -glue-aladin: Aladin Lite viewer -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -A data viewer similar to glue-wwt but for `Aladin Lite -`_, is being developed and is not quite -ready yet for general use, but if you are interested in following on or helping -with the development, the GitHub repository is at -https://github.com/glue-viz/glue-aladin. - -glue-samp: Communicating with SAMP -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -A few common applications in astronomy support communicating via the Simple -Application Messaging Protocol (SAMP) - these include for example `DS9 -`_, `TOPCAT -`_, and `Aladin -`_. The **glue-samp** plugin adds the ability to -use SAMP from glue. You can install this plugin with:: - - conda install -c glueviz glue-samp - -or if you don't use conda:: - - pip install glue-samp - -Once the plugin is installed, you can go to the **Plugins** menu and select -**Open SAMP plugin**: - -.. image:: images/samp_open.png - :align: center - :width: 250px - -A window will then appear: - -.. image:: images/samp_window.png - :align: center - :width: 500px - -Click on **Start SAMP**, and the status should change to something like -**Connected to SAMP Hub**. If you open another SAMP-enabled application such as -TOPCAT, you should now be able to send data from/to glue. To send data from glue -to another application, you can right-click (control-click on Mac) on a dataset -or subset in the glue data collection, then go to **SAMP**, then e.g. **Send to -topcat**: - -.. image:: images/samp_contextual.png - :align: center - :width: 600px - -This can be done for tables or images, and both for the main datasets and -subsets. However, note that not all SAMP-enabled application are able to -understand all types of SAMP messages. For example, while you can send images to -DS9, you will not be able to send them to TOPCAT. Conversely, DS9 may not -understand the concept of a subset. - -You can also send data from other applications to glue - for more information on -doing this, see the guide for the relevant application you want to use - glue -understands messages adding images and tables, as well as messages related to -subsets. diff --git a/doc/customizing_guide/configuration.rst b/doc/customizing_guide/configuration.rst deleted file mode 100644 index c7a4dc938..000000000 --- a/doc/customizing_guide/configuration.rst +++ /dev/null @@ -1,46 +0,0 @@ -.. _configuration: - -Configuring Glue via a startup file -=================================== - -Glue uses a configuration system to customize aspects such as which data viewers -it loads, what link functions to use, and so on. The glue configuration file is -called ``config.py``. Glue looks for this file in the following locations, in -order: - - * The current working directory - * The path specified in the ``GLUERC`` environment variable, if present - * The path ``.glue/config.py`` within the user's home directory - -The easiest place to start is to just create a ``config.py`` file in the current -directory where you are launching glue from. If you want to use your -customizations to be available for all projects, you can then consider using the -``GLUERC`` or ``.glue`` approach. - -Example Usage: Custom Link Functions ------------------------------------- - -As an example, let's create some translation functions which will allow us to -convert temperatures in Celsius to Farenheit:: - - from glue.config import link_function - - @link_function(info="Celsius to Fahrenheit", output_labels=['F']) - def celsius2farhenheit(c): - return c * 9. / 5. + 32 - - @link_function(info="Fahrenheit to Celsius", output_labels=['C']) - def farhenheit2celsius(f): - return (f - 32) * 5. / 9. - -More details about this are provided in :ref:`customization`, but for now, let's -just assume this is how we make custom linking functions. We can copy this code -into an empty ``config.py`` file in the current directory. Next time we start up -Glue, the link functions now appear in the Link Dialog: - -.. image:: images/link_functions.png - :align: center - :width: 300px - -Let's now take a look at all the available customizations in -:ref:`customization`. diff --git a/doc/customizing_guide/coordinates.rst b/doc/customizing_guide/coordinates.rst deleted file mode 100644 index ba3993ad7..000000000 --- a/doc/customizing_guide/coordinates.rst +++ /dev/null @@ -1,167 +0,0 @@ -.. _coordinates: - -Customizing the coordinate system of a data object -================================================== - -Background ----------- - -Data objects represented by the :class:`~glue.core.data.Data` class can have -a coordinate system defined, for display and/or linking purposes. This -coordinate system is defined in the ``.coords`` attribute of data objects. -By default the ``coords`` object for :class:`~glue.core.data.Data` objects -created manually is :obj:`None` unless you explicitly specify ``coords=`` when -creating the data object. For data objects returned by data loaders, whether -``coords`` is set or not will depend on the particular file format. For example -if we use the glue loaders to read in an example file: - - >>> from glue.core.data_factories import load_data - >>> from glue.utils import require_data - >>> require_data('Astronomy/W5/w5.fits') - Successfully downloaded data file to w5.fits - >>> data = load_data('w5.fits') - -the resulting ``coords`` object has methods to convert between pixel coordinates -and so-called 'world' or 'physical' coordinates: - - >>> data.coords.pixel_to_world_values(2, 3) # doctest: +FLOAT_CMP - (array(46.34527244), array(58.85867558)) - >>> data.coords.world_to_pixel_values(46.3, 58.9) # doctest: +FLOAT_CMP - (array(10.39880029), array(16.44193896)) - -If not :obj:`None`, the ``coords`` attribute will be an object exposing the two -above methods as well as other useful methods and properties related to -coordinate transformations. The programmatic interface we have adopted for -``coords`` objects is described in `A shared Python interface for World Coordinate Systems -`_ (while originally -defined by the Astropy project, this is very general and not astronomy-specific). -Any object implementing that API can be used as a coordinate object and will -integrate with the rest of glue. - -A number of convenience coordinate classes are available in glue for common -cases, and it is also possible to define your own (both options are described in -the next sections). - -.. _affine-coordinates: - -Affine coordinates ------------------- - -The most common cases of transformation between pixel and world coordinates are -`affine transformations `_, -which can represent combinations of e.g. reflections, scaling, translations, -rotations, and shear. A common way of representing an affine transformations is -through an `augmented -matrix `_, which has shape -N+1 x N+1, where N is the number of pixel and world dimensions. - -Glue provides an :class:`~glue.core.coordinates.AffineCoordinates` class for -representing arbitrary affine transformations:: - - >>> from glue.core.coordinates import AffineCoordinates - -To initialize it, you will need to provide an augmented matrix, and optionally -lists of units and axis names (as strings). For example, to construct an affine -transformation where the x and y coordinates are each doubled, you would do:: - - >>> import numpy as np - >>> matrix = np.array([[2, 0, 0], [0, 2, 0], [0, 0, 1]]) - >>> affine_coords = AffineCoordinates(matrix, units=['m', 'm'], labels=['xw', 'yw']) - -To use a custom coordinate system, when creating a data object you should specify -the coordinates object via the ``coords=`` keyword argument:: - - >>> from glue.core import Data - >>> data_double = Data(x=[[1, 2], [3, 4]], coords=affine_coords) - >>> data_double.coords.pixel_to_world_values(2, 1) - (4.0, 2.0) - >>> data_double.coords.world_to_pixel_values(4.0, 2.0) - (2.0, 1.0) - -Identity coordinates --------------------- - -A special/simple case of coordinate transformation is the identity transform, -where world coordinates are the same as pixel coordinates. Glue provides an -:class:`~glue.core.coordinates.IdentityCoordinates` class for representing -this transformation:: - - >>> from glue.core.coordinates import IdentityCoordinates - -To initialize it, you will need to specify the number of dimensions in the -data:: - - >>> data_ident = Data(x=[1, 2, 3], coords=IdentityCoordinates(n_dim=1)) - >>> data_ident.coords.pixel_to_world_values(2, 1) - (2, 1) - >>> data_ident.coords.world_to_pixel_values(4.0, 2.0) - (4.0, 2.0) - -Custom coordinates ------------------- - -If you want to define a fully customized coordinate transformation, we -provide a :class:`~glue.core.coordinates.Coordinates` class that you can -start from to make things easier. The only required methods in this case -are the following:: - - from glue.core.coordinates import Coordinates - - - class MyCoordinates(Coordinates): - - def pixel_to_world_values(self, *args): - # This should take N arguments (where N is the number of dimensions - # in your dataset) and assume these are 0-based pixel coordinates, - # then return N world coordinates with the same shape as the input. - - def world_to_pixel_values(self, *args): - # This should take N arguments (where N is the number of dimensions - # in your dataset) and assume these are 0-based pixel coordinates, - # then return N world coordinates with the same shape as the input. - -In addition, you can also optionally specify units and names for all world -coordinates with the two following properties:: - - @property - def world_axis_units(self): - # Returns an iterable of strings given the units of the world - # coordinates for each axis. - - @property - def world_axis_names(self): - # Returns an iterable of strings given the names of the world - # coordinates for each axis. - -For example, let's consider a coordinate system where the world coordinates are -simply scaled by a factor of two compared to the pixel coordinates. The minimal -class implementing this would look like:: - - >>> from glue.core.coordinates import Coordinates - - >>> class DoubleCoordinates(Coordinates): - ... - ... def pixel_to_world_values(self, *args): - ... return tuple([2.0 * x for x in args]) - ... - ... def world_to_pixel_values(self, *args): - ... return tuple([0.5 * x for x in args]) - -To use a custom coordinate system, when creating a data object you should specify -the coordinates object via the ``coords=`` keyword argument:: - - >>> data_double = Data(x=[1, 2, 3], coords=DoubleCoordinates(n_dim=1)) - >>> data_double.coords.pixel_to_world_values(2) - (4.0,) - >>> data_double.coords.world_to_pixel_values(4.0) - (2.0,) - -Note that the ``n_dim=`` argument needs to be passed to give the number of -dimensions in the data. - -In fact you do not need to start from our :class:`~glue.core.coordinates.Coordinates` -class - any class that conforms to the API described in -`A shared Python interface for World Coordinate Systems -`_ is valid. If -you want full control over your coordinate transformations, we recomment you -take a look at that document. diff --git a/doc/customizing_guide/custom_viewer.rst b/doc/customizing_guide/custom_viewer.rst deleted file mode 100644 index b42990b88..000000000 --- a/doc/customizing_guide/custom_viewer.rst +++ /dev/null @@ -1,241 +0,0 @@ -.. _simple-custom-viewer: - -Writing a simple custom data viewer -=================================== - -.. figure:: images/bball_3.png - :align: center - -Glue's standard data viewers (scatter plots, images, histograms) are useful in a -wide variety of data exploration settings. However, they represent a *tiny* -fraction of the ways to view a particular dataset. For this reason, Glue -provides a way to create more data viewers that me better suited to what you -need. - -There are several ways to do this - the tutorial on this page shows the easiest -way for users to develop a new custom visualization, provided that it can be -made using Matplotlib and that you don't want do have to do any GUI programming. -If you are interested in building more advanced custom viewers, see -:ref:`state-qt-viewer`. - -The Goal: Basketball Shot Charts --------------------------------- - -In basketball, Shot Charts show the spatial distribution of shots -for a particular player, team, or game. The `New York Times `_ has a nice example. - -There are three basic features that we might want to incorporate into -a shot chart: - - * The distribution of shots (or some statistic like the success rate), shown as a heatmap in the background. - * The locations of a particular subset of shots, perhaps plotted as - points in the foreground - * The relevant court markings, like the 3-point line and hoop location. - -We'll build a Shot Chart in Glue incrementally, starting with the simplest -code that runs. - - -Shot Chart Version 1: Heatmap and plot --------------------------------------- - -Our first attempt at a shot chart will draw the heatmap of all shots, -and overplot shot subsets as points. Here's the code: - -.. literalinclude:: scripts/bball_viewer_1.py - :linenos: - -Before looking at the code itself, let's look at how it's used. If you -include or import this code in your :ref:`config.py ` file, Glue will recognize the new viewer. -Open `this shot catalog `_, and create a new -shot chart with it. You'll get something that looks like this: - -.. figure:: images/bball_1.png - :align: center - -Furthermore, subsets that we define (e.g., by selecting regions of a -histogram) are shown as points (notice that Tim Duncan's shots are concentrated closer to the hoop). - -.. figure:: images/bball_2.png - :align: center - -Let's look at what the code does. Line 5 creates a new custom viewer, -and gives it the name ``Shot Plot``. It also specifies ``x`` and ``y`` keywords which we'll come back to shortly (spoiler: they tell Glue to -pass data attributes named ``x`` and ``y`` to ``show_hexbin``). - -Line 11 defines a ``show_hexbin`` function, that visualizes a dataset -as a heatmap. Furthermore, the decorator on line 10 registers this -function as the ``plot_data`` function, responsible for visualizing a dataset as a whole. - -Custom functions like ``show_hexbin`` can accept a variety of input -arguments, depending on what they need to do. Glue looks at the names -of the inputs to decide what data to pass along. In the case of this -function: - - - Arguments named ``axes`` contain the Matplotlib Axes object to draw with - - ``x`` and ``y`` were provided as keywords to ``custom_viewer``. They - contain the data (as arrays) corresponding to the attributes labeled - ``x`` and ``y`` in the catalog - -The function body itself is pretty simple -- we just use the -``x`` and ``y`` data to build a hexbin plot in Matplotlib. - -Lines 19-25 follow a similar structure to handle the visualization of subsets, by defining a ``plot_subset`` function. We make use of the -``style`` keyword, to make sure we choose colors, sizes, and -opacities that are consistent with the rest of Glue. The value passed -to the style keyword is a :class:`~glue.core.visual.VisualAttributes` -object. - -Custom data viewers give you the control to visualize data how you -want, while Glue handles all the tedious bookkeeping associated with updating plots when selections, styles, or datasets change. Try it out! - -Still, this viewer is pretty limited. In particular, it's missing -court markings, the ability to select data in the plot, and the ability -to interactively change plot settings with widgets. Let's fix that. - -Shot Chart Version 2: Court markings ------------------------------------- - -We'd like to draw court markings to give some context to the heatmap. -This is independent of the data, and we only need to render it once. -Just as you can register data and subset plot functions, you can also -register a setup function that gets called a single time, when the viewer -is created. That's a good place to draw court markings: - -.. literalinclude:: scripts/bball_viewer_2.py - :linenos: - -This version adds a new ``draw_court`` function at Line 30. Here's the result: - -.. figure:: images/bball_3.png - :align: center - -Shot Chart Version 3: Widgets ------------------------------ -There are several parameters we might want to tweak about our -visualization as we explore the data. For example, maybe we want -to toggle between a heatmap of the shots, and the percentage of -successful shots at each location. Or maybe we want to choose -the bin size interactively. - -The keywords that you pass to :func:`~glue.custom_viewer` allow you to -set up this functionality. Keywords serve two purposes: they define -new widgets to interact with the viewer, and they define keywords to pass -onto drawing functions like ``plot_data``. - -For example, consider :download:`this version ` of the Shot Plot code: - -.. literalinclude:: scripts/bball_viewer_3.py - :linenos: - -This code passes 4 new keywords to :func:`~glue.custom_viewer`: - - * ``bins=(10, 100)`` adds a slider widget, to choose an integer - between 10 and 100. We'll use this setting to set the - bin size of the heatmap. - * ``hitrate=False`` adds a checkbox. We'll use this setting to - toggle between a heatmap of total shots, and a map of - shot success rate. - * ``color=['Reds', 'Purples']`` creates a dropdown list - of possible colormaps to use for the heatmap. - * ``hit='att(shot_made)'`` behaves like the x and y keywords from - earlier -- it doesn't add a new widget, but it will - pass the shot_made data along to our plotting functions. - -This results in the following interface: - -.. figure:: images/bball_4.png - :align: center - -Whenever the user changes the settings of these widgets, the -drawing functions are re-called. Furthermore, the current -setting of each widget is available to the plotting functions: - - * ``bins`` is set to an integer - * ``hitrate`` is set to a boolean - * ``color`` is set to ``'Reds'`` or ``'Purples'`` - * ``x``, ``y``, and ``hit`` are passed as :class:`~glue.viewers.custom.qt.custom_viewer.AttributeWithInfo` objects (which are just numpy arrays with a special ``id`` attribute, useful when performing selection below). - -The plotting functions can use these variables to draw the appropriate -plots -- in particular, the ``show_hexbin`` function chooses -the binsize, color, and aggregation based on the widget settings. - -Shot Chart Version 4: Selection -------------------------------- -One key feature still missing from this Shot Chart is the ability to -select data by drawing on the plot. To do so, we need to write a -``select`` function that computes whether a set of data points are -contained in a user-drawn :class:`region of interest `: - -.. literalinclude:: scripts/bball_viewer_4.py - :lines: 18-20 - :linenos: - -With :download:`this version ` of the code you can how draw shapes on the plot to select data: - -.. figure:: images/bball_5.png - :align: center - -Viewer Subclasses ------------------ -The shot chart example used decorators to define custom plot functions. -However, if your used to writing classes you can also subclass -:class:`~glue.viewers.custom.qt.custom_viewer.CustomViewer` directly. The code is largely the -same: - -.. literalinclude:: scripts/bball_viewer_class.py - :linenos: - - -Valid Function Arguments ------------------------- - -The following argument names are allowed as inputs to custom -viewer functions: - - - Any UI setting provided as a keyword to :func:`glue.custom_viewer`. - The value passed to the function will be the current setting of the - UI element. - - ``axes`` is the matplotlib Axes object to draw to - - ``roi`` is the :class:`glue.core.roi.Roi` object a user created -- - it's only available in ``make_selection``. - - ``style`` is available to ``plot_data`` and ``plot_subset``. It is - the :class:`~glue.core.visual.VisualAttributes` associated with the - subset or dataset to draw - - ``state`` is a general purpose object that you can use to store - data with, in case you need to keep track of state in between - function calls. - -UI Elements ------------ - -Simple user interfaces are created by specifying keywords to -:func:`~glue.custom_viewer` or class-level variables to -:class:`~glue.viewers.custom.qt.custom_viewer.CustomViewer` subclasses. The type of -widget, and the value passed to plot functions, depends on the value -assigned to each variable. See :func:`~glue.custom_viewer` for -information. - -Other Guidelines ----------------- - - You can find other example data viewers at ``_. Contributions to this repository are welcome! - - - Glue auto-assigns the z-order of data and subset layers to the values - [0, N_layers - 1]. If you have elements you want to plot in the - background, give them a negative z-order - - - Glue tries to keep track of the plot layers that each custom function - creates, and auto-deletes old layers. This behavior can be disabled - by setting ``viewer.remove_artists=False``. Likewise, - ``plot_data`` and ``plot_subset`` can explicitly return a list - of newly-created artists. This might be more efficient if your - plot is very complicated. - - - By default, Glue sets the margins of figures so that the space between axes - and the edge of figures is constant in absolute terms. If the default values - are not adequate for your viewer, you can set the margins in the ``setup`` - method of the custom viewer by doing e.g.:: - - axes.resizer.margins = [0.75, 0.25, 0.5, 0.25] - - where the list gives the ``[left, right, bottom, top]`` margins in inches. diff --git a/doc/customizing_guide/customization.rst b/doc/customizing_guide/customization.rst deleted file mode 100644 index 049711e24..000000000 --- a/doc/customizing_guide/customization.rst +++ /dev/null @@ -1,619 +0,0 @@ -.. _customization: - -Customizing your Glue environment -================================= - -Using a ``config.py`` file as described in :ref:`configuration`, you can -customize many aspects of your Glue environment, which are described in the -following sections. - -Registries ----------- - -Before we talk about the different components of the Glue environment that you -can customize, we first need to look at registries. Glue is written so as to -allow users to easily register new data viewers, tools, exporters, and more. -Registering such components can be done via *registries* located in the -``glue.config`` sub-package. Registries include for example ``link_function``, -``data_factory``, ``colormaps``, and so on. As demonstrated below, some -registries can be used as decorators (see e.g. `Custom Link Functions`_) -and for others you can add items using the ``add`` method (see e.g. `Custom -Colormaps`_). - -In the following sections, we show a few examples of registering new -functionality, and a full list of available registries is given in `Complete -list of registries`_. - -.. _custom_links: - -Custom Link Functions ---------------------- - -From the :ref:`Link Data Dialog `, you inform Glue how -to convert between quantities among different data sets. You do this by -selecting a translation function, and specifying which data attributes should -be treated as inputs and outputs. You can use the configuration file to -specify custom translation functions. Here's how: - -.. literalinclude:: scripts/config_link_example.py - -Some remarks about this code: - #. ``link_function`` is used as a `decorator `_. The decorator adds the function to Glue's list of link functions - #. We provide a short summary of the function in the ``info`` keyword, and a list of ``output_labels``. Usually, only one quantity is returned, so ``output_labels`` has one element. - #. Glue will always pass numpy arrays as inputs to a link function, and expects a numpy array (or a tuple of numpy arrays) as output - -With this code in your configuration file, the ``deg_to_rad`` function is -available in the ``Link Data`` dialog: - -.. figure:: images/custom_link.png - :align: center - :width: 400 - -This would allow you to link between two datasets with different conventions -for specifying angles. - -.. _custom_data_factory: - -Custom Data Loaders -------------------- - -Glue lets you create custom data loader functions, -to use from within the GUI. - -Here's a quick example: the default image loader in Glue reads each color in -an RGB image into 3 two-dimensional attributes. Perhaps you want to be able -to load these images into a single 3-dimensional attribute called ``cube``. -Here's how you could do this:: - - from glue.config import data_factory - from glue.core import Data - from skimage.io import imread - - def is_jpeg(filename, **kwargs): - return filename.endswith('.jpeg') - - @data_factory('3D image loader', is_jpeg) - def read_jpeg(file_name): - im = imread(file_name) - return Data(cube=im) - -Let's look at this line-by-line: - -* The `is_jpeg` function takes a filename and keywords as input, - and returns True if a data factory can handle this file - -* The ``@data_factory`` decorator is how Glue "finds" this function. Its two - arguments are a label, and the `is_jpeg` identifier function - -* The first line in ``read_jpeg`` uses scikit-image to load an image file - into a NumPy array. - -* The second line :ref:`constructs a Data object ` from this - array, and returns the result. - -If you put this in your ``config.py`` file, you will see a new -file type when loading data: - - .. figure:: images/custom_data.png - :align: center - :width: 50% - -If you open a file using this file type selection, Glue will pass the path of -this file to your function, and use the resulting Data object. - -If you are defining a data factory that may clash with an existing one, for -example if you are defining a loader for a specific type of FITS file, then -make sure that the identifier function (e.g. ``is_jpeg`` above) returns `True` -only for that specific subset of FITS files. Then you can set the ``priority=`` -keyword in the ``@data_factory`` decorator. The value should be an integer or -floating-point number, with larger numbers indicating a higher priority. - -For more examples of custom data loaders, see the `example repository -`_. - -.. _custom_importers: - -Custom importers ----------------- - -The `Custom Data Loaders`_ described above allow Glue to recognize more file -formats than originally implemented, but it is also possible to write entire -new ways of importing data, including new GUI dialogs. An example would be a -dialog that allows the user to query and download online data. - -Currently, an importer should be defined as a function that returns a list of -:class:`~glue.core.data.Data` objects. In future we may relax this latter -requirement and allow existing tools in Glue to interpret the data. - -An importer can be defined using the ``@importer`` decorator:: - - from glue.config import importer - from glue.core import Data - - @importer("Import from custom source") - def my_importer(): - # Main code here - return [Data(...), Data(...)] - -The label in the ``@importer`` decorator is the text that will appear in the -``Import`` menu in Glue. - -.. _custom_data_exporter: - -Custom Data/Subset Exporters ----------------------------- - -.. note:: This section is about exporting the numerical values for datasets and - subsets. To export the *masks* for subsets, see - :ref:`custom_subset_mask_importer` and - :ref:`custom_subset_mask_exporter`. - -In addition to allowing you to create custom loaders and importers, glue lets -you create custom exporters for datasets and subsets. These exporters can be -accessed by control-clicking on specific datasets or subsets: - -.. figure:: images/export_data.png - :align: center - :width: 50% - -and selecting **Export Data** or **Export Subsets**. - -A custom exporter looks like the following:: - - from glue.config import data_exporter - - @data_exporter('My exporter') - def export_custom(filename, data): - # write out the data here - -The ``data`` argument to the function can be either a -:class:`~glue.core.data.Data` or a :class:`~glue.core.subset.Subset` object, and -``filename`` is a string which gives the file path. You can then write out the -file in any way you like. Note that if you get a -:class:`~glue.core.subset.Subset` object, you should make sure you export the -data subset, not just the mask itself. For e.g. 2-dimensional datasets, we find -that it is more intuitive to export arrays the same size as the original data -but with the values not in the subset masked or set to NaN. - -.. _custom_subset_mask_importer: - -Custom subset mask importers ----------------------------- - -When right-clicking on datasets or subsets, it is possible to select to import -subset *masks* from files (as well as export them). To define a new importer -format, use the ``@subset_mask_importer`` decorator:: - - from glue.config import subset_mask_importer - - @subset_mask_importer(label='My Format') - def my_subset_mask_importer(filename): - # write code that reads in subset masks here - -The function should return a dictionary where the labels are the names of the -subsets, and the values are Numpy boolean arrays. The ``@subset_mask_importer`` -decorator can also take an optional ``extension`` argument that takes a list of -extensions (e.g. ``['fits', 'fit']``). - -.. _custom_subset_mask_exporter: - -Custom subset mask exporters ----------------------------- - -When right-clicking on datasets or subsets, it is also possible to select to -export subset *masks* to files. To define a new exporter format, use the -``@subset_mask_exporter`` decorator:: - - from glue.config import subset_mask_exporter - - @subset_mask_exporter(label='My Format') - def my_subset_mask_exporter(filename, masks): - # write code that writes out subset masks here - -The ``masks`` argument will be given a dictionary where each key is the name of -a subset, and each value is a Numpy boolean array. The ``@subset_mask_exporter`` -decorator can also take an optional ``extension`` argument that takes a list of -extensions (e.g. ``['fits', 'fit']``). - - -.. _custom_menubar_tools: - -Custom menubar tools --------------------- - -In some cases, it might be desirable to add tools to Glue that can operate on -any aspects of the data or subsets, and can be accessed from the menubar. To -do this, you can define a function that takes two arguments (the session -object, and the data collection object), and decorate it with the -``@menubar_plugin`` decorator, giving it the label that will appear in the -**Tools** menubar:: - - from glue.config import menubar_plugin - - @menubar_plugin("Do something") - def my_plugin(session, data_collection): - # do anything here - return - -The function can do anything, such as launch a QWidget, or anything else -(such as a web browser, etc.), and does not need to return anything (instead -it can operate by directly modifying the data collection or subsets). - -Custom Colormaps ----------------- - -You can add additional matplotlib colormaps to Glue's image viewer by adding -the following code into ``config.py``:: - - from glue.config import colormaps - from matplotlib.cm import Paired - colormaps.add('Paired', Paired) - -.. _custom-actions: - -Custom Actions --------------- - -You can add menu items to run custom functions when selecting datasets, subset -groups or subsets in the data collection. To do this, you should define a -function to be called when the menu item is selected, and use the -``@layer_action`` decorator:: - - from glue.config import layer_action - - @layer_action('Do something') - def callback(selected_layers, data_collection): - print("Called with %s, %s" % (selected_layers, data_collection)) - -The ``layer_action`` decorator takes an optional ``single`` keyword argument -that can be set to `True` or `False` to indicate whether the action should only -appear when a single dataset, subset group, or subset is selected. If ``single`` -is `True`, the following keyword arguments can be used to further control when -to show the action: - -* ``data``: only show the action when selecting a dataset -* ``subset_group``: only show the action when selecting a subset group -* ``subset``: only show the action when selecting a subset - -These default to `False`, so setting e.g.:: - - @layer_action('Do something', single=True, data=True, subset=True) - ... - -means that the action will appear when a single dataset or subset is selected -but not when a subset group is selected. - -The callback function is called with two arguments. If ``single`` is `True`, the -first argument is the selected layer, otherwise it is the list of selected -layers. The second argument is the -:class:`~glue.core.data_collection.DataCollection` object. - -Custom Preference Panes ------------------------ - -You can also add custom panes in the Qt preferences dialog. To do this, you -should create a Qt widget that encapsulates the preferences you want to -include, and you should make sure that this widget has a ``finalize`` method -that will get called when the preferences dialog is closed. This method should -then set any settings appropriately in the application state. The following is -an example of a custom preference pane:: - - from glue.config import settings, preference_panes - from qtpy import QtWidgets - - - class MyPreferences(QtWidgets.QWidget): - - def __init__(self, parent=None): - - super(MyPreferences, self).__init__(parent=parent) - - self.layout = QtWidgets.QFormLayout() - - self.option1 = QtWidgets.QLineEdit() - self.option2 = QtWidgets.QCheckBox() - - self.layout.addRow("Option 1", self.option1) - self.layout.addRow("Option 2", self.option2) - - self.setLayout(self.layout) - - self.option1.setText(settings.OPTION1) - self.option2.setChecked(settings.OPTION2) - - def finalize(self): - settings.OPTION1 = self.option1.text() - settings.OPTION2 = self.option2.isChecked() - - - settings.add('OPTION1', '') - settings.add('OPTION2', False, bool) - preference_panes.add('My preferences', MyPreferences) - -This example then looks this the following once glue is loaded: - -.. image:: images/preferences.png - :align: center - -.. _custom_fixed_layout: - -Custom fixed layout tab ------------------------ - -.. note:: this feature is still experimental and may change in future - -By default, the main canvas of glue is a free-form canvas where windows can be -moved around and resized. However, it is also possible to construct fixed -layouts to create 'dashboards'. To do this, you should import the ``qt_fixed_layout_tab`` -object:: - - from glue.config import qt_fixed_layout_tab - -then use it to decorate a Qt widget that should be used instead of the free-form -canvas area, e.g.:: - - @qt_fixed_layout_tab - def MyCustomLayout(QWidget): - pass - -The widget can be any valid Qt widget - for instance it could be a widget with -a grid layout with data viewer widgets in each cell. - -.. _custom_startup: - -Custom startup actions ----------------------- - -It is possible to define actions to be carried out in glue once glue is open -and the data has been loaded. These should be written using the -``startup_action`` decorator:: - - from glue.config import startup_action - - @startup_action("action_name") - def my_startup_action(session, data_collection): - # do anything here - return - -The function has access to ``session``, which includes for example -``session.application``, and thus gives access to the full state of glue. - -Startup actions have to then be explicitly specified using:: - - glue --startup=action_name - -and multiple actions can be given as a comma-separated string. - -Custom layer artist makers --------------------------- - -In some cases, one may want to override the default layer artist classes used -by specific viewers. For example, for a particular data object, one may want to -show a tree or network on top of an image. - -This can be done by defining a function and decorating it with the -``layer_artist_maker`` decorator:: - - @layer_artist_maker('custom_maker') - def custom_maker(viewer, data_or_subset): - ... - -The function should take two arguments - the first argument is the viewer to -which the data is being added, and the second is the -:class:`~glue.core.data.Data` or :class:`~glue.core.subset.Subset` object to be -added. The function should then either return a custom -:class:`~glue.viewers.common.layer_artist.LayerArtist` instance, or `None` if -the function does not need to override the default layer artists. - -Note that ``layer_artist_maker`` can take an optional ``priority=`` argument -(which should be an integer), where higher values indicate that the layer artist -maker should be considered first. - -.. _custom-auto-link: - -Custom auto-linking helper --------------------------- - -It is possible to create functions that will automatically suggest links based -on the available data. To do so, use the ``autolinker`` decorator as follows:: - - from glue.config import autolinker - - @autolinker('Auto-linker name') - def my_autolinker(data_collection): - - ... - - return links - -The function should take a reference to a -:class:`~glue.core.data_collection.DataCollection` and should return a list of new -links that could be added. These will then automatically be suggested to the -user when new data are added. Note that it is your responsibility to ensure -that links that currently exist (and are in ``data_collection.external_links``) -are not suggested. - -.. _custom-data-translation: - -Custom translation to native data objects ------------------------------------------ - -Glue includes infrastructure to make it easy to convert between glue -:class:`~glue.core.data.Data` objects and non-glue-specific data containers, -such as pandas DataFrame. You can define your own converter -by writing a class that has ``to_data`` and ``to_object`` methods, and -register this class with ``@data_translator``, e.g.:: - - from glue.config import data_translator - - @data_translator(MyDataClass) - class MyDataClassHandler: - - def to_data(self, obj): - # This should take a MyDataClass object 'obj' and convert it to a - # glue Data object. - ... - return data - - def to_object(self, data): - # This should take a glue Data or Subset object and convert it to - # a MyDataClass object. - ... - return obj - -With this defined, you should then be able to add objects of type -``MyDataClass`` to the data collection and get them back using -:meth:`~glue.core.data.BaseData.get_object` and -:meth:`~glue.core.data.BaseData.get_subset_object`, e.g.:: - - >>> data_collection['mydata'] = MyDataClass(...) - >>> data = data_collection['mydata'] - >>> data - Data(...) - >>> data.get_object() - MyDataClass(...) - >>> data.get_subset_object(subset_id=0) - MyDataClass(...) - -.. _custom-subset-translation: - -Custom translation of subset definitions to native data objects ---------------------------------------------------------------- - -In the above section, we showed how -:meth:`~glue.core.data.BaseData.get_subset_object` can be used to get non-glue data -objects for a given subset - that is, a dataset with just the relevant subset of -values. However in some cases you may also want to have ways of converting the -conceptual definition of subsets to non-glue objects or to string -serializations. To do this, you should write a class that has a ``to_object`` -method and register the class using the ``@subset_definition_translator`` -decorator, e.g.:: - - from glue.config import subset_definition_translator - - @subset_definition_translator('my-serialized-format') - class SimpleSerializer: - - def to_object(self, subset): - - # This should take a glue subset and translate the subset state, - # which is accessible from subset.subset_state, to a non-glue - # object that represents the selection - this could either be - # a string or bytes serialization, or a different kind of - # object that represents selections. - - ... - - return selection_definition - - -With this defined, you should then be able to extract subsets with this converter -using -:meth:`~glue.core.data.BaseData.get_selection_definition`, e.g.:: - - >>> data.get_selection_definition(subset_id='subset 1', - format='my-serialized-format') - "a > 3" - -Custom session patch --------------------- - -Adding new features and keeping backward compatibility is hard. Because the -features of glue, and/or its plugins changes over time, old but precious -sessions files might not open anymore. To solve this problem, custom patch -functions can be provided to edit *in-place* the session file, just after it -is loaded. These patch functions should take a loaded session file (as a dict), -detect if it is invalid, and if so correct the problem, e.g.:: - - from glue.config import subset_definition_translator - - @session_patch(priority=0) - def correct_bad_version_plugin(session_obj): - # if problem: - # correct problem - - ... - - return - - - -Complete list of registries ---------------------------- - -A few registries have been demonstrated above, and a complete list of main -registries are listed below. All can be imported from ``glue.config`` - each -registry is an instance of a class, given in the second column, and which -provides more information about what the registry is and how it can be used. - -=========================== ========================================================= -Registry name Registry class -=========================== ========================================================= -``qt_client`` :class:`glue.config.QtClientRegistry` -``qt_fixed_layout_tab`` :class:`glue.config.QtFixedLayoutTabRegistry` -``viewer_tool`` :class:`glue.config.ViewerToolRegistry` -``data_factory`` :class:`glue.config.DataFactoryRegistry` -``data_exporter`` :class:`glue.config.DataExporterRegistry` -``subset_mask_importer`` :class:`glue.config.SubsetMaskImporterRegistry` -``subset_mask_exporter`` :class:`glue.config.SubsetMaskExporterRegistry` -``link_function`` :class:`glue.config.LinkFunctionRegistry` -``link_helper`` :class:`glue.config.LinkHelperRegistry` -``colormaps`` :class:`glue.config.ColormapRegistry` -``exporters`` :class:`glue.config.ExporterRegistry` -``settings`` :class:`glue.config.SettingRegistry` -``preference_panes`` :class:`glue.config.PreferencePanesRegistry` -``fit_plugin`` :class:`glue.config.ProfileFitterRegistry` -``layer_action`` :class:`glue.config.LayerActionRegistry` -``startup_action`` :class:`glue.config.StartupActionRegistry` -``autolinker`` :class:`glue.config.AutoLinkerRegistry` -``data_translator`` :class:`glue.config.DataTranslatorRegistry` -``subset_state_translator`` :class:`glue.config.SubsetDefinitionTranslatorRegistry` -``session_patch`` :class:`glue.config.SessionPatchRegistry` -=========================== ========================================================= - -.. _lazy_load_plugin: - -Deferring loading of plug-in functionality (advanced) ------------------------------------------------------ - -In some cases, you may want to defer the loading of your -component/functionality until it is actually needed. To do this: - -* Place the code for your plugin in a file or package that could be imported - from the ``config.py`` (but don't import it directly - it just has to be - importable) - -* Include a function called ``setup`` alongside the plugin, and this function - should contain code to actually add your custom tools to the appropriate - registries. - -* In ``config.py``, you can then add the plugin file or package to a registry - by using the ``lazy_add`` method and pass a string giving the name of the - package or sub-package containing the plugin. - -Imagine that you have created a data viewer ``MyQtViewer``. You could -directly register it using:: - - from glue.config import qt_client - qt_client.add(MyQtViewer) - -but if you want to defer the loading of the ``MyQtViewer`` class, you can -place the definition of ``MyQtViewer`` in a file called e.g. -``my_qt_viewer.py`` that is located in the same directory as your -``config.py`` file. This file should look something like:: - - class MyQtViewer(...): - ... - - def setup(): - from glue.config import qt_client - qt_client.add(MyQtViewer) - -then in ``config.py``, you can do:: - - from glue.config import qt_client - qt_client.lazy_add('my_qt_viewer') - -With this in place, the ``setup`` in your plugin will only get called if the -Qt data viewers are needed, but you will avoid unnecessarily importing Qt if -you only want to access ``glue.core``. diff --git a/doc/customizing_guide/fitting.rst b/doc/customizing_guide/fitting.rst deleted file mode 100644 index d848641a1..000000000 --- a/doc/customizing_guide/fitting.rst +++ /dev/null @@ -1,131 +0,0 @@ -.. _custom-fitting: - -Custom fitting plugins -====================== - -The profile fitting tool is designed to be easily extended, so that -you can plug in your own model fitting code worrying about GUI code. -We will walk through several examples of custom fitting plugins, -to demonstrate the various features of the plugin system. - - -Simple line fitter -------------------- -Our first example is a simple linear model: - -.. literalinclude:: scripts/line_fit_plugin.py - :linenos: - :emphasize-lines: 6,8,10,13 - -Let's look at this line by line: - -Line 6 wraps a subclass of BaseFitter1D in the ``fit_plugin`` decorator. -All plugins follow this basic structure. - -Line 8 gives this class a label, which is used in the GUI to label -this model in the model selection dropdown. - -Line 10 overrides the :meth:`~glue.core.fitters.BaseFitter1D.fit` method. All plugins must implement fit, which -takes at least 4 parameters: - - * x: A numpy array of X values - * y: A numpy array of Y values - * dy: A numpy array of the errors on each Y value, or none - * constraints: A dictionary of constraints (more on this later) - -The fit method can do whatever it wants. Here, we are using :func:`numpy.polyfit` to fit a 1st-order polynomial to the data. We ignore dy and constraints. -We return the result from polyfit -- Glue doesn't care what fit returns, -it just passes that to other methods (as we will now see) - -Line 13 overrides the :meth:`~glue.core.fitters.BaseFitter1D.predict` method. Again, all models must define this method. -It takes 2 inputs -- whatever was returned from :meth:`~glue.core.fitters.BaseFitter1D.fit`, and an array of X values -to evaluate the model at. This method must return a array of model-predicted Y -values at each X location. We use :func:`numpy.polyval` to do this - -This code is enough to let us fit lines to data: - -.. figure:: images/line_screenshot.png - :align: center - :width: 500 - -.. note:: In order for Glue to find this code, we need to copy this file to the same directory as :ref:`config.py ` (``~/.glue`` by default), and add ``import line_fit_plugin`` to ``config.py``. - -Polynomial fitter, with Options -------------------------------- - -Generalizing the line fitter above to higher degree polynomials is trivial, since ``polyfit/polyval`` both handle this case. We might want to make the degree of the fit a user-settable -parameter. We can do this by adding a UI :mod:`option `, and a few keywords to our class: - -.. literalinclude:: scripts/poly_fit_plugin.py - :linenos: - -This code adds a few new features: - -Line 10 adds an :class:`~glue.core.simpleforms.IntOption` named degree to the class. Likewise, -the fit method takes a keyword named degree, and uses this to fit a -polynomial of order ``degree`` (e.g., degree=2 corresponds to a parabola). -This extra information allows Glue to add a widget to the settings window: - -.. figure:: images/poly_screenshot.png - :align: center - :width: 500 - -This plugin also overrides the :meth:`~glue.core.fitters.BaseFitter1D.summarize` method. Summarize returns a string, which is used as the display in the fit summary window. - -Model with constraints ----------------------- - -Models like those found in ``astropy.modeling`` support fixing or -constraining certain parameters. If you would like to add user-settable -constraints to your model, add a ``param_names`` list to the class:: - - class ConstrainedGaussian(BaseFitter1D): - param_names = ['amplitude'] - ... - -Glue uses this information to let the user fix or limit parameters -in the settings tab. This information is passed to the ``constraints`` -argument of :meth:`~glue.core.fitters.BaseFitter1D.fit`. ``constraints`` is a dictionary whose keys are -parameter names. Each value is itself a dictionary with 4 entries: - - * The default ``value`` of the parameter, or None if not set by the user - * ``fixed``, which is True if the parameter should be held fixed - * ``limits``, which is None if the value is unconstrained, or a list of minimum/maximum allowed values - -Astropy-based models --------------------- - -The :class:`~glue.core.fitters.AstropyFitter1D` base class can be subclassed to -plug custom `astropy models and fitters -`_ into Glue. This is very -easy:: - - from astropy.modeling import models, fitting - - @fit_plugin - class Gaussian(AstropyFitter1D): - model_cls = models.Gaussian1D - fitting_cls = fitting.NonLinearLSQFitter - label = "Gaussian" - - def parameter_guesses(self, x, y, dy): - return dict(amplitude=1, stddev=1, mean=1) - -The :meth:`~glue.core.fitters.AstropyFitter1D.parameter_guesses` method is optional, and provides initial guesses -for the model parameters if they weren't set by the user. - -Custom Plotting ----------------- - -Fit plugins can also override the :meth:`~glue.core.fitters.BaseFitter1D.plot` method, to customize how the model fit is drawn on the profile. - - -Example: Gaussian fitting with Emcee ------------------------------------- - -The :download:`emcee plugin example ` combines many -of these ideas. - -.. figure:: images/emcee_screenshot.png - :align: center - :width: 400 diff --git a/doc/customizing_guide/full_custom_qt_viewer.rst b/doc/customizing_guide/full_custom_qt_viewer.rst deleted file mode 100644 index 044907dac..000000000 --- a/doc/customizing_guide/full_custom_qt_viewer.rst +++ /dev/null @@ -1,217 +0,0 @@ -:orphan: - -Writing a fully customized Qt viewer (advanced) -=============================================== - -Motivation ----------- - -This tutorial is intended for people who want to develop a fully custom Qt-based -viewer for glue that does not use Matplotlib and does not use the state class -infrastructure. If you don't already have an existing widget, but want to make -sure it will work outside glue, start off by developing the widget outside of -glue, then use the instructions below to make it usable inside glue. - -Displaying the widget in glue ------------------------------ - -Let's imagine that you have a Qt widget class called ``MyWidget`` the -inherits from ``QWidget`` and implements a specific type of visualization you -are interested in:: - - class MyWidget(QWidget): - ... - -Now let's say we want to use this widget in glue, without having to change -anything in ``MyWidget``. The best way to do this is to create a new class, -``MyGlueWidget``, that will wrap around ``MyWidget`` and make it -glue-compatible. The glue widget should inherit from -:class:`~glue.viewers.common.qt.data_viewer.DataViewer` (this class does a few -boilerplate things such as, for example, adding the ability to drag and drop -data onto your data viewer). The simplest glue widget wrapper that you can write -that will show ``MyWidget`` is:: - - from glue.qt.widgets.data_viewer import DataViewer - - class MyGlueWidget(DataViewer): - - def __init__(self, session, parent=None): - super(MyGlueWidget, self).__init__(session, parent=parent) - self.my_widget = MyWidget() - self.setCentralWidget(self.my_widget) - - # Register the viewer with glue - from glue.config import qt_client - qt_client.add(MyGlueWidget) - -If you put the contents above into a ``config.py`` file then launch glue in -the same folder as the ``config.py`` file, you will then be able to go to the -**Canvas** menu, select **New Data Viewer**, and you should then be presented -with the window to select a data view, which should contain an **Override -this** entry: - -.. image:: images/select_override.png - :width: 200px - :align: center - -To give your viewer a more meaningful name, you should give your class an -attribute called ``LABEL``:: - - class MyGlueWidget(DataViewer): - - LABEL = "My first data viewer" - - def __init__(self, session, parent=None): - super(MyGlueWidget, self).__init__(session, parent=parent) - self.my_widget = MyWidget() - self.setCentralWidget(self.my_widget) - -Passing data to the widget --------------------------- - -Now we want to be able to pass data to this viewer. To do this, you should -define the ``add_data`` method which should take a single argument and return -`True` if adding the data succeeded, and `False` otherwise. So for now, let's -simply return `True` and do nothing:: - - def add_data(self, data): - return True - -Now you can open glue again, and this time you should be able to load a -dataset the usual way. When you drag this dataset onto the main canvas area, -you will be able to then select your custom viewer, and it should appear -(though the data itself will not). You can now expand the ``add_data`` method -to actually add the data to ``MyWidget``, by accessing ``self.my_widget``, -for example:: - - def add_data(self, data): - self.my_widget.plot(data) - return True - -However, this will simply plot the initial data and plot more data if you -drag datasets onto the window, but you will not for example be able to remove -datasets, show subsets, and so on. In some cases, that may be fine, and you -can stop at this point, but in other cases, if you want to define a way to -interact with subsets, propagate selections, and so on, you will need to listen -for events, which is discussed in `Listening for events`_. - -But first, let's take a look at how we can add side panels in the dashboard -which can include for example options for controlling the appearance or contents -of your visualization. - -Adding side panels ------------------- - -In the glue interface, under the data collection is an area we refer to as the -dashboard, where different data viewers can include options for controlling the -appearance or content of visualizations (this is the area indicated as **C** in -:doc:getting-started). You can add any widget to the two available spaces. - -In your wrapper class, ``MyGlueWidget`` in the example above, you will need to -define a method called ``options_widget``, which returns an instantiated widget -that should be included in the dashboard on the bottom left of the glue window, -and can contain options to control the data viewer. - -For example, you could do:: - - class MyGlueWidget(DataViewer): - - ... - - def __init__(self, session, parent=None): - ... - self._options_widget = AnotherWidget(...) - - ... - - def options_widget(self): - return self._options_widget - -Note that despite the name, you can actually use the options widget to what you -want, and the important thing is that ``options_widget`` is the bottom left -pane in the dashboard on the left. - -Note that you can also similarly define (via a method) ``layer_view``, which -sets the widget for the middle widget in the dashboard. However, this will -default to a list of layers which can normally be used as-is (see `Using -Layers`_) - -Listening for events --------------------- - -Once the data viewer has been instantiated, the main glue application will -automatically call the ``register_to_hub`` method on the data viewer, and will -pass it the hub as an argument. This allows you to set up your data viewer as a -client that can listen to specific messages from the hub:: - - from glue.core.message import DataCollectionAddMessage - - class MyGlueWidget(DataViewer): - - ... - - def register_to_hub(self, hub): - - super(MyGlueWidget, self).register_to_hub(hub) - - # Now we can subscribe to messages with the hub - - hub.subscribe(self, - DataUpdateMessage, - handler=self._update_data) - - def _update_data(self, msg): - - # Process DataUpdateMessage here - -Using layers ------------- - -By default, any sub-class of `~glue.viewers.common.qt.data_viewer` will -also include a list of layers in the central panel in the dashboard. Layers can -be thought of as specific components of visualizations - for example, in a -scatter plot, the main dataset will be a layer, while each individual subset -will have its own layer. The order of the layers (which controls which one -appears in front of which) can then be set by dragging the layers around, and -the color/style of the layers can also be set from this list of layers. - -Conceptually, layer artists can be used to carry out the actual drawing and -include any logic about how to convert data into visualizations. If you are -using Matplotlib for your visualization, there are a number of pre-existing -layer artists in ``glue.viewers.*.layer_artist``, but otherwise you will need -to create your own classes. - -The minimal layer artist class looks like the following:: - - from glue.core.layer_artist import LayerArtistBase - - class MyLayerArtist(LayerArtistBase): - - def clear(self): - pass - - def redraw(self): - pass - - def update(self): - pass - -Essentially, each layer artist has to define the three methods shown above. The -``clear`` method should remove the layer from the visualization, the ``redraw`` -method should redraw the entire visualization, and ``update``, should update -the appearance of the layer as necessary before redrawing. - -In the data viewer, when the user adds a dataset or a subset, the list of -layers should then be updated. The layers are kept in a list in the -``_layer_artist_container`` attribute of the data viewer, and layers can be added and -removed with ``append`` and ``remove`` (both take one argument, which is a -specific layer artist). So when the user adds a dataset, the viewer should do -something along the lines of:: - - layer_artist = MyLayerArtist(data, ...) - self._layer_artist_container.append(layer_artist) - layer_artist.redraw() - -If the user removes a layer from the list of layers by e.g. hitting the -backspace key, the ``clear`` method is called, followed by the ``redraw`` -method. diff --git a/doc/customizing_guide/images/bball_1.png b/doc/customizing_guide/images/bball_1.png deleted file mode 100644 index b66658028..000000000 Binary files a/doc/customizing_guide/images/bball_1.png and /dev/null differ diff --git a/doc/customizing_guide/images/bball_2.png b/doc/customizing_guide/images/bball_2.png deleted file mode 100644 index 45779ffd8..000000000 Binary files a/doc/customizing_guide/images/bball_2.png and /dev/null differ diff --git a/doc/customizing_guide/images/bball_3.png b/doc/customizing_guide/images/bball_3.png deleted file mode 100644 index 83c9348fd..000000000 Binary files a/doc/customizing_guide/images/bball_3.png and /dev/null differ diff --git a/doc/customizing_guide/images/bball_4.png b/doc/customizing_guide/images/bball_4.png deleted file mode 100644 index bbf6be414..000000000 Binary files a/doc/customizing_guide/images/bball_4.png and /dev/null differ diff --git a/doc/customizing_guide/images/bball_5.png b/doc/customizing_guide/images/bball_5.png deleted file mode 100644 index 262458baf..000000000 Binary files a/doc/customizing_guide/images/bball_5.png and /dev/null differ diff --git a/doc/customizing_guide/images/custom_data.png b/doc/customizing_guide/images/custom_data.png deleted file mode 100644 index 75b719de6..000000000 Binary files a/doc/customizing_guide/images/custom_data.png and /dev/null differ diff --git a/doc/customizing_guide/images/custom_link.png b/doc/customizing_guide/images/custom_link.png deleted file mode 100644 index 3b4d7b4fd..000000000 Binary files a/doc/customizing_guide/images/custom_link.png and /dev/null differ diff --git a/doc/customizing_guide/images/emcee_screenshot.png b/doc/customizing_guide/images/emcee_screenshot.png deleted file mode 100644 index 153de782e..000000000 Binary files a/doc/customizing_guide/images/emcee_screenshot.png and /dev/null differ diff --git a/doc/customizing_guide/images/export_data.png b/doc/customizing_guide/images/export_data.png deleted file mode 100644 index bf83de04a..000000000 Binary files a/doc/customizing_guide/images/export_data.png and /dev/null differ diff --git a/doc/customizing_guide/images/line_screenshot.png b/doc/customizing_guide/images/line_screenshot.png deleted file mode 100644 index 6c71aa86e..000000000 Binary files a/doc/customizing_guide/images/line_screenshot.png and /dev/null differ diff --git a/doc/customizing_guide/images/link_functions.png b/doc/customizing_guide/images/link_functions.png deleted file mode 100644 index f336547cd..000000000 Binary files a/doc/customizing_guide/images/link_functions.png and /dev/null differ diff --git a/doc/customizing_guide/images/plugin_plotly.png b/doc/customizing_guide/images/plugin_plotly.png deleted file mode 100644 index a06acb926..000000000 Binary files a/doc/customizing_guide/images/plugin_plotly.png and /dev/null differ diff --git a/doc/customizing_guide/images/poly_screenshot.png b/doc/customizing_guide/images/poly_screenshot.png deleted file mode 100644 index 27cd85567..000000000 Binary files a/doc/customizing_guide/images/poly_screenshot.png and /dev/null differ diff --git a/doc/customizing_guide/images/preferences.png b/doc/customizing_guide/images/preferences.png deleted file mode 100644 index a74ec884e..000000000 Binary files a/doc/customizing_guide/images/preferences.png and /dev/null differ diff --git a/doc/customizing_guide/images/samp_contextual.png b/doc/customizing_guide/images/samp_contextual.png deleted file mode 100644 index eea22d575..000000000 Binary files a/doc/customizing_guide/images/samp_contextual.png and /dev/null differ diff --git a/doc/customizing_guide/images/samp_open.png b/doc/customizing_guide/images/samp_open.png deleted file mode 100644 index 13842f7f6..000000000 Binary files a/doc/customizing_guide/images/samp_open.png and /dev/null differ diff --git a/doc/customizing_guide/images/samp_window.png b/doc/customizing_guide/images/samp_window.png deleted file mode 100644 index d6627ee33..000000000 Binary files a/doc/customizing_guide/images/samp_window.png and /dev/null differ diff --git a/doc/customizing_guide/images/select_override.png b/doc/customizing_guide/images/select_override.png deleted file mode 100644 index 57424760d..000000000 Binary files a/doc/customizing_guide/images/select_override.png and /dev/null differ diff --git a/doc/customizing_guide/introduction.rst b/doc/customizing_guide/introduction.rst deleted file mode 100644 index 11266f995..000000000 --- a/doc/customizing_guide/introduction.rst +++ /dev/null @@ -1,26 +0,0 @@ -Introduction to customizing/extending glue -========================================== - -Glue has been designed from the ground up to be easily customizable by users, -with a number of customization aspects requiring only simple Python code and no -knowledge of e.g. Qt. There are two main ways to customize/extend glue: - -* Installing an existing plugin package that will automatically extend glue. - For example, you can install a package called glue-geospatial to add the - ability to read in geospatial (e.g. GeoTIFF) data, or glue-medical to add - the ability to read in common medical (e.g. DICOM) formats. You can find - a list of available plugins as well as information on installing them - at :ref:`available_plugins` - -* Writing your own customizations and placing these in a file named - ``config.py`` (we will look shortly at where to place that file). This is - ideal if you just want to customize glue quickly for your own work, or if you - are prototyping ideas for a more advanced plugin package. To find out more - about what aspects of glue you can customize and how to do it, you can read - :ref:`configuration`. - -If you are developing something for your own work only, then using the -``config.py`` approach is usually sufficient, and you don't have to develop a -fully fledged plugin package. But if you are interested in distributing your -plugin for others to use, then you should make sure you read -:ref:`writing_plugin` for more information on how to do this. diff --git a/doc/customizing_guide/matplotlib_qt_viewer.rst b/doc/customizing_guide/matplotlib_qt_viewer.rst deleted file mode 100644 index 8482ddf17..000000000 --- a/doc/customizing_guide/matplotlib_qt_viewer.rst +++ /dev/null @@ -1,96 +0,0 @@ -.. _matplotlib-qt-viewer: - -Writing a custom viewer for glue with Qt and Matplotlib -======================================================= - -If you are a user trying to build a very simple viewer using Matplotlib, you may -want to check out :doc:`custom_viewer` -- the present tutorial is intended for -people who wish to write and distribute a viewer using Matplotlib with full -control over layout and behavior. This tutorial assumes that you have already -gone over the :ref:`state-viewer` and :ref:`state-qt-viewer` tutorials. - -Glue provides a set of base classes for the state classes, layer artist, and -data viewer which already take care of a number of aspects common to all -Matplotlib-based viewers. We describe each of these in turn in the following -sections, then simplify the example from :ref:`state-qt-viewer` using this -infrastructure. - -State classes -------------- - -The :class:`~glue.viewers.matplotlib.state.MatplotlibDataViewerState` class -provides a subclass of :class:`~glue.viewers.common.state.ViewerState` which -adds properties related to: - -* the appearance of the plot (font and tick sizes) -* the limits of the current view (this currently assumes 2D plots) -* the aspect ratio of the axes -* whether the axes are log or linear - -Note that it does not add e.g. ``x_att`` and ``y_att`` since not all Matplotlib- -based viewers will require the same number of attributes, and since some viewers -may define attributes that aren't specific to the x or y axis (e.g. in the case -of networks). - -The :class:`~glue.viewers.matplotlib.state.MatplotlibLayerState` class -provides a subclass of :class:`~glue.viewers.common.state.LayerState` which -adds the ``color`` and ``alpha`` property (and keeps them in sync with -``layer.style.color`` and ``layer.style.alpha``). - -Layer artist ------------- - -The :class:`~glue.viewers.matplotlib.layer_artist.MatplotlibLayerArtist` class -implements the -:meth:`~glue.viewers.matplotlib.layer_artist.MatplotlibLayerArtist.redraw`, -:meth:`~glue.viewers.matplotlib.layer_artist.MatplotlibLayerArtist.remove`, and -:meth:`~glue.viewers.matplotlib.layer_artist.MatplotlibLayerArtist.clear` -methods assuming that all the contents of the layer use Matplotlib artists. In -the ``__init__`` of your -:class:`~glue.viewers.matplotlib.layer_artist.MatplotlibLayerArtist` sub-class, -you should make sure you add all artist references to the ``mpl_artists`` -property for this to work. - -Data viewer ------------ - -The :class:`~glue.viewers.matplotlib.qt.data_viewer.MatplotlibDataViewer` class -adds functionality on top of the base -:class:`~glue.viewers.common.qt.data_viewer.DataViewer` -class: - -* It automatically sets up the Matplotlib axes -* It keeps the x/y limits of the plot, the scale (linear/log), the font/tick - parameters, and the aspect ratio in sync with the - :class:`~glue.viewers.matplotlib.state.MatplotlibDataViewerState` -* It adds tools for saving, zooming, panning, and resetting the view -* It recognizes the global glue preferences for foreground/background color - -Functional example ------------------- - -Let's now take the take full example from :ref:`state-qt-viewer` and -update/improve it to use the infrastructure described above. As before if you -want to try this out, you can copy the code below into a file called -``config.py`` in the directory from where you are starting glue. In addition you -will also need the :download:`viewer_state.ui ` -file. - -.. literalinclude:: mpl_viewer/config.py - -While the code is not much shorter, there is additional functionality available. -In particular, the viewer now has standard Matplotlib buttons in the toolbar: - -.. image:: mpl_viewer/tutorial_viewer.png - :width: 600px - :align: center - -In addition, the layer artist has been improved to take into account the color -and transparency given by the layer state (via the ``_on_visual_change`` -method), and the axis labels are now set in the viewer state class. - -Further reading ---------------- - -To find out how to add tools to your custom viewer, see the -:ref:`custom-toolbars` tutorial. diff --git a/doc/customizing_guide/mpl_viewer/config.py b/doc/customizing_guide/mpl_viewer/config.py deleted file mode 100644 index 6e9a534cb..000000000 --- a/doc/customizing_guide/mpl_viewer/config.py +++ /dev/null @@ -1,140 +0,0 @@ -import os - -import numpy as np - -from qtpy.QtWidgets import QWidget, QVBoxLayout, QCheckBox - -from glue.config import qt_client -from glue.core.data_combo_helper import ComponentIDComboHelper - -from echo import CallbackProperty, SelectionCallbackProperty -from echo.qt import (connect_checkable_button, - autoconnect_callbacks_to_qt) - -from glue.viewers.matplotlib.layer_artist import MatplotlibLayerArtist -from glue.viewers.matplotlib.state import MatplotlibDataViewerState, MatplotlibLayerState -from glue.viewers.matplotlib.qt.data_viewer import MatplotlibDataViewer - -from glue.utils.qt import load_ui - - -class TutorialViewerState(MatplotlibDataViewerState): - - x_att = SelectionCallbackProperty(docstring='The attribute to use on the x-axis') - y_att = SelectionCallbackProperty(docstring='The attribute to use on the y-axis') - - def __init__(self, *args, **kwargs): - super(TutorialViewerState, self).__init__(*args, **kwargs) - self._x_att_helper = ComponentIDComboHelper(self, 'x_att') - self._y_att_helper = ComponentIDComboHelper(self, 'y_att') - self.add_callback('layers', self._on_layers_change) - self.add_callback('x_att', self._on_attribute_change) - self.add_callback('y_att', self._on_attribute_change) - - def _on_layers_change(self, value): - self._x_att_helper.set_multiple_data(self.layers_data) - self._y_att_helper.set_multiple_data(self.layers_data) - - def _on_attribute_change(self, value): - if self.x_att is not None: - self.x_axislabel = self.x_att.label - if self.y_att is not None: - self.y_axislabel = self.y_att.label - - -class TutorialLayerState(MatplotlibLayerState): - fill = CallbackProperty(False, docstring='Whether to show the markers as filled or not') - - -class TutorialLayerArtist(MatplotlibLayerArtist): - - _layer_state_cls = TutorialLayerState - - def __init__(self, axes, *args, **kwargs): - - super(TutorialLayerArtist, self).__init__(axes, *args, **kwargs) - - self.artist = self.axes.plot([], [], 'o', mec='none')[0] - self.mpl_artists.append(self.artist) - - self.state.add_callback('fill', self._on_visual_change) - self.state.add_callback('visible', self._on_visual_change) - self.state.add_callback('zorder', self._on_visual_change) - self.state.add_callback('color', self._on_visual_change) - self.state.add_callback('alpha', self._on_visual_change) - - self._viewer_state.add_callback('x_att', self._on_attribute_change) - self._viewer_state.add_callback('y_att', self._on_attribute_change) - - def _on_visual_change(self, value=None): - - self.artist.set_visible(self.state.visible) - self.artist.set_zorder(self.state.zorder) - self.artist.set_markeredgecolor(self.state.color) - if self.state.fill: - self.artist.set_markerfacecolor(self.state.color) - else: - self.artist.set_markerfacecolor('white') - self.artist.set_alpha(self.state.alpha) - - self.redraw() - - def _on_attribute_change(self, value=None): - - if self._viewer_state.x_att is None or self._viewer_state.y_att is None: - return - - x = self.state.layer[self._viewer_state.x_att] - y = self.state.layer[self._viewer_state.y_att] - - self.artist.set_data(x, y) - - self.axes.set_xlim(np.nanmin(x), np.nanmax(x)) - self.axes.set_ylim(np.nanmin(y), np.nanmax(y)) - - self.redraw() - - def update(self): - self._on_attribute_change() - self._on_visual_change() - - -class TutorialViewerStateWidget(QWidget): - - def __init__(self, viewer_state=None, session=None): - - super(TutorialViewerStateWidget, self).__init__() - - self.ui = load_ui('viewer_state.ui', self, - directory=os.path.dirname(__file__)) - - self.viewer_state = viewer_state - self._connections = autoconnect_callbacks_to_qt(self.viewer_state, self.ui) - - -class TutorialLayerStateWidget(QWidget): - - def __init__(self, layer_artist): - - super(TutorialLayerStateWidget, self).__init__() - - self.checkbox = QCheckBox('Fill markers') - layout = QVBoxLayout() - layout.addWidget(self.checkbox) - self.setLayout(layout) - - self.layer_state = layer_artist.state - self._connection = connect_checkable_button(self.layer_state, 'fill', self.checkbox) - - -class TutorialDataViewer(MatplotlibDataViewer): - - LABEL = 'Tutorial viewer' - _state_cls = TutorialViewerState - _options_cls = TutorialViewerStateWidget - _layer_style_widget_cls = TutorialLayerStateWidget - _data_artist_cls = TutorialLayerArtist - _subset_artist_cls = TutorialLayerArtist - - -qt_client.add(TutorialDataViewer) diff --git a/doc/customizing_guide/mpl_viewer/tutorial_viewer.png b/doc/customizing_guide/mpl_viewer/tutorial_viewer.png deleted file mode 100644 index 89d2ffb58..000000000 Binary files a/doc/customizing_guide/mpl_viewer/tutorial_viewer.png and /dev/null differ diff --git a/doc/customizing_guide/mpl_viewer/viewer_state.ui b/doc/customizing_guide/mpl_viewer/viewer_state.ui deleted file mode 100644 index 1e6308dc5..000000000 --- a/doc/customizing_guide/mpl_viewer/viewer_state.ui +++ /dev/null @@ -1,33 +0,0 @@ - - - Form - - - Form - - - - - - x attribute - - - - - - - - - - y attribute - - - - - - - - - - - diff --git a/doc/customizing_guide/qt_viewer.rst b/doc/customizing_guide/qt_viewer.rst deleted file mode 100644 index 1d2e302b8..000000000 --- a/doc/customizing_guide/qt_viewer.rst +++ /dev/null @@ -1,172 +0,0 @@ -.. _state-qt-viewer: - -Writing a custom viewer for glue with Qt -======================================== - -In :ref:`state-viewer`, we looked at the basics of building data viewers for -glue. In this follow-up tutorial, we look at specifically how to use this to -build a data viewer for the Qt front-end of glue. - -Options widgets ---------------- - -We mentioned in :ref:`state-viewer` that there are state classes that contain a -conceptual representation of the overall viewer options and the settings -pertaining to each layer in the visualization. What is now needed are widgets -that will allow users to easily change this state, and also reflect changes to -the state that are made programmatically. - -In the Qt version of glue, viewers typically define a widget to control the -viewer state, which is usually shown is the area indicated as **C** in -the following diagram: - -.. image:: ../getting_started/images/main_window.png - :width: 600px - :align: center - -and a widget to control the layer state, which is usually shown is the area -indicated as **B** in the above diagram (in addition to the layer list). - -The only requirement for these widgets is that the widget for the viewer options -should take an argument which is the viewer state (as well as a ``session`` -keyword argument which is a :class:`~glue.core.session.Session` object that -contains a reference to the data collection and hub), and the widget for the -layer settings should take an argument which is the layer artist (in future this -will likely be changed to the layer state), but beyond this, you can implement -the widgets any way you like. Let's take the simple layer state example above -with the ``fill`` option. You could implement a layer options widget by doing:: - - from echo.qt import connect_checkable_button - from qtpy.QtWidgets import QWidget, QVBoxLayout, QCheckBox - - class TutorialLayerStateWidget(QWidget): - - def __init__(self, layer_artist): - - super(LayerEditWidget, self).__init__() - - self.checkbox = QCheckBox('Fill markers') - layout = QVBoxLayout() - layout.addWidget(self.checkbox) - self.setLayout(layout) - - self.layer_state = layer_artist.state - self._connection = connect_checkable_button(self.layer_state, 'fill', self.checkbox) - -In the above example, you can see that we use the -:class:`~echo.qt.connect_checkable_button` function to link the -``fill`` property from the layer state with the checkbox. For a full list of -available functions, see `here -`__. - -For more complex cases, you may want to use Qt Designer to create a ui file with -your layout (such as :download:`viewer_state.ui `), then load it -into the options widget - you can then also automatically connect UI elements to -state properties using the -:func:`~echo.qt.autoconnect_callbacks_to_qt` function. Let's use -this to create a widget to control the viewer state:: - - from echo.qt import autoconnect_callbacks_to_qt - from qtpy.QtWidgets import QWidget - from glue.utils.qt import load_ui - - class TutorialViewerStateWidget(QWidget): - - def __init__(self, viewer_state, session=None): - - super(TutorialViewerStateWidget, self).__init__() - - # The dirname= is used to indicate that the .ui file is in the same - # directory as the present file. - self.ui = load_ui('options_widget.ui', dirname=os.path.dirname(__file__)) - - self.viewer_state = viewer_state - self._connections = autoconnect_callbacks_to_qt(self.viewer_state, self.ui) - -For :func:`~echo.qt.autoconnect_callbacks_to_qt` to work, you need -to follow certain naming conventions for the UI elements in the ``.ui`` file. You -can read up more about this convention `here -`__. - -Data viewer ------------ - -In the case of Qt, defining a data viewer is similar to the general case -described in :ref:`state-viewer` but this time we need to use the -``DataViewer`` class from ``glue.viewers.common.qt.data_viewer`` and define -two additional attributes to point to the widgets that control the viewer -and layer state:: - - from glue.viewers.common.qt.data_viewer import DataViewer - - class TutorialDataViewer(DataViewer): - - LABEL = 'Tutorial viewer' - _state_cls = TutorialViewerState - _data_artist_cls = TutorialLayerArtist - _subset_artist_cls = TutorialLayerArtist - - # Additional attributes for Qt viewers - _options_cls = TutorialViewerStateWidget - _layer_style_widget_cls = TutorialLayerStateWidget - -As mentioned previously, you will need to set up the actual visualization in the -``__init__`` method for the data viewer, and this time you should use the -``setCentralWidget`` method to add your custom Qt visualization to the widget, -e.g.:: - - from matplotlib import pyplot as plt - - def __init__(self, *args, **kwargs): - super(TutorialDataViewer, self).__init__(*args, **kwargs) - self.axes = plt.subplot(1, 1, 1) - self.setCentralWidget(self.axes.figure.canvas) - -Functional example ------------------- - -Let's now take all these pieces and construct a functional example. To try this -out you can simply copy the code below into a ``config.py`` file in the -directory from where you are starting glue. In addition you will also need the -:download:`viewer_state.ui ` file. In `File layout -in glue`_ we discuss how this code are split into different files in glue. - -Note that if you are interested in building a Matplotlib-based viewer, you can -make use of the ``glue.viewers.matplotlib`` sub-package to simplify things -as described in :ref:`matplotlib-qt-viewer`. - -.. literalinclude:: state_viewer/config.py - -Try opening a tabular dataset in glue, drag it onto the canvas area, and select -**Tutorial viewer** - you should now get something that looks like: - -.. image:: state_viewer/tutorial_viewer.png - :width: 600px - :align: center - -File layout in glue -------------------- - -In glue, we split up the classes using the following layout: - -============================ ======================================== -Filename Description -============================ ======================================== -``state.py`` State classes for the viewer and layer -``layer_artist.py`` Layer artist class -``qt/options_widget.ui`` Qt ui file for the viewer state widget -``qt/options_widget.py`` Qt viewer state widget -``qt/layer_style_editor.ui`` Qt ui file for the layer state widget -``qt/layer_style_editor.py`` Qt layer state widget -``qt/data_viewer.py`` Qt data viewer -============================ ======================================== - -You are of course free to organize the files how you wish, but this should help -understand the existing viewers in glue if needed. - -Further reading ---------------- - -To find out how to add tools to your custom viewer, see the -:ref:`custom-toolbars` tutorial, and for information on building a data viewer -with Matplotlib, take a look at the :ref:`matplotlib-qt-viewer` tutorial. diff --git a/doc/customizing_guide/scripts/bball_viewer_1.py b/doc/customizing_guide/scripts/bball_viewer_1.py deleted file mode 100644 index 416ef98ba..000000000 --- a/doc/customizing_guide/scripts/bball_viewer_1.py +++ /dev/null @@ -1,25 +0,0 @@ -from glue import custom_viewer - -from matplotlib.colors import LogNorm - -bball = custom_viewer('Shot Plot', - x='att(x)', - y='att(y)') - - -@bball.plot_data -def show_hexbin(axes, x, y): - axes.hexbin(x, y, - cmap='Purples', - gridsize=40, - norm=LogNorm(), - mincnt=1) - - -@bball.plot_subset -def show_points(axes, x, y, style): - axes.plot(x, y, 'o', - alpha=style.alpha, - mec=style.color, - mfc=style.color, - ms=style.markersize) diff --git a/doc/customizing_guide/scripts/bball_viewer_2.py b/doc/customizing_guide/scripts/bball_viewer_2.py deleted file mode 100644 index b77487c75..000000000 --- a/doc/customizing_guide/scripts/bball_viewer_2.py +++ /dev/null @@ -1,52 +0,0 @@ -from glue import custom_viewer - -from matplotlib.colors import LogNorm -from matplotlib.patches import Circle, Rectangle, Arc -from matplotlib.lines import Line2D - -bball = custom_viewer('Shot Plot', - x='att(x)', - y='att(y)') - - -@bball.plot_data -def show_hexbin(axes, x, y): - axes.hexbin(x, y, - cmap='Purples', - gridsize=40, - norm=LogNorm(), - mincnt=1) - - -@bball.plot_subset -def show_points(axes, x, y, style): - axes.plot(x, y, 'o', - alpha=style.alpha, - mec=style.color, - mfc=style.color, - ms=style.markersize) - - -@bball.setup -def draw_court(axes): - - c = '#777777' - opts = dict(fc='none', ec=c, lw=2) - hoop = Circle((0, 63), radius=9, **opts) - axes.add_patch(hoop) - - box = Rectangle((-6 * 12, 0), 144, 19 * 12, **opts) - axes.add_patch(box) - - inner = Arc((0, 19 * 12), 144, 144, theta1=0, theta2=180, **opts) - axes.add_patch(inner) - - threept = Arc((0, 63), 474, 474, theta1=0, theta2=180, **opts) - axes.add_patch(threept) - - opts = dict(c=c, lw=2) - axes.add_line(Line2D([237, 237], [0, 63], **opts)) - axes.add_line(Line2D([-237, -237], [0, 63], **opts)) - - axes.set_ylim(0, 400) - axes.set_aspect('equal', adjustable='datalim') diff --git a/doc/customizing_guide/scripts/bball_viewer_3.py b/doc/customizing_guide/scripts/bball_viewer_3.py deleted file mode 100644 index 2bc123355..000000000 --- a/doc/customizing_guide/scripts/bball_viewer_3.py +++ /dev/null @@ -1,65 +0,0 @@ -from glue import custom_viewer - -from matplotlib.colors import LogNorm -from matplotlib.patches import Circle, Rectangle, Arc -from matplotlib.lines import Line2D -import numpy as np - -bball = custom_viewer('Shot Plot', - x='att(x)', - y='att(y)', - bins=(10, 100), - hitrate=False, - color=['Reds', 'Purples'], - hit='att(shot_made)') - - -@bball.plot_data -def show_hexbin(axes, x, y, style, - hit, hitrate, color, bins): - if hitrate: - axes.hexbin(x, y, hit, - reduce_C_function=lambda x: np.array(x).mean(), - cmap=color, - gridsize=bins, - mincnt=5) - else: - axes.hexbin(x, y, - cmap=color, - gridsize=bins, - norm=LogNorm(), - mincnt=1) - - -@bball.plot_subset -def show_points(axes, x, y, style): - axes.plot(x, y, 'o', - alpha=style.alpha, - mec=style.color, - mfc=style.color, - ms=style.markersize) - - -@bball.setup -def draw_court(axes): - - c = '#777777' - opts = dict(fc='none', ec=c, lw=2) - hoop = Circle((0, 63), radius=9, **opts) - axes.add_patch(hoop) - - box = Rectangle((-6 * 12, 0), 144, 19 * 12, **opts) - axes.add_patch(box) - - inner = Arc((0, 19 * 12), 144, 144, theta1=0, theta2=180, **opts) - axes.add_patch(inner) - - threept = Arc((0, 63), 474, 474, theta1=0, theta2=180, **opts) - axes.add_patch(threept) - - opts = dict(c=c, lw=2) - axes.add_line(Line2D([237, 237], [0, 63], **opts)) - axes.add_line(Line2D([-237, -237], [0, 63], **opts)) - - axes.set_ylim(0, 400) - axes.set_aspect('equal', adjustable='datalim') diff --git a/doc/customizing_guide/scripts/bball_viewer_4.py b/doc/customizing_guide/scripts/bball_viewer_4.py deleted file mode 100644 index d8f69821f..000000000 --- a/doc/customizing_guide/scripts/bball_viewer_4.py +++ /dev/null @@ -1,70 +0,0 @@ -from glue import custom_viewer - -from matplotlib.colors import LogNorm -from matplotlib.patches import Circle, Rectangle, Arc -from matplotlib.lines import Line2D -import numpy as np - -bball = custom_viewer('Shot Plot', - x='att(x)', - y='att(y)', - bins=(10, 100), - hitrate=False, - color=['Reds', 'Purples'], - hit='att(shot_made)') - - -@bball.select -def select(roi, x, y): - return roi.contains(x, y) - - -@bball.plot_data -def show_hexbin(axes, x, y, - hit, hitrate, color, bins): - if hitrate: - axes.hexbin(x, y, hit, - reduce_C_function=lambda x: np.array(x).mean(), - cmap=color, - gridsize=bins, - mincnt=5) - else: - axes.hexbin(x, y, - cmap=color, - gridsize=bins, - norm=LogNorm(), - mincnt=1) - - -@bball.plot_subset -def show_points(axes, x, y, style): - axes.plot(x, y, 'o', - alpha=style.alpha, - mec=style.color, - mfc=style.color, - ms=style.markersize) - - -@bball.setup -def draw_court(axes): - - c = '#777777' - opts = dict(fc='none', ec=c, lw=2) - hoop = Circle((0, 63), radius=9, **opts) - axes.add_patch(hoop) - - box = Rectangle((-6 * 12, 0), 144, 19 * 12, **opts) - axes.add_patch(box) - - inner = Arc((0, 19 * 12), 144, 144, theta1=0, theta2=180, **opts) - axes.add_patch(inner) - - threept = Arc((0, 63), 474, 474, theta1=0, theta2=180, **opts) - axes.add_patch(threept) - - opts = dict(c=c, lw=2) - axes.add_line(Line2D([237, 237], [0, 63], **opts)) - axes.add_line(Line2D([-237, -237], [0, 63], **opts)) - - axes.set_ylim(0, 400) - axes.set_aspect('equal', adjustable='datalim') diff --git a/doc/customizing_guide/scripts/bball_viewer_class.py b/doc/customizing_guide/scripts/bball_viewer_class.py deleted file mode 100644 index 50e6dfb82..000000000 --- a/doc/customizing_guide/scripts/bball_viewer_class.py +++ /dev/null @@ -1,72 +0,0 @@ -from glue.viewers.custom.qt import CustomViewer - -from glue.core.subset import RoiSubsetState - -from matplotlib.colors import LogNorm -from matplotlib.patches import Circle, Rectangle, Arc -from matplotlib.lines import Line2D -import numpy as np - - -class BBall(CustomViewer): - name = 'Shot Plot' - x = 'att(x)' - y = 'att(y)' - bins = (10, 100) - hitrate = False - color = ['Reds', 'Purples'] - hit = 'att(shot_made)' - - def make_selector(self, roi, x, y): - - state = RoiSubsetState() - state.roi = roi - state.xatt = x.id - state.yatt = y.id - - return state - - def plot_data(self, axes, x, y, - hit, hitrate, color, bins): - if hitrate: - axes.hexbin(x, y, hit, - reduce_C_function=lambda x: np.array(x).mean(), - cmap=color, - gridsize=bins, - mincnt=5) - else: - axes.hexbin(x, y, - cmap=color, - gridsize=bins, - norm=LogNorm(), - mincnt=1) - - def plot_subset(self, axes, x, y, style): - axes.plot(x, y, 'o', - alpha=style.alpha, - mec=style.color, - mfc=style.color, - ms=style.markersize) - - def setup(self, axes): - - c = '#777777' - opts = dict(fc='none', ec=c, lw=2) - hoop = Circle((0, 63), radius=9, **opts) - axes.add_patch(hoop) - - box = Rectangle((-6 * 12, 0), 144, 19 * 12, **opts) - axes.add_patch(box) - - inner = Arc((0, 19 * 12), 144, 144, theta1=0, theta2=180, **opts) - axes.add_patch(inner) - - threept = Arc((0, 63), 474, 474, theta1=0, theta2=180, **opts) - axes.add_patch(threept) - - opts = dict(c=c, lw=2) - axes.add_line(Line2D([237, 237], [0, 63], **opts)) - axes.add_line(Line2D([-237, -237], [0, 63], **opts)) - - axes.set_ylim(0, 400) - axes.set_aspect('equal', adjustable='datalim') diff --git a/doc/customizing_guide/scripts/config_link_example.py b/doc/customizing_guide/scripts/config_link_example.py deleted file mode 100644 index e233463da..000000000 --- a/doc/customizing_guide/scripts/config_link_example.py +++ /dev/null @@ -1,6 +0,0 @@ -from glue.config import link_function - - -@link_function(info="Link from deg to rad", output_labels=['rad']) -def deg_to_rad(deg): - return deg * 3.14159 / 180 diff --git a/doc/customizing_guide/scripts/coord_convert.py b/doc/customizing_guide/scripts/coord_convert.py deleted file mode 100644 index 628a555d0..000000000 --- a/doc/customizing_guide/scripts/coord_convert.py +++ /dev/null @@ -1,13 +0,0 @@ -from glue.config import link_function - - -@link_function(info="Celsius to Fahrenheit", - output_labels=['F']) -def celsius2farhenheit(c): - return c * 9. / 5. + 32 - - -@link_function(info="Fahrenheit to Celsius", - output_labels=['C']) -def farhenheit2celsius(f): - return (f - 32) * 5. / 9. diff --git a/doc/customizing_guide/scripts/emcee_plugin.py b/doc/customizing_guide/scripts/emcee_plugin.py deleted file mode 100644 index 12fff1dc7..000000000 --- a/doc/customizing_guide/scripts/emcee_plugin.py +++ /dev/null @@ -1,89 +0,0 @@ -from glue.core.fitters import BaseFitter1D -from glue.core.simpleforms import IntOption -from glue.config import fit_plugin - -import numpy as np -import emcee - - -def gaussian(x, mean, amplitude, stddev): - return np.exp(-(x - mean) ** 2 / (2 * stddev ** 2)) * amplitude - - -def lnprob(param, x, y, dy): - # mean, amplitude, stddev = param - if param[-1] < 0: - return -np.inf - yp = gaussian(x, *param) - diff = (y - yp) - if dy is not None: - diff /= dy - return -np.dot(diff, diff) - - -@fit_plugin -class EmceeGaussianFitter(BaseFitter1D): - label = "Emcee Gaussian" - walkers = IntOption(label="Walkers", min=1, max=200, default=50) - burnin = IntOption(label="Burn in steps", min=1, max=10000, default=500) - steps = IntOption(label="Steps", min=10, max=10000, default=500) - - def fit(self, x, y, dy, constraints, - walkers=50, burnin=500, steps=500): - ndim = 3 - # initialize walker parameters - amp = y.max() - mean = (x * y).sum() / y.sum() - var = ((x - mean) ** 2 * y).sum() / y.sum() - p0 = np.array([amp, mean, np.sqrt(var)]).reshape(1, -1) - p0 = np.random.lognormal(sigma=.1, size=(walkers, ndim)) * p0 - sampler = emcee.EnsembleSampler(walkers, ndim, lnprob, - args=[x, y, dy]) - - # burnin - pos, prob, state = sampler.run_mcmc(p0, burnin) - sampler.reset() - - # run - sampler.run_mcmc(pos, steps) - return sampler - - def predict(self, fit_result, x): - chain = fit_result.flatchain - params = np.mean(chain, axis=0) - return gaussian(x, *params) - - def summarize(self, fit_result, x, y, dy): - af = fit_result.acceptance_fraction.mean() - chain = fit_result.flatchain - amp, mean, sigma = chain.mean(axis=0) - damp, dmean, dsigma = np.std(chain, axis=0) - walkers, steps, dim = fit_result.chain.shape - - result = [ - "Walkers: %i" % walkers, - "Steps: %i" % steps, - "Acceptance fraction: %0.2f" % af, - "-------------------------", - "amplitude = %0.3e +/- %0.1e" % (amp, damp), - "mean = %0.3e +/- %0.1e" % (mean, dmean), - "stddev = %0.3e +/- %0.1e" % (sigma, dsigma) - ] - return '\n'.join(result) - - def plot(self, fit_result, axes, x): - chain = fit_result.flatchain - result = [] - - # background samples - for i in range(100): - row = np.random.randint(0, chain.shape[0]) - params = chain[row] - y = gaussian(x, *params) - result.extend(axes.plot(x, y, 'k', alpha=.08)) - - # foreground prediction of posterior mean model - result.extend( - super(EmceeGaussianFitter, self).plot(fit_result, axes, x)) - - return result diff --git a/doc/customizing_guide/scripts/line_fit_plugin.py b/doc/customizing_guide/scripts/line_fit_plugin.py deleted file mode 100644 index f48705983..000000000 --- a/doc/customizing_guide/scripts/line_fit_plugin.py +++ /dev/null @@ -1,14 +0,0 @@ -from glue.core.fitters import BaseFitter1D -from glue.config import fit_plugin -import numpy as np - - -@fit_plugin -class LineFit(BaseFitter1D): - label = "Line" - - def fit(self, x, y, dy, constraints): - return np.polyfit(x, y, 1) - - def predict(self, fit_result, x): - return np.polyval(fit_result, x) diff --git a/doc/customizing_guide/scripts/poly_fit_plugin.py b/doc/customizing_guide/scripts/poly_fit_plugin.py deleted file mode 100644 index a6f3897c9..000000000 --- a/doc/customizing_guide/scripts/poly_fit_plugin.py +++ /dev/null @@ -1,20 +0,0 @@ -from glue.core.fitters import BaseFitter1D -from glue.core.simpleforms import IntOption -from glue.config import fit_plugin -import numpy as np - - -@fit_plugin -class PolynomialFitter(BaseFitter1D): - label = "Polynomial" - degree = IntOption(min=0, max=5, default=3, label="Polynomial Degree") - - def fit(self, x, y, dy, constraints, degree=2): - return np.polyfit(x, y, degree) - - def predict(self, fit_result, x): - return np.polyval(fit_result, x) - - def summarize(self, fit_result, x, y, dy=None): - return "Coefficients:\n" + "\n".join("%e" % coeff - for coeff in fit_result.tolist()) diff --git a/doc/customizing_guide/state_viewer/config.py b/doc/customizing_guide/state_viewer/config.py deleted file mode 100644 index b59efd468..000000000 --- a/doc/customizing_guide/state_viewer/config.py +++ /dev/null @@ -1,153 +0,0 @@ -import os - -import numpy as np -import matplotlib -matplotlib.use('Qt5Agg') -from matplotlib import pyplot as plt -from qtpy.QtWidgets import QWidget, QVBoxLayout, QCheckBox - -from glue.config import qt_client -from glue.core.data_combo_helper import ComponentIDComboHelper - -from echo import CallbackProperty, SelectionCallbackProperty -from echo.qt import connect_checkable_button, autoconnect_callbacks_to_qt - -from glue.viewers.common.layer_artist import LayerArtist -from glue.viewers.common.state import ViewerState, LayerState -from glue.viewers.common.qt.data_viewer import DataViewer - -from glue.utils.qt import load_ui - - -class TutorialViewerState(ViewerState): - - x_att = SelectionCallbackProperty(docstring='The attribute to use on the x-axis') - y_att = SelectionCallbackProperty(docstring='The attribute to use on the y-axis') - - def __init__(self, *args, **kwargs): - super(TutorialViewerState, self).__init__(*args, **kwargs) - self._x_att_helper = ComponentIDComboHelper(self, 'x_att') - self._y_att_helper = ComponentIDComboHelper(self, 'y_att') - self.add_callback('layers', self._on_layers_change) - - def _on_layers_change(self, value): - # self.layers_data is a shortcut for - # [layer_state.layer for layer_state in self.layers] - self._x_att_helper.set_multiple_data(self.layers_data) - self._y_att_helper.set_multiple_data(self.layers_data) - - -class TutorialLayerState(LayerState): - fill = CallbackProperty(False, docstring='Whether to show the markers as filled or not') - - -class TutorialLayerArtist(LayerArtist): - - _layer_state_cls = TutorialLayerState - - def __init__(self, axes, *args, **kwargs): - - super(TutorialLayerArtist, self).__init__(*args, **kwargs) - - self.axes = axes - - self.artist = self.axes.plot([], [], 'o', color=self.state.layer.style.color)[0] - - self.state.add_callback('fill', self._on_fill_change) - self.state.add_callback('visible', self._on_visible_change) - self.state.add_callback('zorder', self._on_zorder_change) - - self._viewer_state.add_callback('x_att', self._on_attribute_change) - self._viewer_state.add_callback('y_att', self._on_attribute_change) - - def _on_fill_change(self, value=None): - if self.state.fill: - self.artist.set_markerfacecolor(self.state.layer.style.color) - else: - self.artist.set_markerfacecolor('none') - self.redraw() - - def _on_visible_change(self, value=None): - self.artist.set_visible(self.state.visible) - self.redraw() - - def _on_zorder_change(self, value=None): - self.artist.set_zorder(self.state.zorder) - self.redraw() - - def _on_attribute_change(self, value=None): - - if self._viewer_state.x_att is None or self._viewer_state.y_att is None: - return - - x = self.state.layer[self._viewer_state.x_att] - y = self.state.layer[self._viewer_state.y_att] - - self.artist.set_data(x, y) - - self.axes.set_xlim(np.nanmin(x), np.nanmax(x)) - self.axes.set_ylim(np.nanmin(y), np.nanmax(y)) - - self.redraw() - - def clear(self): - self.artist.set_visible(False) - - def remove(self): - self.artist.remove() - - def redraw(self): - self.axes.figure.canvas.draw_idle() - - def update(self): - self._on_fill_change() - self._on_attribute_change() - - -class TutorialViewerStateWidget(QWidget): - - def __init__(self, viewer_state=None, session=None): - - super(TutorialViewerStateWidget, self).__init__() - - self.ui = load_ui('viewer_state.ui', self, - directory=os.path.dirname(__file__)) - - self.viewer_state = viewer_state - self._connections = autoconnect_callbacks_to_qt(self.viewer_state, self.ui) - - -class TutorialLayerStateWidget(QWidget): - - def __init__(self, layer_artist): - - super(TutorialLayerStateWidget, self).__init__() - - self.checkbox = QCheckBox('Fill markers') - layout = QVBoxLayout() - layout.addWidget(self.checkbox) - self.setLayout(layout) - - self.layer_state = layer_artist.state - self._connection = connect_checkable_button(self.layer_state, 'fill', self.checkbox) - - -class TutorialDataViewer(DataViewer): - - LABEL = 'Tutorial viewer' - _state_cls = TutorialViewerState - _options_cls = TutorialViewerStateWidget - _layer_style_widget_cls = TutorialLayerStateWidget - _data_artist_cls = TutorialLayerArtist - _subset_artist_cls = TutorialLayerArtist - - def __init__(self, *args, **kwargs): - super(TutorialDataViewer, self).__init__(*args, **kwargs) - self.axes = plt.subplot(1, 1, 1) - self.setCentralWidget(self.axes.figure.canvas) - - def get_layer_artist(self, cls, layer=None, layer_state=None): - return cls(self.axes, self.state, layer=layer, layer_state=layer_state) - - -qt_client.add(TutorialDataViewer) diff --git a/doc/customizing_guide/state_viewer/tutorial_viewer.png b/doc/customizing_guide/state_viewer/tutorial_viewer.png deleted file mode 100644 index 3e6ca696e..000000000 Binary files a/doc/customizing_guide/state_viewer/tutorial_viewer.png and /dev/null differ diff --git a/doc/customizing_guide/state_viewer/viewer_state.ui b/doc/customizing_guide/state_viewer/viewer_state.ui deleted file mode 100644 index 1e6308dc5..000000000 --- a/doc/customizing_guide/state_viewer/viewer_state.ui +++ /dev/null @@ -1,33 +0,0 @@ - - - Form - - - Form - - - - - - x attribute - - - - - - - - - - y attribute - - - - - - - - - - - diff --git a/doc/customizing_guide/toolbar.rst b/doc/customizing_guide/toolbar.rst deleted file mode 100644 index ed9780b27..000000000 --- a/doc/customizing_guide/toolbar.rst +++ /dev/null @@ -1,235 +0,0 @@ -.. _custom-toolbars: - -Custom tools for viewers and custom toolbars -============================================ - -Writing a custom tool for a viewer toolbar ------------------------------------------- - -Here we take a look at how to create a tool to include in a viewer's toolbar -(either one of the built-in viewers or a custom viewer) There are two types of -tools: ones that can be checked and unchecked, and ones that simply trigger an -event when pressed, but do not remain pressed. These are described in the -following two sub-sections. - -Non-checkable tools -^^^^^^^^^^^^^^^^^^^ - -The basic structure for a non-checkable tool is: - -.. code:: python - - from glue.config import viewer_tool - from glue.viewers.common.tool import Tool - - @viewer_tool - class MyCustomTool(Tool): - - icon = 'myicon.png' - tool_id = 'custom_tool' - action_text = 'Does cool stuff' - tool_tip = 'Does cool stuff' - shortcut = 'D' - - def __init__(self, viewer): - super(MyCustomMode, self).__init__(viewer) - - def activate(self): - pass - - def close(self): - pass - -The class-level variables set at the start of the class are as follows: - -* ``icon``: this should be set either to the name of a built-in glue icon, or - to the path to a PNG file to be used for the icon. Note that this should - **not** be a ``QIcon`` object. - -* ``tool_id``: a unique string that identifies this tool. If you create a - tool that has the same ``tool_id`` as an existing tool already implemented in - glue, you will get an error. - -* ``action_text``: a string describing the tool. This is not currently used, - but would be the text that would appear if the tool was accessible by a menu. - -* ``tool_tip``: this should be a string that will be shown when the user hovers - above the button in the toolbar. This can include instructions on how to use - the tool. - -* ``shortcut``: this should be a string giving a key that the user can press - when the viewer is active, which will activate the tool. This can include - modifier keys, e.g. ``'Ctrl+A'`` or ``'Ctrl+Shift+U'``, but can also just be - a single key, e.g. ``'K'``. If present, the shortcut is added at the end of - the tooltip. If multiple tools in a viewer have the same shortcut, a warning - will be emitted, and only the first tool registered with a particular - shortcut will be accessible with that shortcut. - -When the user presses the tool icon, the ``activate`` method is called. In this -method, you can write any code including code that may for example open a Qt -window, or change the state of the viewer (for example changing the zoom or -field of view). You can access the viewer instance with ``self.viewer``. -Finally, when the viewer is closed the ``close`` method is called, so you should -use this to do any necessary cleanup. - -The ``@viewer_tool`` decorator tells glue that this class represents a viewer -tool, and you will then be able to add the tool to any viewers (see -:ref:`toolbar_content`) using the ``tool_id``. - -Checkable tools -^^^^^^^^^^^^^^^ - -The basic structure for a checkable tool is similar to the above, but with an -additional ``deactivate`` method, and a ``status_tip`` attribute: - -.. code:: python - - from glue.config import viewer_tool - from glue.viewers.common.tool import CheckableTool - - @viewer_tool - class MyCustomButton(CheckableTool): - - icon = 'myicon.png' - tool_id = 'custom_tool' - action_text = 'Does cool stuff' - tool_tip = 'Does cool stuff' - status_tip = 'Instructions on what to do now' - shortcut = 'D' - - def __init__(self, viewer): - super(MyCustomMode, self).__init__(viewer) - - def activate(self): - pass - - def deactivate(self): - pass - - def close(self): - pass - -When the tool icon is pressed, the ``activate`` method is called, and when the -button is unchecked (either by clicking on it again, or if the user clicks on -another tool icon), the ``deactivate`` method is called. As before, when the -viewer is closed, the ``close`` method is called. The ``status_tip`` is a -message shown in the status bar of the viewer when the tool is active. This can -be used to provide instructions to the user as to what they should do next. - -Drop-down menus -^^^^^^^^^^^^^^^ - -For both checkable and non-checkable tools, it is possible to show a menu -when the user clicks on the icon. To do this, simply add a ``menu_actions`` -method to your class: - -.. code:: python - - def menu_actions(self): - return [] - -This method should return a list of ``QActions`` which can include e.g. icons, -text, and callbacks. - -.. note:: In future, we will allow this to be done in a way that - does not rely on Qt QActions. - -.. _toolbar_content: - -Customizing the content of a toolbar ------------------------------------- - -When defining a tool as above, the ``@viewer_tool`` decorator ensures that -the tool is registered with glue, but does not add it to any specific viewer. -Which buttons are shown for a viewer is controlled by the ``tools`` class-level -attribute on viewers: - -.. code:: python - - >>> from glue.viewers.image.qt import ImageViewer - >>> ImageViewer.tools - ['select:rectangle', 'select:xrange', 'select:yrange', - 'select:circle', 'select:polygon', 'image:colormap'] - -The strings in the ``tools`` list correspond to the ``tool_id`` attribute on the -tool classes. If you want to add an existing or custom button to a viewer, you -can therefore simply do e.g.: - -.. code:: python - - from glue.viewers.image.qt import ImageViewer - ImageViewer.tools.append('custom_tool') - -Including toolbars in custom viewers ------------------------------------- - -When defining a data viewer (as described in :ref:`state-qt-viewer`), it -is straightforward to add a toolbar that can then be used to add tools. To do -this, when defining your -:class:`~glue.viewers.common.qt.data_viewer.DataViewer` subclass, -you should also specify the ``_toolbar_cls`` and ``tools`` class-level -attributes, which should give the class to use for the toolbar, and the default -tools that should be present in the toolbar: - -.. code:: python - - from glue.viewers.common.qt.data_viewer import DataViewer - from glue.viewers.common.qt.toolbar import BasicToolbar - - class MyViewer(DataViewer): - - _toolbar_cls = BasicToolbar - tools = ['custom_tool'] - -In the example above, the viewer will include an toolbar with one tool (the one -we defined above). Currently the only toolbar class that is defined -is :class:`~glue.viewers.common.qt.toolbar.BasicToolbar`. - -Note that the toolbar is set up after ``__init__`` has run. Therefore, if you -want to do any custom set-up to the toolbar after it has been set up, you -should overload the ``initialize_toolbar`` method, e.g: - -.. code:: python - - class MyViewer(DataViewer): - - _toolbar_cls = BasicToolbar - tools = ['custom_tool'] - - def initialize_toolbar(self): - super(MyViewer, self).initialize_toolbar() - # custom code here - -In ``initialize_toolbar`` (and elsewhere in the class) you can then access the -tool instances using ``self.toolbar.tools`` (which is a dictionary where each -key is a ``tool_id``). - -By default, tools are inherited from parent classes, but this can be controlled -using the ``inherit_tools`` class-level attribute - for example, the following -will result in only the ``custom_tool`` being available, and nothing else: - -.. code:: python - - class MyImageViewer(ImageViewer): - - tools = ['custom_tool'] - inherit_tools = False - -Available tools ---------------- - -The following tools are available by default (note that not all tools can be -used in all viewers, click on each tool class name to find out more): - -====================== ======================================================== -Tool ID Class -====================== ======================================================== -``'select:circle'`` :class:`~glue.viewers.matplotlib.toolbar_mode.CircleMode` -``'select:pick'`` :class:`~glue.viewers.matplotlib.toolbar_mode.PickMode` -``'select:polygon'`` :class:`~glue.viewers.matplotlib.toolbar_mode.PolyMode` -``'select:rectangle'`` :class:`~glue.viewers.matplotlib.toolbar_mode.RectangleMode` -``'select:xrange'`` :class:`~glue.viewers.matplotlib.toolbar_mode.HRangeMode` -``'select:yange'`` :class:`~glue.viewers.matplotlib.toolbar_mode.VRangeMode` -``'image:colormap'`` :class:`~glue.viewers.matplotlib.qt.toolbar_mode.ColormapMode` -``'image:contrast'`` :class:`~glue.viewers.matplotlib.qt.toolbar_mode.ContrastMode` -====================== ======================================================== diff --git a/doc/customizing_guide/units.rst b/doc/customizing_guide/units.rst deleted file mode 100644 index 48c172308..000000000 --- a/doc/customizing_guide/units.rst +++ /dev/null @@ -1,71 +0,0 @@ -Unit conversion in glue -======================= - -.. note:: Support for automatic unit conversion in glue is experimental - at the moment - the ability to select units for the x and y axes is only available in the profile viewer. - -Data components can be assigned units as a string (or `None` to indicate no known units). -By default, glue uses `astropy.units `_ package -to carry out unit conversions based on these units. However, it is possible to customize the -unit conversion machinery, either to use a different unit transformation machinery, or to specify, -e.g., equivalencies in the astropy unit conversion. To customize the unit conversion behavior, you -will need to define a unit converter as shown below:: - - from astropy import units as u - from glue.core.units import unit_converter - - @unit_converter('custom-name') - class MyCustomUnitConverter: - - def equivalent_units(self, data, cid, units): - # Given a glue data object (data), a component ID (cid), and the units - # of that component in the data object (units), this method should - # return a flat list of units (as strings) that the data could be - # converted to. This is used to construct the drop-down menus with the - # available units to convert to. - - def to_unit(self, data, cid, values, original_units, target_units): - # Given a glue data object (data), a component ID (cid), the values - # to convert, and the original and target units of the values, this method - # should return the converted values. Note that original_units - # gives the units of the values array, which might not be the same - # as the original native units of the component in the data. - -In both methods, the data and cid are passed in not to get values or units (those should be -used from the other arguments to the methods) but rather to allow logic for the unit -conversion that might depend on which component is being converted. An example of -a simple unit converter based on `astropy.units`_ would be:: - - from astropy import units as u - from glue.core.units import unit_converter - - @unit_converter('example-1') - class ExampleUnitConverter: - - def equivalent_units(self, data, cid, units): - return map(str, u.Unit(units).find_equivalent_units(include_prefix_units=True)) - - def to_unit(self, data, cid, values, original_units, target_units): - return (values * u.Unit(original_units)).to_value(target_units) - -This does not actually make use of ``data`` and ``cid``. An example that does would be:: - - from astropy import units as u - from glue.core.units import unit_converter - - @unit_converter('example-2') - class ExampleUnitConverter: - - def equivalent_units(self, data, cid, units): - equivalencies = u.temperature() if 'temp' in cid.label.lower() else None - return map(str, u.Unit(units).find_equivalent_units(include_prefix_units=True, equivalencies=equivalencies)) - - def to_unit(self, data, cid, values, original_units, target_units): - equivalencies = u.temperature() if 'temp' in cid.label.lower() else None - return (values * u.Unit(original_units)).to_value(target_units, equivalencies=equivalencies) - -Once you have defined a unit conversion class, you can then opt-in to using it in glue by adjusting -the following setting:: - - from glue.config import settings - settings.UNIT_CONVERTER = 'example-2' diff --git a/doc/customizing_guide/viewer.rst b/doc/customizing_guide/viewer.rst deleted file mode 100644 index f8d254744..000000000 --- a/doc/customizing_guide/viewer.rst +++ /dev/null @@ -1,320 +0,0 @@ -.. _state-viewer: - -Writing a custom viewer for glue -================================ - -Motivation ----------- - -The simple way of defining new custom viewers described in :doc:`custom_viewer` -are well-suited to developing new custom viewers that include simple Matplotlib -plots, and for now is limited to Qt-based viewers. But in some cases, you may -want to write a data viewer with more customized functionality, or that doesn't -depend on Matplotlib or Qt and may use an existing third-party widget. - -In this tutorial, we will take a look at the pieces needed to build a data -viewer. The sections here are relevant regardless of whether you are building a -data viewer for e.g. Qt or Jupyter. If you are interested in building a Qt-based -viewer, you can then proceed to :ref:`state-qt-viewer`. If you are interested in -building a Matplotlib-based Qt viewer, you can then also make use of the -``glue.viewers.matplotlib`` sub-package to simplify things as described in -:ref:`matplotlib-qt-viewer`. - -Terminology ------------ - -When we talk about a *data viewer*, we mean specifically one of the -visualizations in glue (e.g. scatter plot, histogram, network diagram, etc.). Inside each -visualization, there may be multiple datasets or subsets shown. For example, a -dataset might be shown as markers of a certain color, while a subset might be -shown in a different color. We refer to these as *layers* in the visualization, -and these typically appear in a list on the left of the glue application window. - -State classes -------------- - -Overview -^^^^^^^^ - -The first piece to construct when developing a new data viewer are *state* -classes for the data viewer and layers, which you can think of as a conceptual -representation of the data viewer and layers, but doesn't contain any code -specific to e.g. Qt or Jupyter or even the visualization library you are using. -As an example, a scatter viewer will have a state class that indicates which -attributes are shown on which axes, and what the limits of the axes are. Each -layer then also has a state class which includes information for example about -what the color of the layer should be, and whether it is currently visible or -not. - -Viewer state -^^^^^^^^^^^^ - -To create a viewer, we import the base -:class:`~glue.viewers.common.state.ViewerState` class, as well as the -:class:`~echo.CallbackProperty` class:: - - from glue.viewers.common.state import ViewerState - from echo import CallbackProperty - -The latter is used to define properties on the state class and we can attach -callback functions to them (more on this soon). Let's now imagine we want to -build a simple scatter plot viewer. Our state class would then look like:: - - class TutorialViewerState(ViewerState): - x_att = CallbackProperty(docstring='The attribute to use on the x-axis') - y_att = CallbackProperty(docstring='The attribute to use on the y-axis') - -Once a state class is defined with callback properties, it is possible to -attach callback functions to them:: - - >>> def on_x_att_change(value): - ... print('x_att has changed and is now', value) - >>> state = TutorialViewerState() - >>> state.add_callback('x_att', on_x_att_change) - >>> state.x_att = 'a' - x_att has changed and is now a - -What this means is that when you are defining the state class for your viewer, -think about whether you want to change certain properties based on others. For -example we could write a state class that changes x to match y (but not y to -match x):: - - class TutorialViewerState(ViewerState): - - x_att = CallbackProperty(docstring='The attribute to use on the x-axis') - y_att = CallbackProperty(docstring='The attribute to use on the y-axis') - - def __init__(self, *args, **kwargs): - super(TutorialViewerState).__init__(*args, **kwargs) - self.add_callback('y_att', self._on_y_att_change) - - def _on_y_att_change(self, value): - self.x_att = self.y_att - -The idea is to implement as much of the logic as possible here rather than -relying on e.g. Qt events, so that your class can be re-used for e.g. both a Qt -and Jupyter data viewer. - -Note that the :class:`~glue.viewers.common.state.ViewerState` defines one -property by default, which is ``layers`` - a container that is used to store -:class:`~glue.viewers.common.state.LayerState` objects (see `Layer state`_). -You shouldn't need to add/remove layers from this manually, but you can attach -callback functions to ``layers`` in case any of the layers change. - -Layer state -^^^^^^^^^^^ - -Similarly to the viewer state, you need to also define a state class for -layers in the visualization using :class:`~glue.viewers.common.state.LayerState`:: - - from glue.viewers.common.state import LayerState - -The :class:`~glue.viewers.common.state.LayerState` class defines the following -properties by default: - -* ``layer``: the :class:`~glue.core.data.Data` or :class:`~glue.core.subset.Subset` - attached to the layer (the naming of this property is historical/confusing and - may be changed to ``data`` in future). -* ``visible``: whether the layer is visible or not -* ``zorder``: a numerical value indicating (when relevant) which layer should - appear in front of which (higher numbers mean the layer should be shown more - in the foreground) - -Furthermore, ``layer.style`` is itself a state class that includes global -settings for the data or subset, such as ``color`` and ``alpha``. - -Let's say that you want to define a way to indicate in the layer whether to -use filled markers or not - this is not one of the settings in ``layer.style``, -so you can define it using:: - - class TutorialLayerState(LayerState): - fill = CallbackProperty(False, docstring='Whether to show the markers as filled or not') - -The optional first value in :class:`~echo.CallbackProperty` is the -default value that the property should be set to. - -Multi-choice properties -^^^^^^^^^^^^^^^^^^^^^^^ - -In some cases, you might want the properties on the state classes to be a -selection from a fixed set of values -- for instance line style, or as -demonstrated in `Viewer State`_, the attribute to show on an axis (since -it should be chosen from the existing data attributes). This can be -done by using the :class:`~echo.SelectionCallbackProperty` class, -which should be used as follows:: - - class TutorialViewerState(ViewerState): - - linestyle = SelectionCallbackProperty() - - def __init__(self, *args, **kwargs): - super(TutorialViewerState).__init__(*args, **kwargs) - MyExampleState.linestyle.set_choices(['solid', 'dashed', 'dotted']) - -This then makes it so that the ``linestyle`` property knows about what valid -values are, and this will come in useful when developing for example Qt widgets -so that they can automatically populate combo/selection boxes for example. - -For the specific case of selecting attributes from the data, we also provide a -class :class:`~glue.core.data_combo_helper.ComponentIDComboHelper` that can -automatically keep the attributes for datasets in sync with the choices in a -:class:`~echo.SelectionCallbackProperty` class. Here's an example -of how to use it:: - - class TutorialViewerState(ViewerState): - - x_att = SelectionCallbackProperty(docstring='The attribute to use on the x-axis') - y_att = SelectionCallbackProperty(docstring='The attribute to use on the y-axis') - - def __init__(self, *args, **kwargs): - super(TutorialViewerState, self).__init__(*args, **kwargs) - self._x_att_helper = ComponentIDComboHelper(self, 'x_att') - self._y_att_helper = ComponentIDComboHelper(self, 'y_att') - self.add_callback('layers', self._on_layers_change) - - def _on_layers_change(self, value): - # self.layers_data is a shortcut for - # [layer_state.layer for layer_state in self.layers] - self._x_att_helper.set_multiple_data(self.layers_data) - self._y_att_helper.set_multiple_data(self.layers_data) - -Now whenever layers are added/removed, the choices for ``x_att`` and ``y_att`` -will automatically be updated. - -Layer artist ------------- - -In the previous section, we saw that we can define classes to hold the -conceptual state of viewers and of the layers in the viewers. The next -type of class we are going to look at is the *layer artist*. - -Conceptually, layer artists can be used to carry out the actual drawing and -include any logic about how to convert data and subsets into layers in your -visualization. - -The minimal layer artist class looks like the following:: - - from glue.viewers.common.layer_artist import LayerArtist - - class TutorialLayerArtist(LayerArtist): - - _layer_artist_cls = TutorialLayerState - - def clear(self): - pass - - def remove(self): - pass - - def redraw(self): - pass - - def update(self): - pass - -Each layer artist class has to define the four methods shown above. The -:meth:`~glue.viewers.common.layer_artist.LayerArtist.clear` method -should remove the layer from the visualization, bearing in mind -that the layer might be added back (this can happen for example when toggling -the visibility of the layer property), the -:meth:`~glue.viewers.common.layer_artist.LayerArtist.remove` method -should permanently remove the layer from the visualization, the -:meth:`~glue.viewers.common.layer_artist.LayerArtist.redraw` method -should force the layer to be redrawn, and -:meth:`~glue.viewers.common.layer_artist.LayerArtist.update` should -update the appearance of the layer as necessary before redrawing -- note that -:meth:`~glue.viewers.common.layer_artist.LayerArtist.update` is called -for example when a subset has changed. - -By default, layer artists inheriting from -:class:`~glue.viewers.common.layer_artist.LayerArtist` will be -initialized with a reference to the layer state (accessible as ``state``) and -the viewer state (accessible as ``_viewer_state``). - -This means that we can then do the following, assuming a layer state -with the ``fill`` property defined previously:: - - from glue.viewers.common.layer_artist import LayerArtist - - class TutorialLayerArtist(LayerArtist): - - _layer_artist_cls = TutorialLayerState - - def __init__(self, *args, **kwargs): - super(MyLayerArtist, self).__init__(*args, **kwargs) - self.state.add_callback('fill', self._on_fill_change) - - def _on_fill_change(self): - # Make adjustments to the visualization layer here - -In practice, you will likely need a reference to the overall visualization to -be passed to the layer artist (for example the axes for a Matplotlib plot, -or an OpenGL canvas). We will take a look at this after introducing the data -viewer class in `Data viewer`_. - -Note that the layer artist doesn't have to be specific to the front-end used -either. If for instance you are developing a widget based on e.g. -Matplotlib, and are then developing a Qt and Jupyter version of the viewer, -you could write the layer artist in such a way that it only cares about the -Matplotlib API and works for either the Qt or Jupyter viewers. - -Data viewer ------------ - -We have now seen how to define state classes for the viewer and layer, and layer -artists. The final piece of the puzzle is the data viewer class itself, which -brings everything together. The simplest definition of the data viewer class -is:: - - from glue.viewers.common.viewer import Viewer - - class TutorialDataViewer(Viewer): - - LABEL = 'Tutorial viewer' - _state_cls = TutorialViewerState - _data_artist_cls = TutorialLayerArtist - _subset_artist_cls = TutorialLayerArtist - -In practice, this isn't enough, since we need to actually set up the main -visualization and pass references to it to the layer artists. This can be -done in the initializer of the ``TutorialDataViewer`` class. For example, -if you were building a Matplotlib-based viewer, assuming you imported Matplotlib -as:: - - from matplotlib import pyplot as plt - -you could do:: - - def __init__(self, *args, **kwargs): - super(TutorialDataViewer, self).__init__(*args, **kwargs) - self.axes = plt.subplot(1, 1, 1) - -Note however that you need a way to pass the axes to the layer artist. The way -to do this is to add ``axes`` as a positional argument for the -``TutorialLayerArtist`` class defined previously then to add the following -method to the data viewer:: - - def get_layer_artist(self, cls, layer=None, layer_state=None): - return cls(self.axes, self.state, layer=layer, layer_state=layer_state) - -This method defines how the layer artists should be instantiated, and you can -see that we added a ``self.axes`` positional argument, so that the layer artist -classes should now have access to the axes. - -With this in place, what will happen now is that when a data viewer is created, -and when a new dataset or subset is added to it, the ``layers`` attribute of -the viewer state class will automatically be updated to include a new -:class:`~glue.viewers.common.state.LayerState` object. At the same time, -a :class:`~glue.viewers.common.layer_artist.LayerArtist` object will be -instantiated. The main task is therefore to implement the methods for the -:class:`~glue.viewers.common.layer_artist.LayerArtist` (in particular -:meth:`~glue.viewers.common.layer_artist.LayerArtist.update`). You can then add -any required logic in the state classes if needed. - -Further reading ---------------- - -If you are interested in building a viewer for the Qt front-end of glue, you can -find out more about this and see a complete example in :ref:`state-qt-viewer`. -Even if you want to develop a viewer for a different front-end, you may find -the Qt example useful. diff --git a/doc/customizing_guide/writing_plugin.rst b/doc/customizing_guide/writing_plugin.rst deleted file mode 100644 index cf14648b6..000000000 --- a/doc/customizing_guide/writing_plugin.rst +++ /dev/null @@ -1,170 +0,0 @@ -.. _writing_plugin: - -Distributing your own plugin package -==================================== - -If you are looking to customize glue for your own use, you don't necessarily -need to create a plugin package - instead you can just use a ``config.py`` file -as described in :doc:`configuration`. However, if you are interested in sharing -your customizations with others, then the best approach is to develop and -distribute a plugin package. - -Plugin packages use the same mechanism of registering customizations described -in :ref:`customization` as you would if you were using a ``config.py`` file - -the only real difference is the file structure you will need to use. To make -things easier, we provide a template plugin package at -https://github.com/glue-viz/glue-plugin-template to show you how files should be -organized. - -Required files --------------- - -To start with, any Python code that is part of the plugin package should be -placed in a directory with the name of the module for the plugin. In our -template, this is the ``myplugin`` directory - for some of the real plugins we -have developed in the past, this is for example ``glue_medical`` or -``glue_geospatial``. This directory should contain at least one Python file -that contains the customizations that you would otherwise have put in your -``config.py`` file. In the template example, this is the ``data_viewer.py`` file -which contains a custom data viewer. - -In addition to this file (or multiple files), you will need an ``__init__.py`` -file, which should contain a ``setup`` function. This function is used to -register any customizations with glue. In this function, you should import any -files containing customizations. If the customizations use a decorator to be -registered (e.g. ``@data_factory`` or ``@menubar_plugin``), then you are all set. -Otherwise, for registering e.g. custom_viewers, the ``setup`` function should -also do the registration - in the template, this looks like:: - - def setup(): - from .data_viewer import MyViewer - from glue.config import qt_client - qt_client.add(MyViewer) - -Finally, at the root of the package, you will need a ``setup.py`` file similar -to the one in the template (you can copy it over and edit the relevant parts). -One of the important parts is the definition of ``entry_points``, which is the -part that tells glue that this package is a plugin:: - - entry_points = """ - [glue.plugins] - myplugin=myplugin:setup - """ - -The entry in ``[glue.plugins]`` has the form:: - - plugin_name=module_name:setup_function_name - -In general, to avoid confusion, the ``plugin_name`` and ``module_name`` should -both be set to the name of the directory containing the code (``myplugin`` in -our case). - -Once you have this in place, you should be able to install the plugin in -'develop' mode (meaning that you can then make changes and have them be updated -in the installed version without having to re-install every time) with:: - - pip install -e . - -You can then start up glue with:: - - glue -v - -The startup log then contains information about whether plugins were -successfully loaded. You should either see something like:: - - INFO:glue:Loading plugin myplugin succeeded - -Or if you are unlucky:: - - INFO:glue:Loading plugin myplugin failed (Exception: No module named 'numpyy') - -In the latter case, the exception should help you figure out what went wrong. -For a more detailed error message, you can also just import your plugin package -and run the setup function:: - - python -c 'from myplugin import setup; setup()' - -Optional files --------------- - -The only files that are really required are the directory with the source code -and the ``setup.py`` file - however, you should make sure you also include an -`open source license `_ if you are planning to -distribute the package, as well as a README file that describes your package, -its requirements, and how to install and use it. - -Consider also adding tests (using e.g. the `pytest `_ -framework), as well as setting up continuous integration services such as -`Travis `_ to run the tests any time a change is made. -Describing how to do this is beyond the scope of this tutorial, but there are -plenty of resources online to help you do this. - -Distributing the package ------------------------- - -Since your package follows the standard layout for packages, you can follow the -`Packaging Python Projects `_ -guide to release your package and upload it to PyPI. The usual release process -for glue plugins is as follows: - -#. Start off by editing the changelog (if present) and change the release date - from ``unreleased`` to today's date, in the ``YYYY-MM-DD`` format, for - example:: - - 0.2 (2019-02-04) - ---------------- - -#. Edit the ``version.py`` file in your package to not include the - ``.dev0`` suffix, e.g.:: - - version = '0.2' - -#. Commit the changes, e.g.:: - - git add CHANGES.rst */version.py - git commit -m "Preparing release v0.2" - -#. Remove any uncommitted files/changes with:: - - git clean -fxd - -#. Now create the release with:: - - python setup.py sdist - - Provided you don't have any C extensions in your package, you can also make - a so-called 'wheel' release:: - - python setup.py bdist_wheel --universal - -#. Go inside the ``dist`` directory and use the `twine - `_ tool to upload the files to PyPI:: - - cd dist - twine upload *.tar.gz *.whl - -#. Optionally tag the release in git with:: - - git tag -m v0.2 v0.2 - -#. Add a new section in the changelog file for the next release:: - - 0.3 (unreleased) - ---------------- - - - No changes yet. - - and update the ``version.py`` file to point to the next version, with a - ``.dev0`` suffix:: - - version = '0.3.dev0' - -#. Finally, commit the changes and push to GitHub:: - - git add CHANGES.rst */version.py - git commit -m "Back to development: v0.3" - git push --tags upstream master - -If you are interested in including your package as a conda package in the -``glueviz`` channel, please let us know by opening an issue at -https://github.com/glue-viz/conda-dev. diff --git a/doc/developer_guide/ci_status.png b/doc/developer_guide/ci_status.png deleted file mode 100644 index 74d56d07a..000000000 Binary files a/doc/developer_guide/ci_status.png and /dev/null differ diff --git a/doc/developer_guide/coding_guidelines.rst b/doc/developer_guide/coding_guidelines.rst deleted file mode 100644 index 4ba71beb0..000000000 --- a/doc/developer_guide/coding_guidelines.rst +++ /dev/null @@ -1,19 +0,0 @@ -Coding guidelines -================= - -Glue is written entirely in Python, and we abide by the following guidelines: - -* We follow many of the same guidelines as the `Astropy `_ project, which you can find `here `__. - -* We use absolute imports for most of the code in Glue, with the exception of - tests, which are allowed to import the classes/functions they are testing - using relative imports. This means that if we need to move files and their - associated tests around, the tests will still work without having to change - the imports. - -* All Qt-specific code should live in ``qt/`` sub-directories (see - :ref:`qt_code` for more details). - -* Docstrings should be written using the `numpydoc - `_ format, which is described in detail - `here `__. diff --git a/doc/developer_guide/communication.rst b/doc/developer_guide/communication.rst deleted file mode 100644 index 5e482132f..000000000 --- a/doc/developer_guide/communication.rst +++ /dev/null @@ -1,123 +0,0 @@ -.. _communication: - -The communication framework -=========================== - -.. _publish_subscribe: - -Publish/Subscribe model ------------------------ - -Glue is built around a publish/subscribe paradigm that allows individual -components to remain synchronized without knowing about each other. The core -object that allows this is the :class:`~glue.core.hub.Hub`, which listens for -messages from various parts of Glue and relays messages to other interested -objects about changes in state to the data and subsets. - -You *can* instantiate a :class:`~glue.core.hub.Hub` instance directly:: - - >>> from glue.core import Hub - >>> hub = Hub() - -but in most cases if you are using a -:class:`~glue.core.data_collection.DataCollection`, you can let it instantiate -the hub instead and access it via the ``.hub`` attribute:: - - >>> from glue.core import DataCollection - >>> data_collection = DataCollection() - >>> data_collection.hub - - -Messages are exchanged using :class:`~glue.core.message.Message` objects. A -message is a notice that something interesting has happened. Various -sub-classes of :class:`~glue.core.message.Message` exist, such as -:class:`~glue.core.message.DataMessage` or -:class:`~glue.core.message.SubsetMessage`, and even more specialized ones such -as :class:`~glue.core.message.DataCollectionAddMessage`. - -Using the :meth:`~glue.core.hub.Hub.subscribe` method, you can easily attach callback functions/methods to specific messages using the syntax:: - - hub.subscribe(self, subscriber, message_class, handler=..., filter=...) - -where the ``message_class`` is the type of message to listen for, such as -:class:`~glue.core.message.DataMessage`, ``handler`` is the function/method to -be called if the message is received (the function/method should take one -argument which is the message), and ``filter`` can be used to specify -conditions in which to pass on the message to the function/method (for more -information on this, see the :meth:`~glue.core.hub.Hub.subscribe` -documentation). - -Subscribing to messages has to be done from a -:class:`~glue.core.hub.HubListener` instance. The following simple example shows how to set up a basic :class:`~glue.core.hub.HubListener` and register to listen for :class:`~glue.core.message.DataMessage` and :class:`~glue.core.message.DataCollectionAddMessage`:: - - >>> from glue.core import Hub, HubListener, Data, DataCollection - >>> from glue.core.message import (DataMessage, - ... DataCollectionMessage) - >>> - >>> class MyListener(HubListener): - ... - ... def __init__(self, hub): - ... hub.subscribe(self, DataCollectionMessage, - ... handler=self.receive_message) - ... hub.subscribe(self, DataMessage, - ... handler=self.receive_message) - ... - ... def receive_message(self, message): - ... print("Message received:") - ... print("{0}".format(message)) - -We can then create a data collection, and create an instance of the above -class:: - - >>> data_collection = DataCollection() - >>> hub = data_collection.hub - >>> listener = MyListener(hub) - -If we create a new dataset, then add it to the data collection created above, we then trigger the ``receive_message`` method:: - - >>> data = Data(x=[1,2,3]) - >>> data_collection.append(data) - Message received: - DataCollectionAddMessage: - Sent from: DataCollection (1 data set) - 0: - -Note that :class:`~glue.core.message.DataCollectionAddMessage` is a subclass of -:class:`~glue.core.message.DataCollectionMessage` -- when registering to a -message class, sub-classes of this message will also be received. - -It is also possible to trigger messages manually:: - - >>> # We can also create messages manually - ... message = DataMessage(data) - >>> hub.broadcast(message) - Message received: - DataMessage: - Sent from: Data Set: Number of dimensions: 1 - Shape: 3 - Components: - 0) x - 1) Pixel Axis 0 - 2) World 0 - -Typical workflow ----------------- - -This is used in Glue to produce the following communication workflow: - - * An empty :class:`~glue.core.data_collection.DataCollection` object is - created, and automatically connected to a :class:`~glue.core.hub.Hub`. - * Data are added to the data collection - * Several *clients* register to the hub, and subscribe to particular - types of messages. - * Something (perhaps code, perhaps user interaction with a client) - acts to change the state of a data or subset object. These changes - automatically generate particular messages that get sent to the Hub. These - messages communicate atomic events such as a change in the data, a change in - a subset, or the fact a subset has been deleted. - * Upon receiving a message, the hub relays it to all clients that - have subscribed to that particular message type. - * The clients react to the message however they see fit. - -Here, we use the term client in the generic sense of a class that interacts -with the hub. diff --git a/doc/developer_guide/data.rst b/doc/developer_guide/data.rst deleted file mode 100644 index 6ecd18a9a..000000000 --- a/doc/developer_guide/data.rst +++ /dev/null @@ -1,194 +0,0 @@ -.. _basedata: - -Defining your own data objects -============================== - -Background ----------- - -By default, data objects in glue are instances of the -:class:`~glue.core.data.Data` class, and this class assumes that the data are -stored in one or more local n-dimensional Numpy arrays. However, glue now -includes a way of defining a wider variety of data objects, which may rely for -example on large remote datasets, or datasets that are not inherently stored as -regular n-dimensional arrays. - -The base class for all datasets is :class:`~glue.core.data.BaseData`, which is -intended to represent completely arbitrary datasets. However, glue does not yet -recognize classes that inherit directly from :class:`~glue.core.data.BaseData`. -Instead, for now, the base class that can be used to define custom data objects -is :class:`~glue.core.data.BaseCartesianData`, which inherits from -:class:`~glue.core.data.BaseData` and requires data objects to present an -interface that looks like n-dimensional arrays (although the storage of the data -could still be unstructured). In future, we will also make it possible to -support a more generic interface for data access based on the -:class:`~glue.core.data.BaseData` class. - -Main data interface -------------------- - -Before we dive in, we recommend that you take a look at the :ref:`data_tutorial` -tutorial to understand how the default :class:`~glue.core.data.Data` objects -work. The most important takeaway from this which is relevant here is that glue -data objects are collections of attributes (*components* in glue-speak) that are -assumed to be aligned (for regular cartesian datasets, this means they are on -the same grid). For example a table consists of a collection of 1-d attributes -that are all the same length. A traditional image consists of a single 2-d -attribute. Attributes/components are identified in data objects by -:class:`~glue.core.component_id.ComponentID` objects (though these objects don't -contain the actual values -- they are just a reference to that attribute). - -To define your own data object, you should write a class that inherits from -:class:`~glue.core.data.BaseCartesianData`. You will then need to define a few -properties and methods for the data object to be usable in glue. The properties -you need to define are: - -* :attr:`~glue.core.data.BaseData.label`: the name of the dataset, as a string -* :attr:`~glue.core.data.BaseCartesianData.shape`: the shape of a single - component, given as a tuple -* :attr:`~glue.core.data.BaseData.main_components`: a list of all - :class:`~glue.core.component_id.ComponentID` that your data object recognizes, - excluding coordinate components (more on this later). - -The methods you need to define are: - -* :meth:`~glue.core.data.BaseData.get_kind`: given a - :class:`~glue.core.component_id.ComponentID`, return a string that should be - either ``'numerical'`` (for e.g. floating-point and integer attributes), - ``'categorical'`` (for e.g. string attributes), or ``'datetime'`` (for - attributes that use the ``np.datetime64`` type). -* :meth:`~glue.core.data.BaseCartesianData.get_data`: given a - :class:`~glue.core.component_id.ComponentID` and optionally a *view*, return a - Numpy array. A view can be anything that can be used to slice a Numpy array. - For example a single integer (``view=4``), a tuple of slices ( - ``view=[slice(1, 14, 2), slice(4, 50, 3)]``), a list of tuples of indices - (``view=[(1, 2, 3), (4, 3, 4)]``), and so on. If a view is specified, only that - subset of values should be returned. For example if the data has an overall - shape of ``(10,)`` and ``view=slice(1, 6, 2)``, ``get_data`` should - return an array with shape ``(3,)``. By default, :meth:`BaseCartesianData.get_data ` - will return values for pixel and world :class:`~glue.core.component_id.ComponentID` - objects as well as any linked :class:`~glue.core.component_id.ComponentID`, so we - recommend that your implementation calls :meth:`BaseCartesianData.get_data ` - for any :class:`~glue.core.component_id.ComponentID` you do not expose yourself. -* :meth:`~glue.core.data.BaseCartesianData.get_mask`: given a - :class:`~glue.core.subset.SubsetState` object (described in `Subset states`_) - and optionally a ``view``, return a boolean array describing which values are - in the specified subset (where `True` indicates values inside the subset). -* :meth:`~glue.core.data.BaseCartesianData.compute_statistic`: given a statistic - name (e.g. ``'mean'``) and a :class:`~glue.core.component_id.ComponentID`, as - well as optional keyword arguments (see - :meth:`~glue.core.data.BaseCartesianData.compute_statistic`), return - a statistic for the required component. In particular one of the keyword - arguments is ``subset_state``, which can be used to indicate that the - statistic should only be computed for a subset of values. -* :meth:`~glue.core.data.BaseCartesianData.compute_histogram`: given a list of - :class:`~glue.core.component_id.ComponentID` objects, as well as optional - keyword arguments (see - :meth:`~glue.core.data.BaseCartesianData.compute_histogram`), compute an - n-dimensional histogram of the required attributes. At the moment glue only - makes use of this for one or two dimensions, though we may also use it for - three dimensions in future. -* :meth:`~glue.core.data.BaseCartesianData.compute_fixed_resolution_buffer`: - given a list of bounds of the form ``(min, max, n)`` along each dimension - of the data, return a fixed resolution buffer that goes from the respective - ``min`` to ``max`` in ``n`` steps. Bounds can also be single scalars in cases - where the fixed resolution buffer is a lower-dimensional slice. This method - can optionally take a target dataset in the case where the fixed resolution - buffer should be computed in the frame of reference of a different dataset, - in which case the bounds should be interpreted in the frame of reference of the - target dataset (but this is only needed if data linking is used). See - :meth:`~glue.core.data.BaseCartesianData.compute_fixed_resolution_buffer` for - a full list of arguments. - -Subset states -------------- - -In the above section, we mentioned the concept of a -:class:`~glue.core.subset.SubsetState`. In glue parlance, a *subset state* is an -abstract representation of a subset in the data -- for instance *the subset of -data where a > 3* or *the subset of data inside a polygon with vertices vx and -vy*. Several of the methods in `Main data interface`_ can -take subset states, and so in your data object, you should decide how to -most efficiently implement each kind of subset state. - -You can find a full list of subset states defined in glue :mod:`here -`, and in particular you can look at the documentation of each -one to understand how to access the relevant information. For example, if you -have an :class:`~glue.core.subset.InequalitySubsetState`, you can access the -relevant information as follows:: - - >>> subset_state - 1.2)> - >>> subset_state.left - x - >>> subset_state.right - 1.2 - >>> subset_state.operator - - -:class:`~glue.core.subset.SubsetState` objects have a -:meth:`~glue.core.subset.SubsetState.to_mask` method that can take a data -object and a view and return a mask:: - - >>> subset_state.to_mask(d) - array([False, True, True]) - -In this case, the subset state essentially accesses the data using -:meth:`~glue.core.data.BaseCartesianData.get_data`, so this may be very -inefficient for large datasets. Therefore, you may choose to re-interpret the -subset states and compute a mask yourself. - -While developing your data class, one way to make sure that glue doesn't crash -if you haven't yet implemented support for a specific subset state is to -interpret any unimplemented subset state as simply indicating an empty subset. - -Linking -------- - -You should be able to link data objects that inherit from -:class:`~glue.core.data.BaseCartesianData` with other datasets - however -for this to work properly you should make sure that your implementation of -:meth:`~glue.core.data.BaseCartesianData.get_data` calls -:meth:`BaseCartesianData.get_data ` -for any unrecognized :class:`~glue.core.component_id.ComponentID`, as the base -implementation will handle returning linked values. - -Using your data object ----------------------- - -Assuming you have written your own data class, there are several ways that you -can start using it in glue: - -* You can define your own :ref:`data loader ` which is a - function that takes a filename and should return an instance of a subclass - of :class:`~glue.core.data.BaseCartesianData`. - -* You can define your own :ref:`data importer `, which is a - function that can do anything you need, for example showing a dialog, and - should return a list of instances of - :class:`~glue.core.data.BaseCartesianData`. This is more general than a - data loader since a data importer doesn't need to rely on a filename. It - might include for example opening a dialog in which you can log in to a - remote service and browse available datasets. - -* You can start up glue programmatically, including constructing your data - object. This is particularly useful when initially developing your custom - data object:: - - # Construct your data object - d = MyCustomData(...) - - # Create glue application and start - dc = DataCollection([d]) - ga = GlueApplication(dc) - ga.start() - -Example -------- - -As an example of a minimal custom data class, the following implements a (very -uninteresting) dataset that simply generates values randomly in the range [0:1] -on-the-fly, and does not take subset states into account. A glue session is then -created with one of these data objects: - -.. literalinclude:: random_data.py diff --git a/doc/developer_guide/developer_guide.rst b/doc/developer_guide/developer_guide.rst deleted file mode 100644 index d1a248c36..000000000 --- a/doc/developer_guide/developer_guide.rst +++ /dev/null @@ -1,26 +0,0 @@ -Developer Guide -=============== - -So you want to help develop Glue? Great, let's get started! :) - -First, be sure to join the `glueviz-dev -`_ mailing list for any -questions or discussions related to development, and let us know if any of the -documentation below is unclear. You are also very welcome to introduce yourself on the list and let us know that you are interested in contributing! - -If you are going to be developing Glue, we recommend that you familiarize yourself with the rest of the documentation, and in particular the :ref:`glue architecture ` documentation, which describes in detail how some of the Glue internals work (although there are still a number of areas on which you can work without understanding all the internals in detail). - -If you want to contribute, but don't yet have a specific idea of where to make contributions, you can check out our :ref:`guide to picking issues `! - -The following pages provide logistical information about the layout of the code, coding and testing guidelines, and some more advanced topics: - -.. toctree:: - :maxdepth: 1 - - issues.rst - organization.rst - roadmap.rst - qt_development.rst - coding_guidelines.rst - testing.rst - release.rst diff --git a/doc/developer_guide/issues.rst b/doc/developer_guide/issues.rst deleted file mode 100644 index a0cfc150a..000000000 --- a/doc/developer_guide/issues.rst +++ /dev/null @@ -1,77 +0,0 @@ -.. _issue_guide: - -Finding issues/projects to work on -================================== - -About issues ------------- - -Glue has an `issue tracker `_ on GitHub. In GitHub terminology, an *issue* can be: - -* A bug report -* A report of installation/usage issues (not all of these end up being bugs) -* A request for a new feature -* A request for improved documentation - -Issues can be assigned to specific people, which means that they are planning -on working on it. In principle, you can pick any issue that is not already -assigned! Even in the case where an issue is assigned but there has not been -any recent activity, you can add a comment to ask about whether you can help -with it. - -.. note:: If you decide to work on an open issue, please leave a comment on - it to make sure other people know you are doing so! - -Picking an issue ----------------- - -Picking an issue may seem daunting, but we label issues with various tags to -make it easier for you to find issues that might be interesting to you. Here -are some of the labels (click on the label name to go to those issues): - -* `package-novice `_: these are issues that do not require you to know much about glue before - starting (there are also matching `package-intermediate `_ and `package-expert `_ labels) - -* `non-gui `_: these are - issues that **don't** require any knowledge of how to do GUI programming - (including Qt). Some of these may of course be hard for other reasons, but - there are also a number of reasonably straightforward issues that just need - a little time spent on them. - -* `bug `_: these are bug - reports. Fixing bugs can sometimes be a nice place to start, because you - don't have to worry about creating new functionality, just fixing existing - ones. However, not all bugs are easy, so make sure you check the other - labels. - -* `effort-low `_, - `effort-medium `_, and - `effort-high `_: these - indicate whether the issue will likely take respectively: at most a few hours - to fix, at most a few days, and more than a few days. Not all issues are - labeled with these, because it's not always easy to predict how long issues - will take to tackle. - -* `standalone-project `_: these are - issues which are projects rather than simple fixes, in that they could take - several days or more to implement, and they can sometimes be done in - several stages. These projects are nicely isolated from the rest of the - glue development, and have a well defined end goal that will result in a - shiny new feature in glue. These are great issues to work on if you want to - get more involved in glue development! - -Note that GitHub allows you to filter by multiple labels, so you can for example -search for issues that are both `package-novice and non-gui -`_ - -If you have other ideas of things to implement, you can of course do so, and the -issue tracker is not meant to be an exhaustive list. Just drop us a message on -`glue-viz-dev `_ to let us -know what you are working on! - -Getting help ------------- - -In the event that you need any advice when working on an issue, or get stuck, -just leave a comment on the issue, and one of our friendly developers will -help you out! diff --git a/doc/developer_guide/linking.rst b/doc/developer_guide/linking.rst deleted file mode 100644 index b4116e1fe..000000000 --- a/doc/developer_guide/linking.rst +++ /dev/null @@ -1,155 +0,0 @@ -.. _linking-framework: - -The linking framework -===================== - -One of the strengths of Glue is the ability to be able to link different -datasets together. The :ref:`linking` page describes how to set up links -graphically from the Glue application, but in this page, we look at how links -are set up programmatically. - -Creating component links programmatically ------------------------------------------ - -As described in :ref:`data_tutorial`, components are identified by -:class:`~glue.core.component_id.ComponentID` instances. We can then use these -to create links across datasets. Note that links are not defined between -:class:`~glue.core.data.Data` or :class:`~glue.core.component.Component` -objects, but between :class:`~glue.core.component_id.ComponentID` instances. - -The basic linking object is :class:`~glue.core.component_link.ComponentLink`. -This describes how two :class:`~glue.core.component_id.ComponentID` instances -are linked. The following example demonstrates how to set up a -:class:`~glue.core.component_link.ComponentLink` programmatically: - - >>> from glue.core import Data, DataCollection - >>> d1 = Data(x1=[1, 2, 3]) - >>> d2 = Data(x2=[2, 3, 4, 5]) - >>> dc = DataCollection([d1, d2]) - >>> from glue.core.component_link import ComponentLink - >>> link = ComponentLink([d1.id['x1']], d2.id['x2']) - -Note that the first -argument of :class:`~glue.core.component_link.ComponentLink` should be a list of :class:`~glue.core.component_id.ComponentID` -instances. - -Since no linking function was specified in the above example, -:class:`~glue.core.component_link.ComponentLink` defaults to the simplest kind -of link, ``identity``. For the link to be useful, we need to add it to the data -collection, and we'll be able to see what it changes:: - - >>> dc.add_link(link) - -If we look at the list of components on the :class:`~glue.core.data.Data` -objects, we see that the ``x2`` component in ``d2`` has been replaced by ``x1``: - - >>> print(d1.components) - [Pixel Axis 0, World 0, x1] - >>> print(d2.components) - [Pixel Axis 0, World 0, x1] - -This is because we used the identify transform, so since the -:class:`~glue.core.component_id.ComponentID` objects ``x1`` and ``x2`` are -interchangeable, Glue decided to use ``x1`` instead of ``x2`` in ``d2`` for -simplicity. - -The benefit of this is now that if we create a -:class:`~glue.core.subset.SubsetState` based on the ``x1`` -:class:`~glue.core.component_id.ComponentID`, this -:class:`~glue.core.subset.SubsetState` will be applicable to both datasets: - - >>> subset_state = d2.id['x1'] > 2.5 - >>> subset_group = dc.new_subset_group('x1 > 2.5', subset_state) - -This has now created subsets in both ``d1`` and ``d2``:: - - >>> d1.subsets[0].to_mask() - array([False, False, True], dtype=bool) - >>> d2.subsets[0].to_mask() - array([False, True, True, True], dtype=bool) - -Let's now try and use a custom linking function that is not simply identity:: - - >>> link = ComponentLink([d1.id['x1']], d2.id['x2'], - ... using=lambda x: 2*x) - >>> dc.add_link(link) - -This time, if we look at the list of components on the :class:`~glue.core.data.Data` -objects, we see that ``d1`` now has an additional component, ``x2``:: - - >>> print(d1.components) - [Pixel Axis 0, World 0, x1, x2] - >>> print(d2.components) - [Pixel Axis 0, World 0, x2] - -We can take a look at the values of all the components:: - - >>> print(d1['x1']) - [1 2 3] - >>> print(d1['x2']) - [2 4 6] - >>> print(d2['x2']) - [2 3 4 5] - -In this case, both datasets have kept their original components, but ``d1`` now -also includes an ``x2`` :class:`~glue.core.component.DerivedComponent` which -was computed as being twice the values of ``d1['x1']``. - -Creating simple component links can also be done using arithmetic operations on -:class:`~glue.core.component_id.ComponentID` instances: - - >>> d3 = Data(xa=[1, 2, 3], xb=[1, 3, 5]) - >>> dc = DataCollection([d3]) - >>> diff = d3.id['xa'] - d3.id['xb'] - >>> diff - - >>> dc.add_link(diff) - >>> d3['diff'] - array([ 0, -1, -2]) - -.. note:: This is different from using comparison operators such as ``>`` or - ``<=`` on :class:`~glue.core.component_id.ComponentID` instances, - which produces :class:`~glue.core.subset.SubsetState` objects. - -It is also possible to add a component link to just one particular -:class:`~glue.core.data.Data` object, in which case this is equivalent to creating a :class:`~glue.core.component.DerivedComponent`. The following:: - - >>> from glue.core import Data - >>> d4 = Data(xa=[1, 2, 3], xb=[1, 3, 5]) - >>> link = d4.id['xa'] * 2 - >>> d4.add_component_link(link, 'xa_double_1') - - >>> print(d4['xa_double_1']) - [2 4 6] - -is equivalent to creating a derived component:: - - >>> d4['xa_double_2'] = d4.id['xa'] * 2 - >>> print(d4['xa_double_2']) - [2 4 6] - -When adding a component link via the -:class:`~glue.core.data_collection.DataCollection` -:meth:`~glue.core.data_collection.DataCollection.add_link` method, new -component IDs are only added to :class:`~glue.core.data.Data` objects for which -the set of :class:`~glue.core.component_id.ComponentID` required for the link -already exist. For instance, in the following example, ``xu`` is only added to -``d6``:: - - >>> d5 = Data(xs=[5, 5, 6]) - >>> d6 = Data(xt=[3, 2, 3]) - >>> dc = DataCollection([d5, d6]) - >>> new_component = ComponentID('xu') - >>> link = ComponentLink([d6.id['xt']], new_component, - ... using=lambda x: x + 3) - >>> dc.add_link(link) - >>> print(d5.components) - [Pixel Axis 0, World 0, xs] - >>> print(d6.components) - [Pixel Axis 0, World 0, xt, xu] - -Built-in link functions ------------------------ - -Glue includes a number of built-in link functions that are collected in the -``link_function`` registry object from :mod:`glue.config`. You can easily create new link functions as described in :ref:`custom_links`, and these will then be available through the user interface, as shown in :ref:`linking` in the User guide. diff --git a/doc/developer_guide/organization.rst b/doc/developer_guide/organization.rst deleted file mode 100644 index e6594120b..000000000 --- a/doc/developer_guide/organization.rst +++ /dev/null @@ -1,127 +0,0 @@ -Code organization -================= - -The Glue code base is intended to be organized in a modular way, such that you -will never need to understand *all* the code in Glue, and the aim is for it to -be easy for you to identify where to make specific changes to implement the -functionality you need or fix issues. - -Glue sub-packages ------------------ - -The code is organized into the following -top-level sub-packages (starting with some of the easy ones): - -:mod:`!glue.external` -^^^^^^^^^^^^^^^^^^^^^ - -This is a sub-package that you should never have to edit directly. It contains -files and modules edited in other repositories that have been bundled with -Glue. If you do need to make any changes to them, you should first edit those -other repositories, and then port over the changes to Glue. In general, it's -useful to know these bundled modules are available, but you will likely not -need to edit them. - -:mod:`!glue.utils` -^^^^^^^^^^^^^^^^^^ - -This is a sub-package that contains various Python, Matplotlib, and Qt-related -utilities that do not depend on any other parts of Glue. These utilities don't -know about Glue data objects, subsets, or specific data viewers. Instead, this -sub-package includes utilities such as :func:`~glue.utils.geometry.points_inside_poly`, -a function to find whether points are inside a polygon, or -:func:`~glue.utils.qt.cmap2pixmap`, a function to convert a Matplotlib colormap -into a Qt ``QPixmap`` instance. This is one of the easiest sub-packages to -approach -- it is just a collection of small helper functions and classes and -doesn't require understanding any other parts of Glue. - -:mod:`!glue.core` -^^^^^^^^^^^^^^^^^ - -As its name describes, this is the most important part of the Glue package. -This defines the general classes for datasets, subsets, data collections, -messages, layer artists, and other Glue concepts. On the other hand it does -*not* define specific viewers or data readers. The code in this sub-package is -not concerned with specific graphical user interface (GUI) representations, and -you could in principle develop a completely different GUI than the main Glue -one making use of the Glue core code. You could even use :mod:`!glue.core` to -give glue-like functionality to other existing applications. - -:mod:`!glue.viewers` -^^^^^^^^^^^^^^^^^^^^ - -This sub-package contains the code for all the built-in viewers in glue, such -as the scatter plot and image viewers. Each viewer is contained in a -sub-package of :mod:`!glue.viewers`, such as :mod:`!glue.viewers.scatter`. A -:mod:`!glue.viewers.common` sub-package is also provided, with utilities and -base classes that might be useful for various viewers. For instance, the -:mod:`!glue.viewers.common.qt.toolbar_mode` sub-module contains code related to -defining toolbar mouse 'modes' for selection. - -:mod:`!glue.dialogs` -^^^^^^^^^^^^^^^^^^^^ - -This sub-package contains implementations of various common dialogs, each -organized into sub-packages. For instance, :mod:`!glue.dialogs.custom_component` -contains the implementation of the dialog used to add new components to -datasets in the Glue application. The implementation for these dialogs only -uses the framework from the :mod:`!glue.core` package and the dialogs don't need -to know anything about the rest of the state of the design of the Glue -application. - -.. :mod:`glue.core.data_factories` -.. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. -.. While the core package defines the basic infrastructure for reading/writing -.. files, specific implementations of readers/writers live in -.. :mod:`glue.core.data_factories`. If you want to add a new reader or writer, this is -.. the place to put it! - -:mod:`!glue.app` -^^^^^^^^^^^^^^^^ - -This package defines the Glue *Application*, that is the default GUI that users -interact with if they launch the Glue Application. This essentially pulls -together all the components from other sub-packages into a single application. -However, it would be entirely possible to develop other applications using the -available components - for instance, one could build an application with fixed -data viewers for a specific purpose. - -:mod:`!glue.plugins` -^^^^^^^^^^^^^^^^^^^^ - -This package features more specialized tools/viewers for Glue, and in the long -term some of these will be moved into top-level sub-packages such as -``glue.viewers`` as they are made more general. - -:mod:`!glue.icons` -^^^^^^^^^^^^^^^^^^ - -This contains various icons used in Glue, both in the vector SVG form, and in -rasterized PNG format. - -.. _qt_code: - -Qt-specific code ----------------- - -Glue currently uses the Qt GUI framework. However, this does not mean that you -need to know Qt to understand all of the code in Glue. Instead, we have taken -care to isolate all Qt-specific code into directories called ``qt/``. For -instance, the ``glue/utils/qt`` directory contains Qt-related utilities, and -any other code in ``glue/utils`` is not allowed to import Qt. We enforce this -while testing by making sure that all the tests in Glue run if all the ``qt/`` -directories are removed, and no Qt implementation is installed. - -Another example is that the ``glue/viewers/scatter/qt`` directory contains code -for the scatter plot viewer that is Qt-specific, but any other code in -``glue/viewers/scatter`` is Qt-agnostic. As a result, if you are trying to fix -something that is not related to the GUI, but to e.g. the data structures in -Glue, or the specific way in which e.g. Matplotlib displays something, you -shouldn't have to go into any of the ``qt`` sub-directories. - -Another consequence of this is that if you or anyone else is interested in -developing a GUI front-end for Glue that is not based on Qt, you can re-use a -lot of the existing code that is not in the Qt directories. If we were to add -the code for another GUI framework into the Glue package, we could simply -create directories parallel to the ``qt`` directories but for the new framework. diff --git a/doc/developer_guide/qt_development.rst b/doc/developer_guide/qt_development.rst deleted file mode 100644 index e20481359..000000000 --- a/doc/developer_guide/qt_development.rst +++ /dev/null @@ -1,21 +0,0 @@ -Qt development in Glue -====================== - -.. _qtpy: - -Using QtPy ----------- - -If you are interested in working on some of the Qt-specific code, it's -important that you don't import any code directly from PyQt or PySide. -Since we want to maintain backward-compatibility with all of these, you should -always use the `QtPy `__ package. The way to -use this package is to import from the ``qtpy`` module as if it was the -``PyQt5`` module, and QtPy will automatically translate this into the -appropriate imports for PyQt or PySide if needed. For instance, instead of:: - - from PyQt5 import QtCore - -you should do:: - - from qtpy import QtCore diff --git a/doc/developer_guide/random_data.py b/doc/developer_guide/random_data.py deleted file mode 100644 index ac55033db..000000000 --- a/doc/developer_guide/random_data.py +++ /dev/null @@ -1,76 +0,0 @@ -import numpy as np - -from glue.core.component_id import ComponentID -from glue.core.data import BaseCartesianData -from glue.utils import view_shape - - -class RandomData(BaseCartesianData): - - def __init__(self): - super(RandomData, self).__init__() - self.data_cid = ComponentID(label='data', parent=self) - - @property - def label(self): - return "Random Data" - - @property - def shape(self): - return (512, 512, 512) - - @property - def main_components(self): - return [self.data_cid] - - def get_kind(self, cid): - return 'numerical' - - def get_data(self, cid, view=None): - if cid is self.data_cid: - return np.random.random(view_shape(self.shape, view)) - else: - return super(RandomData, self).get_data(cid, view=view) - - def get_mask(self, subset_state, view=None): - return subset_state.to_mask(self, view=view) - - def compute_statistic(self, statistic, cid, - axis=None, finite=True, - positive=False, subset_state=None, - percentile=None, random_subset=None): - if axis is None: - if statistic == 'minimum': - return 0 - elif statistic == 'maximum': - if cid in self.pixel_component_ids: - return self.shape[cid.axis] - else: - return 1 - elif statistic == 'mean' or statistic == 'median': - return 0.5 - elif statistic == 'percentile': - return percentile / 100 - elif statistic == 'sum': - return self.size / 2 - else: - final_shape = tuple(self.shape[i] for i in range(self.ndim) - if i not in axis) - return np.random.random(final_shape) - - def compute_histogram(self, cid, - range=None, bins=None, log=False, - subset_state=None, subset_group=None): - return np.random.random(bins) * 100 - - -# We now create a data object using the above class, -# and launch a a glue session - -from glue.core import DataCollection -from glue.app.qt.application import GlueApplication - -d = RandomData() -dc = DataCollection([d]) -ga = GlueApplication(dc) -ga.start() diff --git a/doc/developer_guide/release.rst b/doc/developer_guide/release.rst deleted file mode 100644 index 76536202e..000000000 --- a/doc/developer_guide/release.rst +++ /dev/null @@ -1,27 +0,0 @@ -Releasing a new package version -=============================== - -A new release of packages in the `glue-viz `_ ecosystem is -now almost fully automated. -For maintainers it should be nice and simple to do, especially if all merged PRs -have informative titles and are correctly labelled. - -Here is the process to follow to create a new release: - -#. Go through all the PRs since the last release, make sure they have - descriptive titles (as these will become the auto-generated changelog entries) - and are labelled correctly - preferably identifying them as one of - `bug`, `enhancement` or `documentation`. - -#. Go to the GitHub releases interface and draft a new release; new tags should - include the trailing patch version ``.0`` (e.g. ``1.6.0``, not ``1.6``) on - `major.minor` releases (early releases of most packages did not). - -#. Use the GitHub autochange log generator; this should use the configuration in - `.github/release.yml `_ - to make headings based on labels. - -#. Edit the draft release notes as required, particularly to call out major - changes at the top. - -#. Publish the release. diff --git a/doc/developer_guide/roadmap.rst b/doc/developer_guide/roadmap.rst deleted file mode 100644 index edb799228..000000000 --- a/doc/developer_guide/roadmap.rst +++ /dev/null @@ -1,100 +0,0 @@ -Development Roadmap -=================== - -This page provides a high-level overview of some of the directions in which we -want to push development in future. We are very interested in hearing from -people who are interested in contributing to any of the ideas below - if you -are, please join the friendly -`glue-viz-dev `_ list and -let us know! - -There are many more ways you can contribute to glue that are not mentioned -below - these are just the tip of the iceberg, but are here to give you an idea -of places you might be able to contribute. You can also search the issue -tracker on glue for all issues related to `enhancements -`_ -for example. - -Support for big/complex data: an abstract data and computation interface ------------------------------------------------------------------------- - -Glue currently provides ways of importing data from different sources, but -ultimately, the data is essentially loaded in memory. In principle, the -:class:`~glue.core.data.Data` class can be subclassed in order to provide for -example a object where data is only accessed on-the-fly as needed (in fact, -this also happens if a memory-mapped Numpy array is passed to -:class:`~glue.core.data.Data`). Computations such as calculating histograms or -selections is left up to the rest of the glue, and viewers are responsible -for figuring out which subsets of data to access, if needed, and how to stride -over the data when only a subset is needed. In addition, a lot of the glue code -assumes regularly gridded datasets - which makes it difficult to apply to e.g. -simulations with adaptive grids. - -It would be nice to have a much better separation between data -representation/access/computation and the rest of the interactive glue -environment, including the viewers. The idea would be to develop an abstract -base class for data objects which defines ways to access the data values and -subsets, including for example ways of computing fixed resolution buffers for -both the data and subsets. The Data object would be responsible for storing the -data as well as information about the subsets in that dataset, in an efficient -way. In fact, glue could then function entirely in world coordinates and not -even have to worry about the concept of 'pixels' in the data. - -A consequence of this is that image viewers for example would simply request -fixed resolution buffers at the screen resolution, for both data and subsets, -and would then be able to display them. Behind the scenes, the user could be -using e.g. a package such as `yt `_ to access a 3Tb -simulation file with adaptive/nested grids, but this would be seamless to the -user (except of course that the speed would be limited by the computational -requirements of the data object). - -In fact, we could even provide a way to transfer API calls to this standard -Data API over the network, which would open up the possibility of using glue to -explore datasets hosted on computer clusters. Of course, there would be some -network latency during operations, but *some* latency would be expected anyway -for very large datasets, which would still benefit hugely from this. - -Things that would need to be done in order to achieve this: - -#. Define what belongs inside the Data abstraction and what doesn't -#. Define an API for data/subset access and computations -#. Refactor glue to use this data access API with the built-in - :class:`~glue.core.data.Data` objects. -#. Develop new data objects based e.g. on yt -#. Develop a way for the data API calls to be passed over the network - -Related GitHub issues: `#708 `_ - -Support for big data: more efficient viewers --------------------------------------------- - -`Matplotlib `_ and `VisPy `_ both -start becoming slow when the limit of a million points/markers is reached. This -severely limits the size of the largest datasets that can be visualized in the -scatter plot viewers, because the visualization will be slow even if the data -contains only two components of a million elements each. In addition to large -tables, this can easily happen if the user makes a scatter plot of one image -versus another. - -We therefore need to work on more efficient ways to show scatter plot data. In -particular, we could explore methods that rasterize the points extremely -efficiently, or methods that sub-sample the points in smart ways (for example, -neighboring points could be replaced by a slightly larger point). - -Related GitHub issues: `#722 `_ - -Glue in the browser -------------------- - -It is currently already possible to :ref:`launch glue from an IPython/Jupyter notebook ` and -access the data and viewers using the returned application object. However, the -next step would be to implement actual viewers that are not based on Qt, but -instead can be used inside the notebook directly. One promising avenue would be -to explore the use of `bokeh `_. - -The glue code base is designed so that the core representation of data objects, -subsets, and so on in glue.core is completely independent of the visualization -framework. Therefore, this would just require developing new viewers, not -re-writing large sections of already existing code. - -Related GitHub issues: `#801 `_ diff --git a/doc/developer_guide/selection.rst b/doc/developer_guide/selection.rst deleted file mode 100644 index 15a597487..000000000 --- a/doc/developer_guide/selection.rst +++ /dev/null @@ -1,142 +0,0 @@ -.. _dev_selection: - -The selection/subset framework -============================== - -One of the central concepts in Glue is that of subsets, which are typically -created as a result of the user selecting data in a viewer or creating the -subset from the command-line. In order to go from a selection on the screen to -defining a subset from a dataset, Glue includes the following concepts: - -* **Region of interests** (ROIs), which are an abstract representation of a - geometrical region or selection. - -* **Subset states**, which is a descriptions of the subset selection. - -* Data **Subsets**, which are the result of applying a subset state/selection - to a specific dataset. - -When a user makes a selection in a data viewer in the Glue application, the -selection is first translated into a ROI, after which the ROI is converted to a -subset state, then applied to the data collection to produce subsets in each -dataset. These three concepts are described in more detail below. - -Regions of interest -------------------- - -The easiest way to think of regions of interest is as geometrical regions. -Basic classes for common types of ROIs are included in the :mod:`glue.core.roi` -sub-module. For example, the :mod:`~glue.core.roi.RectangularROI` class -describes a rectangular region using the lower and upper values in two -dimensions:: - - >>> from glue.core.roi import RectangularROI - >>> roi = RectangularROI(xmin=1, xmax=3, ymin=2, ymax=5) - -Note that this is not related to any particular dataset -- it is an abstract -representation of a rectangular region. It also doesn't specify which -components the rectangle is drawn in. All ROIs have a -:meth:`glue.core.roi.RectangularROI.contains` method that can be used to check -if a point or a set of points lies inside the region:: - - >>> roi.contains(0, 3) - False - >>> roi.contains(2, 3) - True - >>> import numpy as np - >>> x = np.array([0, 2, 4]) - >>> y = np.array([3, 3, 2]) - >>> roi.contains(x, y) - array([False, True, False], dtype=bool) - -Subset states -------------- - -While regions of interest define geometrical regions, subset states, which are -sub-classes of :class:`~glue.core.subset.SubsetState`, describe a selection as -a function of Glue :class:`~glue.core.component_id.ComponentID` objects. Note -that this is different from :class:`~glue.core.subset.Subset` instances, which -describe the subset *resulting* from the selection (see `Subsets`_). The -following simple example shows how to easily create a -:class:`~glue.core.subset.SubsetState`:: - - - >>> from glue.core import Data - >>> data = Data(x=[1,2,3], y=[2,3,4]) - >>> state = data.id['x'] > 1.5 - >>> state - 1.5)> - -Note that ``state`` is not the subset of values in ``data`` that are greater -than 1.5 -- instead, it is a representation of the inequality, the *concept* of -selecting all values of x greater than 1.5. This distinction is important, -because if another dataset defines a link between one of its components and the -``x`` component of ``data``, then the inequality can be used for that other -component too. - -While the above syntax is convenient for using Glue via the command-line, in the -case of data viewers, we actually want to translate ROIs into subset states. To -do this, we can use the :func:`~glue.core.subset.roi_to_subset_state` function -that takes a ROI and returns a subset state. At the moment this method works for -1- and 2-d ROIs. In more complex cases, you can also define your own logic for -converting ROIs into subset states. See the documentation of -:func:`~glue.core.subset.roi_to_subset_state` for more details. - -Subset states can be combined using logical operations: - ->>> state1 = data.id['x'] > 1.5 ->>> state2 = data.id['y'] < 4 ->>> state1 & state2 - ->>> state1 | state2 - ->>> ~state1 - - -Note that you should use ``&``, ``|``, and ``~`` as opposed to ``and``, ``or``, -and ``not``. - -Subsets -------- - -A subset is what we normally think of as sub-part of a dataset. Subsets are -typically created by making `Subset states`_ first. There are then different -ways of applying this subset state to a :class:`~glue.core.data.Data` object to actually create a subset. The -easiest way of doing this is to simply call the -:meth:`~glue.core.data.BaseData.new_subset` method with the -:class:`~glue.core.subset.SubsetState` and optionally a label describing that -subset:: - - >>> subset = data.new_subset(state, label='x > 1.5') - >>> subset - Subset: x > 1.5 (data: ) - -The resulting subset can then be used in a similar way to a -:class:`~glue.core.data.Data` object, but it will return only the values in the -subset:: - - >>> subset['x'] - array([2, 3]) - - >>> subset['y'] - array([3, 4]) - -Finally, you can also get the mask from a subset:: - - >>> subset.to_mask() - array([False, True, True], dtype=bool) - -One of the benefits of subset states is that they can be applied to multiple -data objects, and if the different data objects have linked components (as described in :doc:`linking`), this -may produce several valid subsets in different datasets. We can apply a :class:`~glue.core.subset.SubsetState` to all datasets in a data collection by using the :meth:`~glue.core.data_collection.DataCollection.new_subset_group` method with -the :class:`~glue.core.subset.SubsetState` and a label describing that subset, similarly to :meth:`~glue.core.data.BaseData.new_subset` - - >>> from glue.core import DataCollection - >>> data_collection = DataCollection([data]) - >>> subset_group = data_collection.new_subset_group('x > 1.5', state) - -This creates a :class:`~glue.core.subset_group.SubsetGroup` which represents a group of subsets, with the individual subsets accessible via the ``subsets`` attribute:: - - >>> subset = subset_group.subsets[0] - >>> subset - Subset: x > 1.5 (data: ) diff --git a/doc/developer_guide/simple_glue.py b/doc/developer_guide/simple_glue.py deleted file mode 100644 index 7cac5a09e..000000000 --- a/doc/developer_guide/simple_glue.py +++ /dev/null @@ -1,40 +0,0 @@ -from glue.core.message import DataMessage, SubsetMessage -from glue.core import HubListener, Data, DataCollection - - -class MyClient(HubListener): - def register_to_hub(self, hub): - """ Sign up to receive DataMessages from the hub """ - hub.subscribe(self, # subscribing object - DataMessage, # message type to subscribe to - handler=self.receive_message) # method to call - - def receive_message(self, message): - """ Receives each DataMessage relay """ - print(" MyClient received a message \n") - - -# create objects -client = MyClient() -data = Data() -subset = data.new_subset() -data_collection = DataCollection() - -# connect them to each other -hub = data_collection.hub -data_collection.append(data) -client.register_to_hub(hub) - -# manually send a DataMessage. Relayed to MyClient -print('Manually sending DataMessage') -message = DataMessage(data) -hub.broadcast(message) - -# modify the data object. Automatically generates a DataMessage -print('Automatically triggering DataMessage') -data.label = "New label" - -# send a SubsetMessage to the Hub. -print('Manually sending SubsetMessage') -message = SubsetMessage(subset) -hub.broadcast(message) # nothing is printed diff --git a/doc/developer_guide/testing.rst b/doc/developer_guide/testing.rst deleted file mode 100644 index 68f1581e2..000000000 --- a/doc/developer_guide/testing.rst +++ /dev/null @@ -1,59 +0,0 @@ -Testing framework -================= - -Writing tests -------------- - -In order to make sure that everything works as expected, and that we don't -introduce any regressions when making changes, Glue includes a number of tests. -All tests are contained inside the ``tests/`` directories in each sub-package -of Glue. For example, the tests for the ``glue.core`` functionality are in -``glue/core/tests``. The tests for the ``glue.utils.qt`` functionality are in -``glue/utils/qt/tests``. Tests should always live close to the code they are -testing. - -In general, we try and make tests as specific as possible to the code being tested - so for example if we create a new dialog, and we want to check that various widgets in the tests are working correctly, we don't need to launch the whole Glue application. Instead, we can create a test data collection with some example data, and simply launch the dialog in question. - -We run tests using `pytest `_, and tests are therefore written using this framework. The syntax for a basic test is actually very simple and not py.test-specific -- it is simply a function whose name starts with ``test_``. If the function crashes, the test fails, and if the function runs without crashing, the test passes:: - - def test_functionality(): - # This is an example of a test that passes - a = 1 - b = 2 + a - assert a == 3 - -Some tests are also written using test classes instead of functions (which -can include py.test-specific methods), and we can also make use of various decorators in py.test to e.g. parameterize tests. - -Rather than provide a full guide on how to write tests, we encourage you to take a look at the Astropy documentation on `Writing tests `_, since we follow the same guidelines as them. - -Running tests -------------- - -The easiest way to run the Glue tests is to do:: - - pytest glue - -To run only a specific test file, you can do:: - - pytest glue/core/tests/test_links.py - -Continuous integration ----------------------- - -Every time someone opens a pull request to the Glue repository, and every time -we merge changes into the code base, all the tests are run on `Travis -`_ and `AppVeyor `_. This is -referred to as *Continuous Integration*. One of the nice things about continuous integration is that it allows us to -automatically run the tests for different operating systems, Python versions, -versions of Numpy, and Qt frameworks (PyQt5, and PySide2). - -`Travis `_ runs tests on Linux and MacOS X, and `AppVeyor -`_ runs the tests on Windows. When you open a pull -request, you will be able to check the status of the tests at the bottom, which -will look something like this: - -.. image:: ci_status.png - -In this example, the tests failed on Travis, but passed on Windows. You can -then get more information about which set-ups failed by clicking on 'Details'. diff --git a/doc/faq.rst b/doc/faq.rst deleted file mode 100644 index 83e5684fc..000000000 --- a/doc/faq.rst +++ /dev/null @@ -1,130 +0,0 @@ -.. _faq: - -Frequently Asked Questions -========================== - -This page contains commonly asked questions about using Glue. For more technical -issues in installing or running Glue, see the :ref:`known-issues` page. - -What data formats does Glue understand? ---------------------------------------- - -Glue relies on several libraries to parse different file formats: - - * `Astropy `_ for FITS images and tables, a - variety of `ascii table formats - `_, and VO - tables. - * `scikit-image `_ to read popular image - formats like ``.jpeg`` and ``.tiff`` - * `h5py `_ to read HDF5 files - -If Glue's predefined data loaders don't fit your needs, you can also :ref:`write -your own ` loader, and plug it into Glue. - -How do I overplot catalogs on images in Glue? ---------------------------------------------- - -Take a look at this video. For more details, consult the :ref:`tutorial `. - -.. raw:: html - -
- -
- -How do I use Glue with the IPython notebook? --------------------------------------------- - -For IPython version >= 4.0, Glue can be started from a Jupyter notebook without -blocking, so that both the notebook and the Glue UI can run at once. This -requires running the following magic function:: - - %gui qt - -And then starting glue (probably by calling :ref:`qglue ` or creating an -instance of :class:`~glue.app.qt.application.GlueApplication`) from the -notebook. - -If you encounter an error like this:: - - ValueError: API 'QString' has already been set to version 1 - -set the ``QT_API`` environment variable to either ``pyqt``, ``pyqt5``, or -``pyside``, depending on which version of Qt you have installed:: - - QT_API=pyside ipython notebook --gui qt - -Does Glue Understand CASA Cubes? --------------------------------- - -Many radio astronomy datacubes are 4 dimensional, and give intensity -as a function of 2 spatial dimensions, a frequency dimension, and a -Stokes polarization dimension. By default, Glue will read these FITS -images in as 4D hypercubes (use the task exportfits in CASA to write -a native CASA image into a FITS cube that glue can read). - -However, you might wish to load the image as a series of 3D cubes, -one for each Stokes parameter. This would more easily allow you, -for example, to compare histograms of intensities for each Stokes -parameter. - -Because of this, Glue has a special "CASA image loader", to load -these kinds of files. To open a file in this mode, select "CASA PPV Cube" -in the file type dropdown when opening a file. This will create a single -Data object with 1 attribute for each Stokes parameter. - -Something is broken, or confusing. What should I do? ----------------------------------------------------- - -If you think you have found a bug in Glue, feel free to add an issue to the -`GitHub issues page `_. If -you have general questions, feel free to post a message to the `Glue mailing -list `_, or send us an `email -`_ directly. - -You can also submit bug reports directly from Glue, by clicking ``Help->Send -Feedback``. This also sends us some diagnostic information about your Glue -installation. - -How do I make a scatterplot of columns from two different catalogs? -------------------------------------------------------------------- - -:ref:`Merge ` the two datasets into a single object. - -How do I make a scatterplot between two images? ------------------------------------------------ - -:ref:`Merge ` the two datasets into a single object, like you do for -catalogs. This will only work if the two images have exactly the same shape -(although it does not check if the WCS is the same). You can then drag the -merged object into the visualization area to view an image of the first -attribute. Repeat a second time to view the second image, and finally create a -scatterplot comparing the image intensities at each pixel. You can then select -regions in any of the three plots, and the corresponding selections in the other -plots will be highlighted. Another interesting way to visualize your two related -images is drop a fourth object using **2D Image Viewer**, but this time select the RGB -instead of Monochrome, and put each image into one of the colors. Right mouse -down will change the *Contrast* selected color after you have selected the -*bias/contrast button* on the top row. Obviously with three images this is a -great way to create a colorful RGB image. - -A note on brushing in the image. If you want to just highlight a single pixel -(pretty easy once you zoom in), you need to make sure your rectangular selection -has its top right selection in that pixel. You cannot just highlight inside the -pixel, it needs to cross boundaries between pixels. - -.. image:: images/two_images.png - :align: center - :width: 400 - -The intensity in image SUM1 and image SUM2 are correlated in the scatter -plot. A second image of SUM1 is zoomed in around two *warm* pixels to see -where they show up in the scatter plot (in yellow). In this particular -diagram one can see that warm pixels follow the same relationship between -SUM1 and SUM2 and the colder pixels. - -I have some other question...? ------------------------------- - -Ask us a question on the `mailing list `_! diff --git a/doc/getting_started/images/data_open.png b/doc/getting_started/images/data_open.png deleted file mode 100644 index d88665ea2..000000000 Binary files a/doc/getting_started/images/data_open.png and /dev/null differ diff --git a/doc/getting_started/images/histogram.png b/doc/getting_started/images/histogram.png deleted file mode 100644 index 9c7b61cbf..000000000 Binary files a/doc/getting_started/images/histogram.png and /dev/null differ diff --git a/doc/getting_started/images/image_selectors.png b/doc/getting_started/images/image_selectors.png deleted file mode 100644 index f5a14ad1f..000000000 Binary files a/doc/getting_started/images/image_selectors.png and /dev/null differ diff --git a/doc/getting_started/images/layer_options.png b/doc/getting_started/images/layer_options.png deleted file mode 100644 index 0a768afa6..000000000 Binary files a/doc/getting_started/images/layer_options.png and /dev/null differ diff --git a/doc/getting_started/images/link_editor.png b/doc/getting_started/images/link_editor.png deleted file mode 100644 index 0f0df8a48..000000000 Binary files a/doc/getting_started/images/link_editor.png and /dev/null differ diff --git a/doc/getting_started/images/link_subset_1.png b/doc/getting_started/images/link_subset_1.png deleted file mode 100644 index 92440c950..000000000 Binary files a/doc/getting_started/images/link_subset_1.png and /dev/null differ diff --git a/doc/getting_started/images/link_subset_2.png b/doc/getting_started/images/link_subset_2.png deleted file mode 100644 index 648afe8c7..000000000 Binary files a/doc/getting_started/images/link_subset_2.png and /dev/null differ diff --git a/doc/getting_started/images/main_window.png b/doc/getting_started/images/main_window.png deleted file mode 100644 index 060cb355a..000000000 Binary files a/doc/getting_started/images/main_window.png and /dev/null differ diff --git a/doc/getting_started/images/make_screenshots.py b/doc/getting_started/images/make_screenshots.py deleted file mode 100644 index 8682ac764..000000000 --- a/doc/getting_started/images/make_screenshots.py +++ /dev/null @@ -1,95 +0,0 @@ -# This is a script that can be used to reproduce the screenshots for the -# Getting Started guide. The idea is that as we update glue, we can easily -# regenerate screenshots to make sure we include the latest ui. - -from glue.app.qt import GlueApplication -from glue.viewers.image.qt import ImageViewer -from glue.viewers.histogram.qt import HistogramViewer -from glue.viewers.scatter.qt import ScatterViewer -from glue.core.edit_subset_mode import AndNotMode, ReplaceMode -from glue.core.link_helpers import LinkSame - -ga = GlueApplication() -ga.resize(1230, 900) -ga.show() - -ga.app.processEvents() -ga.screenshot('main_window1.png') - -image = ga.load_data('w5.fits') -image.label = 'W5' - -ga.app.processEvents() -ga.screenshot('data_open.png') - -image_viewer = ga.new_data_viewer(ImageViewer, data=image) -image_viewer._mdi_wrapper.resize(450, 400) - -image_viewer.state.layers[0].v_min = 440 -image_viewer.state.layers[0].v_max = 900 -image_viewer.state.layers[0].stretch = 'sqrt' -image_viewer.state.reset_limits() - -ga.app.processEvents() -ga.screenshot('main_window2.png') - -py, px = image.pixel_component_ids -subset_state = (px > 500) & (px < 900) & (py > 300) & (py < 800) - -ga.data_collection.new_subset_group(subset_state=subset_state, label='Subset 1') - -ga.app.processEvents() -ga.screenshot('w5_west.png') - -histogram_viewer = ga.new_data_viewer(HistogramViewer, data=image) -histogram_viewer._mdi_wrapper.resize(450, 400) -histogram_viewer._mdi_wrapper.move(450, 0) -histogram_viewer.state.x_min = 400 -histogram_viewer.state.x_max = 700 -histogram_viewer.state.update_bins_to_view() -histogram_viewer.state.normalize = True - -ga.app.processEvents() - -ga.session.edit_subset_mode.mode = AndNotMode -cid = image.main_components[0] -subset_state = (cid >= 450) & (cid <= 500) -ga.session.edit_subset_mode.update(ga.data_collection, subset_state) - -ga.app.processEvents() -ga.screenshot('subset_refine.png') - -catalog = ga.load_data('w5_psc.vot') -catalog.label = 'Point Sources' - -# Set up links -link1 = LinkSame(image.id['Right Ascension'], catalog.id['RAJ2000']) -link2 = LinkSame(image.id['Declination'], catalog.id['DEJ2000']) -ga.data_collection.add_link(link1) -ga.data_collection.add_link(link2) - -scatter_viewer = ga.new_data_viewer(ScatterViewer, data=catalog) -scatter_viewer._mdi_wrapper.resize(900, 400) -scatter_viewer._mdi_wrapper.move(0, 400) -scatter_viewer.state.x_att = catalog.id['__4.5__-__5.8_'] -scatter_viewer.state.y_att = catalog.id['__4.5_'] -scatter_viewer.state.x_min = -1 -scatter_viewer.state.x_max = 1.6 -scatter_viewer.state.y_min = 1 -scatter_viewer.state.y_max = 17 - -ga.session.edit_subset_mode.mode = ReplaceMode -subset_state = (px > 300) & (px < 400) & (py > 600) & (py < 700) -ga.session.edit_subset_mode.update(ga.data_collection, subset_state) - -ga.app.processEvents() -ga.screenshot('link_subset_1.png') - -image_viewer.add_subset(catalog.subsets[0]) - -# FIXME: the following doesn't work currently -image_viewer.axes.set_xlim(250, 450) -image_viewer.axes.set_ylim(550, 750) - -ga.app.processEvents() -ga.screenshot('link_subset_2.png') diff --git a/doc/getting_started/images/modes.png b/doc/getting_started/images/modes.png deleted file mode 100644 index 04c8275de..000000000 Binary files a/doc/getting_started/images/modes.png and /dev/null differ diff --git a/doc/getting_started/images/psc_layer.png b/doc/getting_started/images/psc_layer.png deleted file mode 100644 index 288b49c83..000000000 Binary files a/doc/getting_started/images/psc_layer.png and /dev/null differ diff --git a/doc/getting_started/images/save_with_data.png b/doc/getting_started/images/save_with_data.png deleted file mode 100644 index 80c633864..000000000 Binary files a/doc/getting_started/images/save_with_data.png and /dev/null differ diff --git a/doc/getting_started/images/subset_refine.png b/doc/getting_started/images/subset_refine.png deleted file mode 100644 index a9c3b3f43..000000000 Binary files a/doc/getting_started/images/subset_refine.png and /dev/null differ diff --git a/doc/getting_started/images/w5_west.png b/doc/getting_started/images/w5_west.png deleted file mode 100644 index 7002faec6..000000000 Binary files a/doc/getting_started/images/w5_west.png and /dev/null differ diff --git a/doc/getting_started/index.rst b/doc/getting_started/index.rst deleted file mode 100644 index ec5f4d384..000000000 --- a/doc/getting_started/index.rst +++ /dev/null @@ -1,276 +0,0 @@ -.. _getting_started: - -Getting started -*************** - -This page walks through Glue's basic GUI features, using data from the W5 star -forming region as an example. You can download the data files for this tutorial here: - -* :download:`w5.tgz` (Linux and MacOS X) -* :download:`w5.zip` (Windows) - -After :ref:`installing ` Glue, you can launch it by typing:: - - glue - -or by clicking on the Glue icon in the Anaconda Navigator if using it. After a -few seconds you should see the main window of glue which looks like this: - -.. figure:: images/main_window.png - :align: center - :width: 100% - -The main window consists of 4 areas: - - A. The **data collection**. This lists all open data sets and subsets (highlighted regions). - B. The **viewer layers**. This is where you will see a list of layers in the current viewer, and be able to control the appearance of individual layers - C. The **viewer options**. This is where you will see global options for the active viewer - D. The **visualization canvas**. This is where visualization windows resides. - -Opening Data -============ - -There are multiple ways to open data: - -* By clicking on the red folder icon in the top left -* By selecting the **Open Data Set** item under the **File** menu or using the - equivalent shortcut (e.g. **Ctrl+O** on Linux, **Cmd+O** on Mac). -* By dragging and dropping data files onto the main window - - Find and open the file ``w5.fits`` which should be in the ``w5.tgz`` or - ``w5.zip`` archive you downloaded above. This is a `WISE image - `_ of the `W5 Star Forming Region - `_. While this is an astronomical - dataset, glue can be used for data in any discipline, and many of the concepts - shown below are applicable to many types of dataset. - -Plotting Data -============= - -After opening ``w5.fits``, a new entry will appear in the data manager: - -.. figure:: images/data_open.png - :align: center - :width: 100% - -To visualize a dataset, click and drag the entry from the data manager to the -visualization dashboard. A popup window asks about what kind of plot to make. -Since this is an image, select **2D Image Viewer**. - -Defining Subsets -================ - -Work in glue revolves around "drilling down" into interesting subsets within -data. Each visualization type (image, scatterplot, ...) provides different ways -for defining these subsets. In particular, the image window provides 5 options: - -.. figure:: images/image_selectors.png - :align: center - :width: 300 - -* Rectangular selection -* Horizontal range -* Vertical range -* Circular selection -* Freeform selection - -To use these, click on one of the selection icons then click and drag on the -image to define a selection. If using the polygon selection, you should press -'enter' when the selection is complete (or 'escape' to cancel). - -We can highlight the west arm of W5 using the rectangle selector: - - .. figure:: images/w5_west.png - :align: center - :width: 100% - -Notice that this highlights the relevant pixels in the image, adds a new subset -(named **Subset 1**) to the data manager, and adds a new visualization layer in -the visualization dashboard. - -We can redefine this subset by dragging a new rectangle in the image, or we can -also move around the current subset by pressing the 'control' key and clicking -on the subset then dragging it. As long as **Subset 1** is selected in the data -collection view in the top left, drawing selections will redefine **Subset 1**. -If you deselect this subset and draw a new region, a new subset will be -created. - -You can edit the properties of a visualization layer (color, transparency, etc.) -by clicking on the layer in the **Plot layers** list on the left. Likewise, you -can re-arrange the rows in this widget to change the order in which each layer -is drawn -- the top entry will appear above all other entries. - -Refining Subsets and Linked Views -================================= - -Visualizations are linked in Glue -- that is, we can plot this data in many -different ways, to better understand the properties of each subset. To see this, -click and drag the **W5[PRIMARY]** entry into the visualization area a second -time, and make a histogram. Edit the settings in the histogram visualization -dashboard to produce something similar to this: - - .. figure:: images/histogram.png - :align: center - :width: 100% - -This shows the distribution of intensities for the image as a whole (gray), and -for the subset in red (the label **PRIMARY** comes from the FITS header) - -Perhaps we wish to remove faint pixels from our selection. To do this, we pick -the last mode (**Remove From Selection**) from the selection mode toolbar: - - .. figure:: images/modes.png - :align: center - :width: 300 - -When this mode is active, new regions defined by the mouse are subtracted from -the selected subsets. We can therefore highlight the region between x=450-500 in -the histogram to remove this region from the data. - -.. figure:: images/subset_refine.png - :align: center - :width: 100% - -.. note:: Make sure you switch back to the first, default selection mode - (**Replace Selection**) once you have finished defining the - selection. - -Linking Data -============ - -.. _getting_started_link: - -Glue is designed so that visualization and drilldown can span multiple datasets. -To do this, we need to inform Glue about the logical connections that exist -between each dataset. - -Open the second file, ``w5_psc.vot`` -- a catalog of *Spitzer*-identified point -sources towards this region. You will see a new entry in the data manager. We -can double click on that entry to rename it to **Point Sources**, and the result -will look like this: - -.. figure:: images/psc_layer.png - :align: center - :width: 400px - -At this point, you can visualize and drilldown into this catalog. However, Glue -doesn't know enough to compare the catalog and image. To do that, we must -*Link* these two data entries. Click on the ``Link Data`` button in the toolbar. -This brings up a new window, showing all the pieces of information within each -dataset: - -.. figure:: images/link_editor.png - :align: center - :width: 600px - -Select the two datasets in the network diagram in the top panel, or from the -drop-down menus underneath. The image has an attribute **Right Ascension**. This -is the same quantity as the **RAJ2000** attribute in the **Point Sources** -dataset -- they are both describing Right Ascension (the horizontal spatial -coordinate on the sky). Select these entries, and click **Glue** to instruct the -program that these quantities are equivalent. Likewise, link **Declination** and -**DEJ2000** (Declination, the other coordinate). Click **OK**. - -.. note:: What does this do? This tells Glue how to derive the catalog-defined - quantities **DEJ2000** and **RAJ2000** using data from the image, and - vice versa. In this case, the derivation is simple (it aliases the - quantity **Declination** or **Right Ascension**). In general, the - derivation can be more complex (i.e. an arbitrary function that maps - quantities in the image to a quantity in the catalog). Glue uses this - information to apply subset definitions to different data sets, - overplot multiple datasets, etc. - -After these connections are defined, subsets that are defined via spatial -constraints in the image can be used to filter rows in the catalog. Let's see -how that works. - -First, make a scatter plot of the point source catalog. Then, select **Subset -1** and draw a new region on the image. You should see this selection applied to -all plots: - -.. figure:: images/link_subset_1.png - :align: center - :width: 100% - -You can also overplot the catalog rows on top of the image. To do this, click -the arrow next to the new subset -- this shows the individual selections applied -to each dataset. Click and drag the subset for the point source catalog on top -of the image. To see these points more easily, you may want to disable the -layer showing all the points (named **Point Sources**) in the list of plot -layers. - -.. figure:: images/link_subset_2.png - :align: center - :width: 100% - - -Glue is able to apply this filter to both datasets because it has enough -information to apply the spatial constraint in the image (fundamentally, a -constraint on **Right Ascension** and **Declination**) to a constraint in the -catalog (since it could derive those quantities from the **RAJ2000** and -**DEJ2000** attributes). - -.. tip:: - - Glue stores subsets as sets of constraints -- tracing a rectangle - subset on a plot defines a set of constraints on the - quantities plotted on the x and y axes (left < x < right, bottom < - y < top). Copying a subset copies this definition, and pasting it - applies the definition to a different subset. - -As was mentioned above, the highlighted subsets in the data manager are the ones -which are affected by selecting regions in the plots. Thus, instead of manually -copy-pasting subsets from the image to the catalog, you can also highlight both -subsets before selecting a plot region. This will update both subsets to match -the selection. - -.. note:: Careful readers will notice that we didn't use the image subset - from earlier sections when working with the catalog. This is because - that selection combined spatial constraints (the original rectangle in - the image) with a constraint on intensity (the histogram selection). - There is no mapping from image intensity to quantities in the catalog, - so it isn't possible to filter the catalog on that subset. In - situations where Glue is unable to apply a filter to a dataset, it - doesn't render the subset in the visualization. - -.. _saving_session: - -Saving your work -================ - -Glue provides a number of ways to save your work, and to export your work for -further analysis in other programs. - -**Saving The Session** - -You can save a Glue session for later work via the **Save Session** button in -the toolbar or in the **File** menu. This creates a glue session file (the -preferred file extension is ``.glu``). You can restore this session later via -the **Open Session** button in the toolbar or in the **File** menu. - -By default, these files store references to the files you opened, and not -copies of the files themselves. Thus, you won't be able to re-load this -session if you move any of the original data. To include the data in the -session file, you can select 'Glue Session including data' when saving: - -.. figure:: images/save_with_data.png - :align: center - :width: 400px - -**Saving Plots** - -Static images of individual visualizations can be saved by clicking the floppy -disk icon on a given visualization window. There are also exporters available -under the **File** menu - built-in exporters include one to export plots to the -`plotly `_ service, and one to export plots using -`D3PO `_. - -**Saving Subsets** - -Glue is primarily an exploration environment -- eventually, you may want to -export subsets for further analysis. Glue currently supports saving subsets as -FITS masks. Right click on the subset in the data manager (note that you need to -select the subset applied to a specific dataset, not the overall subset, so be -sure to expand the subset by clicking on the triangle on the left of the subset -name), and select **Export subset values** or **Export subset mask(s)** to write -the subset to disk. diff --git a/doc/getting_started/w5.tgz b/doc/getting_started/w5.tgz deleted file mode 100644 index b79a16243..000000000 Binary files a/doc/getting_started/w5.tgz and /dev/null differ diff --git a/doc/getting_started/w5.zip b/doc/getting_started/w5.zip deleted file mode 100644 index 2b5dc1580..000000000 Binary files a/doc/getting_started/w5.zip and /dev/null differ diff --git a/doc/gui_guide/3d_viewers.rst b/doc/gui_guide/3d_viewers.rst deleted file mode 100644 index 2b8a8d3ab..000000000 --- a/doc/gui_guide/3d_viewers.rst +++ /dev/null @@ -1,109 +0,0 @@ -.. _viewers-3d: - -3D scatter and volume rendering viewers -======================================= - -A plugin with 3D viewers for glue, powered by `VisPy `_, -is available. Provided that you installed glue with ``conda`` or with ``pip``, -you should already have the 3D viewers available. You can check this by going to -the **Canvas** menu in glue and selecting **New Data Viewer**, or alternatively -by dragging a dataset onto the canvas area. If the 3D viewers plugin is -installed, you should see the 3D viewers in the list: - -.. image:: images/3d_viewers_select.png - :align: center - :width: 339 - -If you don't see these in the list, then if you are using -`Anaconda `_ to manage your Python -distribution, you can install the 3D viewers plugin using:: - - conda install -c glueviz glue-vispy-viewers - -With the plugin installed, you will now have access to two new data -viewers in Glue: - -* 3D scatter viewer -* 3D volume rendering viewer - -Please read on to understand the current functionality and limitations of each -viewer. If you are interested in getting involved in implementing some of the -missing functionality or fixing known issues, please join the `glue-viz-dev -`_ mailing list and let -us know! - -Common options for all viewers ------------------------------- - -All 3D viewers share a similar panel in the bottom left: - -.. image:: images/common_options.png - :align: center - :width: 300px - -In addition, all viewers will show a rectangular 3D box, which by default -is a cube. The edges of the cube correspond to the min/max values in the -options panel above. If these values are changed, the data is renormalized -inside the cube, but the white cube stays the same. It is also possible to -instead stretch the cube into a rectangular box with a different size in each -dimension, using the sliders. This can be useful in the case of a volume -rendering, if the number of pixels is very different along different axes. - -3D Scatter Plot Viewer ----------------------- - -The 3D scatter plot viewer is the most feature-complete of the viewers at the -moment. It is currently able to: - -* Display markers based on any 3 data components -* Highlight subsets made in other viewers -* Control the visual appearance of both data and subsets -* Color code markers using any component -* Set the size of markers to depend on any component -* Scale the absolute size and the transparency of markers in each layer using - sliders - -The following visualization shows a 3D scatter plot with the location of -earthquakes around the globe, color coded by depth and with the size set by the -magnitude of the earthquake: - -.. image:: images/3d_scatter.jpg - :align: center - :width: 600px - -At the moment this viewer is not able to show multiple datasets (just one -dataset with subsets), but this will be added shortly. - -3D Volume Rendering Viewer --------------------------- - -The 3D volume rendering is currently able to show only 3D datasets (not e.g. 4D -cubes sliced along one dimension). The viewer is able to also highlight subsets -made in other viewers: - -.. image:: images/3d_volume.jpg - :align: center - :width: 600px - -At the moment, the 3D viewer is only able to show multiple datasets if they are -on the same grid. Note that for this viewer, the limits in the options panel in -the bottom left are in pixel coordinates, not world coordinates. - -3D Isosurface Viewer --------------------- - -A 3D isosurface viewer is in development. This viewer is still highly -experimental and currently very slow for displaying isosurfaces. In addition, it -is only able to show a single isosurface level. We do not recommend using it at -this time, and have disabled it by default. If you are interested in trying it -out, see the `README.md -`_ file in -the glue-vispy-viewers repository. - -Reporting issues ----------------- - -Please report any issues with the 3D viewers in the following `issue tracker -`_. Please first check that -there is not already a similar issue open -- if there is, please feel free to -comment on that issue to let us know you ran into that problem too! diff --git a/doc/gui_guide/components.rst b/doc/gui_guide/components.rst deleted file mode 100644 index 7749281a9..000000000 --- a/doc/gui_guide/components.rst +++ /dev/null @@ -1,50 +0,0 @@ -.. _new_components: - -Defining New Attributes -======================= - -New attributes of data items can be easily created from mathematical -operations on existing attributes. In this section, we define new -attributes for the W5 Point Source catalog from the -:ref:`tutorial `. - -To start off, click on the **Arithmetic Attributes** tool in the toolbar: - -.. figure:: images/arithmetic_toolbar.png - :align: center - :width: 350 - -A new window will appear for defining attributes. To start off, use the -drop-down menu to select the dataset for which you want to create an arithmetic -attribute. Then, click on **New arithmetic attribute**, which will open up the -equation editor. From here, set the new attribute name, then type the expression -in the box provided. Attribute names should be included in curly brackets, e.g. -``{ID}``. You can either type attribute names directly, using tab-completion -if necessary, or you can also select an attribute from the list of available -attributes and click on **Insert** to insert it into the expression. - -Here we define a new attribute ``J - K`` to be the difference between the J and -the Ks magnitudes: - -.. figure:: images/arithmetic_equation.png - :align: center - :width: 500 - -After clicking **OK**, the new attribute is listed in the list of arithmetic -attributes: - -.. figure:: images/arithmetic_defined.png - :align: center - :width: 500 - -At this point, you could if you wished define additional attributes, or go back -and edit existing ones. Clicking **OK** again will return to the main glue -interface, from which you can now use the new arithmetic attribute(s). - -Note that the expression line can include Numpy functions (prefaced with -``np.``), and anything else you import in your config.py file for Glue. For -example, you could define a normalized version of ``Jmag`` using: - -.. figure:: images/arithmetic_numpy.png - :align: center - :width: 500 diff --git a/doc/gui_guide/dendro.rst b/doc/gui_guide/dendro.rst deleted file mode 100644 index 551cfe171..000000000 --- a/doc/gui_guide/dendro.rst +++ /dev/null @@ -1,44 +0,0 @@ -Visualizing Astronomical Dendrograms -==================================== - -You can use Glue to visualize dendrograms created by the -`astrodendro `_ package. - -Enabling the viewer -------------------- - -The dendrogram viewer will be automatically available if the `astrodendro`_ -package is installed. To install it, simply use:: - - pip install astrodendro - -Building a dendrogram ---------------------- - -The details of constructing dendrograms for astronomical images -is beyond the scope of this document -- see ``_ -for more information. The following snippet builds a dendrogram -from the W5 image used in the :ref:`tutorial `:: - - from astropy.io import fits - from astrodendro import Dendrogram - - data = fits.getdata('W5.fits') - dg = Dendrogram.compute(data, min_value=500, min_npix=50) - dg.save_to('w5_dendro.fits') - -Next, load this file into Glue, choosing "Dendrogram" as a file type. -You can now visualize the W5 dendrogram alongside its image: - -.. figure:: images/dendro.png - :align: center - :width: 400px - -Linking to Catalog Properties ------------------------------ - -If you have used astrodendro to compute a catalog of structure properties, -you can visualize that in Glue as well. The best way to do this is to -save the catalog as a table, load it into Glue, and -:ref:`merge it ` with the dendrogram dataset. This will -supplement the dendrogram with the additional catalog-derived properties. diff --git a/doc/gui_guide/images/3d_scatter.jpg b/doc/gui_guide/images/3d_scatter.jpg deleted file mode 100644 index c88dcc94d..000000000 Binary files a/doc/gui_guide/images/3d_scatter.jpg and /dev/null differ diff --git a/doc/gui_guide/images/3d_viewers_select.png b/doc/gui_guide/images/3d_viewers_select.png deleted file mode 100644 index 0fbee1cc6..000000000 Binary files a/doc/gui_guide/images/3d_viewers_select.png and /dev/null differ diff --git a/doc/gui_guide/images/3d_volume.jpg b/doc/gui_guide/images/3d_volume.jpg deleted file mode 100644 index 948e4d744..000000000 Binary files a/doc/gui_guide/images/3d_volume.jpg and /dev/null differ diff --git a/doc/gui_guide/images/arithmetic_defined.png b/doc/gui_guide/images/arithmetic_defined.png deleted file mode 100644 index 15fec779d..000000000 Binary files a/doc/gui_guide/images/arithmetic_defined.png and /dev/null differ diff --git a/doc/gui_guide/images/arithmetic_equation.png b/doc/gui_guide/images/arithmetic_equation.png deleted file mode 100644 index 33bd412e3..000000000 Binary files a/doc/gui_guide/images/arithmetic_equation.png and /dev/null differ diff --git a/doc/gui_guide/images/arithmetic_numpy.png b/doc/gui_guide/images/arithmetic_numpy.png deleted file mode 100644 index 347c0a720..000000000 Binary files a/doc/gui_guide/images/arithmetic_numpy.png and /dev/null differ diff --git a/doc/gui_guide/images/arithmetic_toolbar.png b/doc/gui_guide/images/arithmetic_toolbar.png deleted file mode 100644 index b54a28ac3..000000000 Binary files a/doc/gui_guide/images/arithmetic_toolbar.png and /dev/null differ diff --git a/doc/gui_guide/images/common_options.png b/doc/gui_guide/images/common_options.png deleted file mode 100644 index b63674e2d..000000000 Binary files a/doc/gui_guide/images/common_options.png and /dev/null differ diff --git a/doc/gui_guide/images/dendro.png b/doc/gui_guide/images/dendro.png deleted file mode 100644 index 8f1a51903..000000000 Binary files a/doc/gui_guide/images/dendro.png and /dev/null differ diff --git a/doc/gui_guide/images/galaxy_slice.png b/doc/gui_guide/images/galaxy_slice.png deleted file mode 100644 index b357d9700..000000000 Binary files a/doc/gui_guide/images/galaxy_slice.png and /dev/null differ diff --git a/doc/gui_guide/images/galaxy_slice_extracted.png b/doc/gui_guide/images/galaxy_slice_extracted.png deleted file mode 100644 index 83de6b309..000000000 Binary files a/doc/gui_guide/images/galaxy_slice_extracted.png and /dev/null differ diff --git a/doc/gui_guide/images/glue_slice.png b/doc/gui_guide/images/glue_slice.png deleted file mode 100644 index f1714443a..000000000 Binary files a/doc/gui_guide/images/glue_slice.png and /dev/null differ diff --git a/doc/gui_guide/images/link_dialog.png b/doc/gui_guide/images/link_dialog.png deleted file mode 100644 index b28bfdc6b..000000000 Binary files a/doc/gui_guide/images/link_dialog.png and /dev/null differ diff --git a/doc/gui_guide/images/link_dialog_2.png b/doc/gui_guide/images/link_dialog_2.png deleted file mode 100644 index ee2c7f190..000000000 Binary files a/doc/gui_guide/images/link_dialog_2.png and /dev/null differ diff --git a/doc/gui_guide/images/link_dialog_3.png b/doc/gui_guide/images/link_dialog_3.png deleted file mode 100644 index 0c4d93bb1..000000000 Binary files a/doc/gui_guide/images/link_dialog_3.png and /dev/null differ diff --git a/doc/gui_guide/images/link_toolbar.png b/doc/gui_guide/images/link_toolbar.png deleted file mode 100644 index a8d242597..000000000 Binary files a/doc/gui_guide/images/link_toolbar.png and /dev/null differ diff --git a/doc/gui_guide/images/profile_data.png b/doc/gui_guide/images/profile_data.png deleted file mode 100644 index 0969bcd9c..000000000 Binary files a/doc/gui_guide/images/profile_data.png and /dev/null differ diff --git a/doc/gui_guide/images/profile_subset.png b/doc/gui_guide/images/profile_subset.png deleted file mode 100644 index 449b54b84..000000000 Binary files a/doc/gui_guide/images/profile_subset.png and /dev/null differ diff --git a/doc/gui_guide/images/spectrum_button.png b/doc/gui_guide/images/spectrum_button.png deleted file mode 100644 index d9c4d0770..000000000 Binary files a/doc/gui_guide/images/spectrum_button.png and /dev/null differ diff --git a/doc/gui_guide/index.rst b/doc/gui_guide/index.rst deleted file mode 100644 index 48cd8c4d4..000000000 --- a/doc/gui_guide/index.rst +++ /dev/null @@ -1,12 +0,0 @@ -Advanced User Interface Guide -============================= - -.. toctree:: - :maxdepth: 2 - - link_tutorial.rst - merging.rst - components.rst - 3d_viewers.rst - spectrum.rst - slice.rst diff --git a/doc/gui_guide/link_tutorial.rst b/doc/gui_guide/link_tutorial.rst deleted file mode 100644 index 7b196b668..000000000 --- a/doc/gui_guide/link_tutorial.rst +++ /dev/null @@ -1,107 +0,0 @@ -.. _linking: - -How Data Linking Works -====================== - -.. currentmodule: glue.core - -Glue makes it possible to compare different, interrelated datasets. For example, Glue allows you to: - -* Overlay scatterplots of the positions of objects in two different catalogs -* Select a region of interest in an image, and use this spatial constraint to filter a catalog with position information -* Overlay histograms that compare mass distributions of two different datasets. - -To do this, Glue needs to understand how quantities in different datasets relate to each other: - -* Sometimes, two datasets define the same quantity (e.g., two catalogs that both report time) -* Sometimes, datasets define the same quantities in different units (elapsed time in hours vs elapsed time in days) -* Sometimes, a quantity (like area) can be derived from other quantities (like length and width). - -**Data Links** tell Glue how to translate between different quantities, -to compare different datasets. - -.. note:: - - **Are data links like table joins?** If you are familiar with - concepts from SQL, R, or Pandas, you might think data links are like - data mergers or joins. They are different -- mergers assume - information about the **same entity** is present in many tables, - such that the different tables can in principle be merged together. - Data Links in glue, on the other hand, assume that the entries in - different datasets correspond to different entities, but may - describe the **same quantity**. For example, an image and a position - catalog both have spatial information, but no row in the catalog - represents a pixel in the image. Data mergers are not yet supported - in Glue. - - -Data Linking from the GUI -------------------------- - -The :ref:`Data Linking Editor ` lets users -define data links from the GUI. To start linking data, click on the **Link -Data** button in the toolbar: - -.. image:: images/link_toolbar.png - :align: center - :width: 400 - -This will open up a dialog where you can set up links between any of the open -datasets: - -.. image:: images/link_dialog.png - :align: center - :width: 600 - -The first step is to select the two datasets you want to link. If there are -only two datasets present, they will be selected by default. Otherwise, click on -the datasets you want to link in the graphical representation, or use the -drop-down menus. - -The simplest link occurs when two datasets define the same quantity in -the same units. In this case, Glue can trivially overplot visualizations in both -datasets. For example, in the image above, both datasets (a catalog and an -image) both have the same RA and Dec spatial information (RA and Dec are -essentially latitude and longitude coordinates on the sky). To link these -quantities, we highlight the equivalent quantities, and click "Glue": - -.. image:: images/link_dialog_2.png - :align: center - :width: 600 - -Note that the graph in the top half of the dialog now shows the two datasets -as being linked - note that in this representation, one link is enough to cause -the two datasets to be connected by a line, but don't forget that in some cases -you may need to link multiple attributes between two datasets. - -In the more general case, one quantity can be computed from one or more others, -but is not identical to another quantity. The ``advanced`` tab let's us -specify how to use a translation function to derive one quantity from others: - -.. image:: images/link_dialog_3.png - :align: center - :width: 600 - -Here, a ``boxes`` dataset reports the linear dimensions of some boxes, and a -crates dataset reports the volume of crates. The box volumes can be -compared with the crate volumes by multiplying the box width, height, and -depth. To specify this link, we select a translation function -(``lengths_to_volume``), and drag the components to the relevant inputs and -output of the translation function. - -Note that this link is one-way: we can compute area from width height -and depth, but not vice versa. Thus, we will be able to overlay -information about box volume on a plot of crate volume, but not any -information about crate height. - - -Links Propagate ---------------- - -Glue knows how to string links together. For example, consider 4 datasets which -report masses in kilograms. There are 6 pairs of equivalent mass quantities -(``m1<->m2, m1<->m3, m1<->m4, m2<->m3, m2<->m4, m3<->m4``). However, you need -only define 3 links (say, ``m1<->m2, m1<->m3, m1<->m4``). Even though there is -no explicit link between ``m2<->m3``, Glue knows they are equivalent (since -``m3<->m1<->m2``). Glue will always be able to figure out these "chains" of -connections. diff --git a/doc/gui_guide/merging.rst b/doc/gui_guide/merging.rst deleted file mode 100644 index 435a037d0..000000000 --- a/doc/gui_guide/merging.rst +++ /dev/null @@ -1,50 +0,0 @@ -.. _merging: - -Merging Datasets -================ - -If several of your files describe the same items, you can merge them into a -single Glue :class:`~glue.core.data.Data` object. Examples of files that make -sense to merge together include: - - - Two or more images that are pixel-aligned to each other - - Several catalogs whose rows describe the same objects - -Why merge? ----------- - -For multi-dimensional visualizations (like a scatter plot, or an RGB image), -merging datasets allows you to combine attributes from two different files -into a single visualization. It also guarantees that any subset defined -using attributes from one file can be applied to the entries in another file. - -Merging vs Linking ------------------- - -Merging is a different operation than :ref:`linking `. The easiest -way to appreciate the difference is to think of spreadsheet-like data. -In Glue, linking two datasets defines a conceptual relationship between -the **columns** of a spreadsheet (e.g., two spreadsheets have a column -called "age", but row N describes a different object in each spreadsheet). - -Merging, on the other hand, indicates that two spreadsheets are pre-aligned -along each **row** (e.g. row N describes the same item in every spreadsheet, but -the columns of each spreadsheet might be different). - -Merging collapses several datasets into a single dataset, while -linking keeps each dataset separate. - -How to merge datasets ---------------------- - -You can merge datasets by highlighting the relevant datasets in the left panel, -right-clicking, and selecting **Merge datasets**. - -To merge datasets programmatically, use the :meth:`DataCollection.merge -` method. - -.. note:: - - Datasets should only be merged if each element describes the same item - in each file. Consequently, all merged datasets must have the same - number of elements. diff --git a/doc/gui_guide/slice.rst b/doc/gui_guide/slice.rst deleted file mode 100644 index 192bb6f65..000000000 --- a/doc/gui_guide/slice.rst +++ /dev/null @@ -1,56 +0,0 @@ -.. _slice: - - -================ -Slice Extraction -================ - -When visualizing image cubes, Glue's image viewer extracts axis-parallel slices -through the data. You can also extract slices from *arbitrary* paths through -the data, using the slice tool in the image viewer: - -.. figure:: images/glue_slice.png - :align: center - :width: 50px - -Activate this mode and click (or click+drag) a path on an image: - -.. figure:: images/galaxy_slice.png - :align: center - :width: 400px - -Hitting escape will reset the path. Hitting enter will extract this -slice from the original cube, and display it in a new window: - -.. figure:: images/galaxy_slice_extracted.png - :align: center - :width: 400px - -The slice plot is linked to the original image viewer (of course!), so that -click+dragging on the slice window will update the orientation of the -image window. - -This video demonstrates the process, and also shows the power of combining -slice extraction and spectrum extraction. - -.. raw:: html - -
- -
-
- -This kind of slice extraction is especially useful for spectral cube analysis, -since the extracted images are position-velocity diagrams. However, they can -be useful in other contexts as well. For example, here's a screenshot showing -an on-the-fly cross section of a brain tumor MRI: - -.. raw:: html - -
- -
-
- -The script used to load this data into Glue can be found `here `__. - diff --git a/doc/gui_guide/spectrum.rst b/doc/gui_guide/spectrum.rst deleted file mode 100644 index 6ca91022c..000000000 --- a/doc/gui_guide/spectrum.rst +++ /dev/null @@ -1,68 +0,0 @@ -.. _spectrum: - -============== -Profile Viewer -============== - -When using the image viewer on data with 3 or more dimensions, you have the -option of extracting and analyzing 1D profiles (for Astronomers dealing with -spectral cubes, this would be a spectrum). To compute a profile, click the -profile button on an image viewer: - -.. figure:: images/spectrum_button.png - :align: center - -This will create a 1D profile viewer with the data collapsed along all but one -axis (which you can select in the bottom left): - -.. figure:: images/profile_data.png - :align: center - :width: 600px - -You can also create 1D profiles directly by dragging datasets onto the canvas -rather than going via the image viewer. If the dataset has subsets, these will -also result in profiles being computed in the profile viewer. If you don't have -any subsets yet, try and select a region in the image viewer, and you will then -see the collapsed profile for that region in the profile viewer: - -.. figure:: images/profile_subset.png - :align: center - :width: 600px - -Interaction Modes -================= - -The profile viewer has various advanced features that we now describe. To access -these, click on the 'Options' button in the toolbar of the profile viewer. - -Navigation -^^^^^^^^^^ - -The default navigation mode inside the spectrum window is **Navigation**: by -clicking on a part of the spectrum (or by dragging the vertical handle), you -will set which slice of the data is shown in any open image viewer showing the -same data. - -Profile Fitting -^^^^^^^^^^^^^^^^ - -By clicking on the **Fit** tab, you can fit a model to the extracted spectrum. -In this mode, you can click and drag on the spectrum to define a range over -which to fit a mode. You can edit the range by dragging either of the edges. -Clicking the fit button will add a best-fit model(s) to the plot (one model for -each dataset and one for each subset). The dropdown lets you choose which model -to fit to the data. - -Different models have different settings, which you can adjust by clicking on -the settings button. For example, the Gaussian fitter allows you to fix certain -parameters, or limit them to specific ranges. For more information about -customizing the models and/or fitting process, see :ref:`custom-fitting`. - -Cube Collapse -^^^^^^^^^^^^^ - -The **Collapse** tab allows you to partially collapse the cube, and send the -result back to the image viewer. As for fitting, you can draw and edit a range -over which to collapse the data. Clicking 'collapse' will temporarily show a -collapsed view of the data over the range in any image viewer currently showing -the same data. diff --git a/doc/help.rst b/doc/help.rst deleted file mode 100644 index 3783a394b..000000000 --- a/doc/help.rst +++ /dev/null @@ -1,12 +0,0 @@ -.. _help: - -Getting Help with Glue -====================== - -If you have questions about Glue, we'd love to hear from you. There are several ways to get in touch: - -* Ask us on `Twitter `_. -* Post a question on the `Glue mailing list `_. -* Join the `Slack channel `_ - note that you will need to first get an account `here `_ -* For specific bug reports, open a new issue on the `GitHub page `_. -* Send feedback from Glue itself by selecting ``Send Feedback`` from the help menu. diff --git a/doc/images/anaconda_launcher.jpg b/doc/images/anaconda_launcher.jpg deleted file mode 100644 index f4f1c25ee..000000000 Binary files a/doc/images/anaconda_launcher.jpg and /dev/null differ diff --git a/doc/images/manage_conda_channels.jpg b/doc/images/manage_conda_channels.jpg deleted file mode 100644 index c6179407a..000000000 Binary files a/doc/images/manage_conda_channels.jpg and /dev/null differ diff --git a/doc/images/two_images.png b/doc/images/two_images.png deleted file mode 100644 index a28afa185..000000000 Binary files a/doc/images/two_images.png and /dev/null differ diff --git a/doc/index.rst b/doc/index.rst index 0a3c2ee41..51c2051bf 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -1,186 +1,12 @@ -Glue Documentation -================== +glue-core documentation +======================== -.. figure:: ../glue/logo.png - :align: center +Glue is a Python library to explore relationships within and among related datasets. -Glue is a Python library to explore relationships within and among related datasets. Its main features include: +This is the documentation for the glue-core frontend-independent library for the +glue project, and is primary intended for advanced users and developers. For the +main documentation about the desktop glue application, see http://docs.glueviz.org. -* **Linked Statistical Graphics.** With Glue, users can create scatter plots, histograms and images (2D and 3D) of their data. Glue is focused on the brushing and linking paradigm, where selections in any graph propagate to all others. -* **Flexible linking across data.** Glue uses the logical links that exist between different data sets to overlay visualizations of different data, and to propagate selections across data sets. These links are specified by the user, and are arbitrarily flexible. -* **Full scripting capability.** Glue is written in Python, and built on top of its standard scientific libraries (i.e., Numpy, Matplotlib, Scipy). Users can easily integrate their own python code for data input, cleaning, and analysis. - -.. raw:: html - -
- -
- -For more demos, check out the :ref:`videos ` page. You can also -`follow us on Twitter `_ for previews of upcoming -functionality! - -**The latest release of glue is v1.0.x** - find out :ref:`what's new in glue! ` - -Getting started ---------------- - -Glue is designed with "data-hacking" workflows in mind, and can be used in -different ways. For instance, you can simply make use of the graphical Glue -application as is, and never type a line of code. However, you can also -interact with Glue via Python in different ways: - -* Using the IPython terminal built-in to the Glue application -* Sending data in the form of NumPy arrays or Pandas DataFrames - to Glue for exploration from a Python or IPython session. -* Customizing/hacking your Glue setup using ``config.py`` files, including - automatically loading and clean data before starting Glue, writing custom - functions to parse files in your favorite file format, writing custom - functions to link datasets, or creating your own data viewers. - -Glue thus blurs the boundary between GUI-centric and code-centric data -exploration. In addition, it is also possible to develop your own plugin -packages for Glue that you can distribute to users separately, and you can also -make use of the Glue framework in your own application to provide data linking -capabilities. - -To see how glue compares with other open-source and commercial data visualization -solutions, you can view `this comparison table `_. - -In the following sections, we cover the different ways of using Glue from the -Glue application to the more advanced ways of interacting with Glue from Python. - -.. note:: For any questions or help with using glue, you can always join the - `user support mailing list `_ - or ask questions on `Slack `_ - (note that you will need to sign up for an account `here `_). - -Using the Glue application --------------------------- - -.. toctree:: - :maxdepth: 2 - - installation/installation.rst - getting_started/index.rst - gui_guide/index.rst - -Interacting with data from Python ---------------------------------- - -.. toctree:: - :maxdepth: 1 - - python_guide/ipython_terminal.rst - python_guide/data_tutorial.rst - python_guide/glue_from_python.rst - python_guide/data_translation.rst - -Domain-specific guides ----------------------- - -.. toctree:: - :maxdepth: 1 - - gui_guide/dendro.rst - -Customizing/Hacking Glue ------------------------- - -.. toctree:: - :maxdepth: 1 - - customizing_guide/introduction.rst - customizing_guide/available_plugins.rst - customizing_guide/configuration.rst - customizing_guide/customization.rst - customizing_guide/writing_plugin.rst - customizing_guide/coordinates.rst - python_guide/data_viewer_options.rst - customizing_guide/custom_viewer.rst - python_guide/liveupdate.rst - customizing_guide/fitting.rst - customizing_guide/units.rst - -Advanced customization ----------------------- - -.. toctree:: - :maxdepth: 1 - - customizing_guide/viewer.rst - customizing_guide/qt_viewer.rst - customizing_guide/matplotlib_qt_viewer.rst - customizing_guide/toolbar.rst - developer_guide/data.rst - -Getting help ------------- - -.. toctree:: - :maxdepth: 1 - - videos.rst - faq.rst - help.rst - known_issues.rst - -.. _architecture: - -The Glue architecture ---------------------- - -The pages below take you through the main infrastructure in Glue, and in -particular how selections, linking, and communications are handled internally. -You don't need to understand all of this in order to get started with -contributing, but in order to tackle some of the more in-depth issues, this -will become important. This is not meant to be a completely exhaustive guide, -but if there are areas that you feel could be explained better, or are missing -and would be useful, please let us know! - -.. toctree:: - :maxdepth: 1 - - developer_guide/selection.rst - developer_guide/communication.rst - developer_guide/linking.rst - -Information on the Data framework is available in :ref:`data_tutorial` and is not repeated here. - -.. _devdocs: - -Developing Glue ---------------- - -.. toctree:: - :maxdepth: 2 - - developer_guide/developer_guide.rst - -Acknowledging glue ------------------- - -If you use glue for research presented in a publication, please consider citing -the following two references: - -* `Beaumont et al. (2015), Hackable User Interfaces In Astronomy with - Glue `_ - -* `Robitaille et al (2017) glueviz v0.13.1: multidimensional data exploration - `_ - -The first is a conference proceedings describing glue, while the second is the -software itself. - -Publications ------------- - -* `Goodman et al. (2012), Principles of high-dimensional data - visualization in astronomy `_ -* `Beaumont et al. (2015), Hackable User Interfaces In Astronomy with - Glue `_ -* `Robitaille et al (2017) glueviz v0.10: multidimensional data exploration - `_ API --- @@ -188,11 +14,4 @@ API .. toctree:: :maxdepth: 1 - developer_guide/api.rst - -Indices and tables -================== - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` + api.rst diff --git a/doc/installation/conda.rst b/doc/installation/conda.rst deleted file mode 100644 index 782c4aaca..000000000 --- a/doc/installation/conda.rst +++ /dev/null @@ -1,112 +0,0 @@ - -Anaconda Python distribution -============================ - -**Platforms:** MacOS X, Linux, and Windows - -We recommend using the `Anaconda `__ Python -distribution from Continuum Analytics (or the related `Miniconda `_ distribution). -Anaconda includes all of Glue's main dependencies. There are two ways of -installing Glue with the Anaconda Python Distribution: :ref:`graphically using the -Anaconda Navigator `, or :ref:`using the conda command -` on the command-line, both of which are described -below. - -.. _anaconda_cli: - -Command-line installation -------------------------- - -Once Anaconda (or Miniconda) is installed, we recommend installing glue using -the ``conda`` command on the command-line rather than using the -:ref:`anaconda_gui`, because errors are more visible on the command-line if you -run into any issues during the installation. - -First, make sure that your conda command is up to date:: - - conda update -n root conda - -then install glue with:: - - conda install -c glueviz glueviz=1.2 - -This will install the latest version of glue from the ``glueviz`` conda channel. - -If you run into any issues, even after having updated ``conda``, see the -`Troubleshooting`_ section below. To update glue in future, use the same install -command as above. - -.. _anaconda_gui: - -Graphical User Interface ------------------------- - -If you prefer to not use the command-line to install glue, you can also use the -Anaconda navigator, but be aware that it is harder to diagnose issues when -things go wrong (the navigator can sometimes silently fail). Once Anaconda is -installed, go to the **Applications** folder and launch the **Anaconda -Navigator**: - -.. image:: images/navigator_icon.png - :align: center - :width: 80 - -If you do not have the Anaconda Navigator icon, but have an Anaconda Launcher, -you are using an old version of Anaconda, and we recommend that you update to -the latest version. - -Assuming you have the navigator open, before installing glue first click on the -**Channels** button: - -.. image:: images/navigator_channels_button.png - :align: center - :width: 373 - -If not already present, add **glueviz** to the list of channels by clicking -on **Add**, typing **glueviz**, and pressing enter, then click on **Update -channels**: - -.. image:: images/navigator_channels_dialog.png - :align: center - :width: 414 - -You can now install the latest version of glue by clicking on **Install**: - -.. image:: images/navigator_install.png - :align: center - :width: 264 - -Once the installation is complete, you can click on the **Launch** button (which -will replace the **Install** button). If updates become available in future, -these should be shown in the Navigator. - -Troubleshooting ---------------- - -If you managed to install glue but it does not launch or you have issues with -viewers not being available or not working correctly, the first thing to try -is to update all your existing conda packages using:: - - conda update -c glueviz --all - -In some cases, glue won't even install due to conflicts between the version of -dependencies required by glue and that required by other packages. The easiest -way to avoid this is to install glue in a separate environment. To do this, -first create an environment in which you will install glue:: - - conda create -n glueviz-env python - -This will create an environment called ``glueviz`` in which Python will be -installed. You only need to create the environment once. Once created, you can -switch to the environment with:: - - source activate glueviz-env - -Then, install glue as indicated in :ref:`anaconda_cli` using:: - - conda install -c glueviz glueviz - -Whenever you open a new terminal, if you want to run glue you should then -remember to switch to the ``glueviz-env`` environment using the ``source -activate`` command above. If you want to update glue, run the installation -command again inside the environment. diff --git a/doc/installation/dependencies.rst b/doc/installation/dependencies.rst deleted file mode 100644 index c58c5d2a7..000000000 --- a/doc/installation/dependencies.rst +++ /dev/null @@ -1,44 +0,0 @@ -.. _glue-deps: - -Full list of dependencies -========================= - -Glue has the following required dependencies: - -* Python 3.8 or later -* `Numpy `_ 1.16 or later -* `Matplotlib `_ 3.2 or later -* `SciPy `_ 1.0 or later -* `Pandas `_ 1.0 or later -* `Astropy `_ 4.0 or higher -* `setuptools `_ 30.3 or later -* Either `PyQt5 `__ or - `PySide2 `__ 5.14 or later -* `QtPy `__ 1.9 or later - this is an - abstraction layer for the Python Qt packages -* `IPython `_ 4.0 or later -* `ipykernel `_ 4.0 or later -* `qtconsole `_ -* `dill `_ 0.2 or later (which improves session saving) -* `h5py `_ 2.10 or later, for reading HDF5 files -* `xlrd `_ 1.2 or later, for reading Excel files -* `mpl-scatter-density `_ 0.7 or later, for making - scatter density maps of many points. - -The following optional dependencies are also highly recommended and -domain-independent: - -* `SciPy `_ -* `scikit-image `_ - -Finally, there are domain-specific optional dependencies. For astronomy, these -are: - -* `astrodendro `_ for dendrograms -* `pyavm `_ for reading AVM metadata -* `spectral-cube `_ for reading spectral cubes - -You can check which dependencies are installed and which versions are available -by running (once glue is installed):: - - glue-deps list diff --git a/doc/installation/developer.rst b/doc/installation/developer.rst deleted file mode 100644 index 62b4d281d..000000000 --- a/doc/installation/developer.rst +++ /dev/null @@ -1,106 +0,0 @@ -Installing the latest developer version -======================================= - -.. note:: The latest developer version is not guaranteed to work correctly - or be functional at all, so use with care! - -With conda ----------- - -If you use Anaconda/conda to install glue normally, we provide nightly builds of -the conda packages for the latest developer versions. Unless you want to -actively develop glue, this is the best way to try out the latest developer -version. We recommend that you install the developer version into a conda -environment in case you also want to be able to have the stable version of glue -in your normal environment (we'll show how to do this in the next few steps). - -To create an environment (which only needs to be done the first time), type:: - - conda create -n glueviz-dev python - -Then switch to the ``glueviz-dev`` environment:: - - source activate glueviz-dev - -and install the latest nightly builds of the glue packages with:: - - conda install -c glueviz -c glueviz/label/dev glueviz - -You should normally see long version numbers for the glue-* packages that get -installed:: - - $ conda install -c glueviz -c glueviz/label/dev glueviz - Fetching package metadata ............. - Solving package specifications: . - - Package plan for installation in environment /Users/tom/miniconda3/envs/glue-dev: - - The following NEW packages will be INSTALLED: - - glue-core: 0.11.0.dev20170705102151.3ea9531-py36_0 glueviz/label/dev - glue-vispy-viewers: 0.8.dev20170602171439.7533769-py36_0 glueviz/label/dev - glueviz: 0.11.0.dev20170705211525.3af839b-0 glueviz/label/dev - pyopengl: 3.1.1a1-np113py36_0 - - Proceed ([y]/n)? y - -To update to a more recent version of the developer packages, use the same -command. If you want to switch back to the original environment you were in, you -can type:: - - source activate - -where ``>`` might be e.g. ``root`` or ``glueviz-env`` -depending on how you chose to set up your stable glue environment. - -From source (if you use conda) ------------------------------- - -If you use conda but want to install the latest developer version from the git -repository (for example if you want to work on the code) then the easiest way to -get all the dependencies installed is to first install the stable version, which -will pull in all the dependencies, then to remove it and install the developer -version:: - - conda install -c glueviz glueviz - conda remove glueviz - - git clone https://github.com/glue-viz/glue.git - cd glue - pip install . - cd .. - -You can also use ``python setup.py develop`` instead of ``pip install .`` if you -want changes made in the local repository to be reflected immediately in the -installed version. Note that you can do all this in an environment as described -in `With conda`_ if you want to have the stable version of glue in a separate -environment. - -The same instructions apply to other glue packages, for example the plugin with -the 3D viewers:: - - conda install -c glueviz glue-vispy-viewers - conda remove glue-vispy-viewers - - git clone https://github.com/glue-viz/glue-vispy-viewers.git - cd glue-vispy-viewers - pip install . - cd .. - -If you want to uninstall the developer versions and install the stable versions -again, you can uninstall the developer versions with:: - - pip uninstall glueviz glue-vispy-viewers - -then install the stable versions with conda as usual. - -From source (if you don't use conda) ------------------------------------- - -If you don't use conda, but use ``pip`` instead, then you can install the latest -version of the glue core package using:: - - git clone https://github.com/glue-viz/glue.git - cd glue - pip install -e . - cd .. diff --git a/doc/installation/images/navigator_channels_button.png b/doc/installation/images/navigator_channels_button.png deleted file mode 100644 index 82ea5cf3f..000000000 Binary files a/doc/installation/images/navigator_channels_button.png and /dev/null differ diff --git a/doc/installation/images/navigator_channels_dialog.png b/doc/installation/images/navigator_channels_dialog.png deleted file mode 100644 index 21b93f349..000000000 Binary files a/doc/installation/images/navigator_channels_dialog.png and /dev/null differ diff --git a/doc/installation/images/navigator_icon.png b/doc/installation/images/navigator_icon.png deleted file mode 100644 index 6bf733427..000000000 Binary files a/doc/installation/images/navigator_icon.png and /dev/null differ diff --git a/doc/installation/images/navigator_install.png b/doc/installation/images/navigator_install.png deleted file mode 100644 index 0ed573c77..000000000 Binary files a/doc/installation/images/navigator_install.png and /dev/null differ diff --git a/doc/installation/images/warning1_osx.png b/doc/installation/images/warning1_osx.png deleted file mode 100644 index 3e2e4db6b..000000000 Binary files a/doc/installation/images/warning1_osx.png and /dev/null differ diff --git a/doc/installation/images/warning1_windows.png b/doc/installation/images/warning1_windows.png deleted file mode 100644 index 7d08d2610..000000000 Binary files a/doc/installation/images/warning1_windows.png and /dev/null differ diff --git a/doc/installation/images/warning2_osx.png b/doc/installation/images/warning2_osx.png deleted file mode 100644 index 7cbc6daf9..000000000 Binary files a/doc/installation/images/warning2_osx.png and /dev/null differ diff --git a/doc/installation/images/warning2_windows.png b/doc/installation/images/warning2_windows.png deleted file mode 100644 index e3cf956ce..000000000 Binary files a/doc/installation/images/warning2_windows.png and /dev/null differ diff --git a/doc/installation/images/warning3_osx.png b/doc/installation/images/warning3_osx.png deleted file mode 100644 index 9235d1b0e..000000000 Binary files a/doc/installation/images/warning3_osx.png and /dev/null differ diff --git a/doc/installation/installation.rst b/doc/installation/installation.rst deleted file mode 100644 index 7e0e39293..000000000 --- a/doc/installation/installation.rst +++ /dev/null @@ -1,46 +0,0 @@ -.. _installation: - -Installing and running glue -=========================== - -Several installation methods for glue are outlined in the sections below. If you -run into issues, each page should provide relevant troubleshooting, and you can -also check the :ref:`known-issues` page which collects some more general issues. -If your problem is not described there, `open a new issue -`_ on GitHub. - -.. toctree:: - :maxdepth: 1 - - standalone - conda - pip - qt - dependencies - developer - -.. note:: If you are using Apple M1 hardware, be sure to read :ref:`apple-m1` - before proceeding with the installation instructions. - -Once glue is installed, you will be able to type:: - - glue - -in a terminal to start glue. Glue accepts a variety of command-line arguments. -See ``glue --help`` for examples. If you used the Anaconda Navigator, you can -also launch glue from the navigator, but be aware that errors may be hidden, so -if you have any issues, try running glue from the command-line. - -You can also start glue with the ``-v`` option:: - - glue -v - -to get more verbose output, which may help diagnose issues. - -On Windows, installation creates an executable ``glue.exe`` file within the -python script directory (e.g., ``C:\Python27\Scripts``). Windows users can -create a desktop shortcut for this file, and run Glue by double clicking on the -icon. - -.. note:: If you have issues with Glue after installing it on MacOS X Big Sur, - see :ref:`apple-bigsur`. diff --git a/doc/installation/pip.rst b/doc/installation/pip.rst deleted file mode 100644 index 4c41b8348..000000000 --- a/doc/installation/pip.rst +++ /dev/null @@ -1,23 +0,0 @@ -Installing with pip -=================== - -**Platforms:** MacOS X, Linux, and Windows - -You can install glue along with **all** :ref:`required and optional dependencies -` with `pip `__ using:: - - pip install glueviz[all,qt] - -The above will include domain-specific plugins as well as the PyQt5 library. -If you only want to install glue with all required and only non-domain-specific -optional dependencies (for example excluding the optional dependencies for -astronomy), you can do:: - - pip install glueviz[recommended,qt] - -And finally, if you don't want to install optional dependencies at all, and -already have PyQt5 or PySide2 installed, you can do:: - - pip install glueviz - -Note that this will still installed required dependencies. diff --git a/doc/installation/qt.rst b/doc/installation/qt.rst deleted file mode 100644 index e5d7af3bb..000000000 --- a/doc/installation/qt.rst +++ /dev/null @@ -1,20 +0,0 @@ -.. _installing-qt: - -Installing PyQt or PySide -========================= - -.. note:: If you are installing glue with conda or with pip then PyQt5 should be - automatically installed so you can ignore this page. If however you - need to manually install PyQt5 or PySide2, then read on! - -If you are using Linux, PyQt and PySide will typically be available in the -built-in package manager. For example, if you are using Ubuntu, then you can do:: - - sudo apt-get install python3-pyqt5 - -If you are using MacOS X, then if you are using MacPorts to manage your Python -installation, you can do:: - - sudo port install py37-pyqt5 - -assuming you are using Python 3.7 (modify the ``py37`` version as needed). diff --git a/doc/installation/standalone.rst b/doc/installation/standalone.rst deleted file mode 100644 index cdf7ce7d5..000000000 --- a/doc/installation/standalone.rst +++ /dev/null @@ -1,86 +0,0 @@ -Standalone MacOS X and Windows applications -=========================================== - -**Platforms:** MacOS X and Windows - -On MacOS X and Windows, the easiest way to install glue along with a few of -the common glue plugins is to download pre-built single-file applications. - -.. note:: This installation method is very recent, so if you run into any issues - it would be really helpful if you could let us know by `opening an issue - `_. In the - mean time, you can always try one of the other installation methods. - -The plugins included by default in the standalone applications are: - -* `glue-vispy-viewers `_ -* `glue-wwt `_ -* `glue-plotly `_ - -With this installation method, it is not possible to install additional plugins -beyond those included by default, so if you want the ability to do this, you -should check one of the other installation methods mentioned in -:ref:`installation`. - -MacOS X -------- - -Donwload the :download:`glue 2023.02.0.dmg -` -file, open it and copy the **glue 2023.02.0.app** application to your -**Applications** folder (or any other location you want to use). - -Open the copied application - after a short wait, you will likely see a dialog that looks like: - -.. image:: images/warning1_osx.png - :align: center - :width: 300 - -Click **OK** then go to the MacOS X **Settings**, then **Security and Privacy**, then go to the -**General** tab. You will likely see the following message: - -.. image:: images/warning2_osx.png - :align: center - :width: 600 - -Click on **Open anyway**, then in the following dialog: - -.. image:: images/warning3_osx.png - :align: center - :width: 300 - -Click **Open**. The glue application should now open! You should only need to do all this when -installing a new version, if you relaunch the same application it should now work straight away. - -Windows -------- - -Donwload the :download:`glue 2023.02.0.exe -` file. -Once the file has downloaded, open the **glue 2023.02.0.exe** application. You -will likely then see a dialog such as: - -.. image:: images/warning1_windows.png - :align: center - :width: 400 - -Click on **More info** and you will then see: - -.. image:: images/warning2_windows.png - :align: center - :width: 400 - -Click on **Run anyway** and glue should now open. - -Nightly builds --------------- - -The applications above are built every few months to provide stability -and are checked to make sure they all work correctly. We also provide -'nightly' builds which use the latest (released) versions of all the relevant -glue packages and plugins. These are generated automatically and are -not hand-checked, so may be unstable. The download links are: - -* Linux: :download:`glue-nightly ` -* MacOS X: :download:`glue nightly.dmg ` -* Windows: :download:`glue nightly.exe ` diff --git a/doc/known_issues.rst b/doc/known_issues.rst deleted file mode 100644 index 31a6341d6..000000000 --- a/doc/known_issues.rst +++ /dev/null @@ -1,114 +0,0 @@ -.. _known-issues: - -Known issues and solutions -========================== - -.. _apple-m1: - -Using glue on Apple M1 hardware -------------------------------- - -At the moment, glue is not easy to set up to run using a Python stack running -natively on M1 processors - instead you should -`enable Rosetta 2 `_ -for the **Terminal.app**. You can do this by selecting **Terminal.app** in the -Finder, then going to **File** and selecting **Get Info**, then clicking the -checkbox **Open using Rosetta**. - -Once you have done this, follow the regular -:ref:`installation` instructions for glue as if you were using an intel Mac. -Be sure to also read `Using glue on Big Sur`_ since this is the version of -MacOS X that is installed on Apple M1 hardware. - -.. _apple-bigsur: - -Using glue on Big Sur ---------------------- - -If you have issues with getting the 3D functionality to work, you will need to -install the latest development version of VisPy. If you are using conda, you -will first need to remove the conda-installed version of VisPy using:: - - conda remove vispy --force - -You can then install the latest development version of VisPy with:: - - pip install git+https://github.com/vispy/vispy - -Once this is done the 3D viewers should work properly. - -3D viewers not working on Linux with PyQt5 ------------------------------------------- - -Until recently, the main conda packages for PyQt5 provided by Anaconda did not -support OpenGL, which is needed for the 3D viewers. However, the latest -available conda packages now properly support OpenGL, so if you are having -issues getting the 3D viewers to work on Linux, try the following: first, make -sure you have the latest version of conda installed:: - - conda update -n root conda - -then update all packages in your environment using:: - - conda update -c glueviz --all - -Updating all packages is safest to make sure there are no conflicts between -packages, but if you prefer to try updating just the relevant packages, you -can try:: - - conda update qt pyqt icu sip - -but note that this may not always be sufficient to fix the issue. - -Qt internal error: qt_menu.nib could not be loaded --------------------------------------------------- - -When using Glue with the `PySide `_ bindings, the -following error sometimes occurs for some MacOS X conda users:: - - Qt internal error: qt_menu.nib could not be loaded. The .nib file should be - placed in QtGui.framework/Versions/Current/Resources/ or in the resources - directory of your application bundle. - -This is due to the PySide conda package in the ``defaults`` conda channel being -broken (see the following -`issue `_ for the -latest status on this issue). The workaround is to either use PyQt instead of -PySide, or to use the PySide package from the ``conda-forge`` channel:: - - conda install -c conda-forge pyside - -Undefined symbol: ``_ZNK7QSslKey9algorithmEv`` ----------------------------------------------- - -On certain Linux installations, when using Anaconda/conda to manage the Python -installation you are using for glue, you may run into the following error when -launching glue:: - - ImportError: /usr/lib/libkdecore.so.5: undefined symbol: _ZNK7QSslKey9algorithmEv - -This should be resolved in recent versions of the PyQt conda package, so -updating to the latest version should be sufficient to resolve this issue. -First, make sure you have the latest version of conda installed:: - - conda update -n root conda - -then update all packages in your environment using:: - - conda update -c glueviz --all - -Updating all packages is safest to make sure there are no conflicts between -packages, but if you prefer to try updating just the relevant packages, you -can try:: - - conda update qt pyqt icu sip - -but note that this may not always be sufficient to fix the issue. - -Incompatibility with PySide2 5.12.0 and 5.12.1 ----------------------------------------------- - -Glue is known to not work correctly with PySide2 5.12.0 due to `a bug in PySide2 -`_ and with PySide2 5.12.1 due to `a -different bug in PySide2 `_. If you -are using PySide2, be sure to use 5.12.2 or later. diff --git a/doc/python_guide/data_translation.rst b/doc/python_guide/data_translation.rst deleted file mode 100644 index eb63381b7..000000000 --- a/doc/python_guide/data_translation.rst +++ /dev/null @@ -1,196 +0,0 @@ -Working with non-glue data objects -================================== - -The main type of data object in glue are represented by the -:class:`~glue.core.data.Data` class. In some cases, you may however want to be -able to convert between these objects and other data classes (such as pandas -DataFrames). In addition, you may want to be able to convert selections in glue -to other kinds of data objects. Glue now includes a command-line interface to -make such translations as seamless as possible. - -The functionality shown below is only possible for a limited set of non-glue -data classes in the core glue package, but glue plugins may add support for -other kinds of data objects. If you want to develop your own translation -functions, you can do this as described in :ref:`custom-data-translation` -and :ref:`custom-subset-translation`. - -Converting data objects ------------------------ - -There are several ways of getting non-glue data objects into glue. To illustrate -this, we use a pandas :class:`~pandas.DataFrame` as an example:: - - >>> from pandas import DataFrame - >>> df1 = DataFrame() - >>> df1['a'] = [1.2, 3.4, 2.9] - >>> df1['g'] = ['r', 'q', 's'] - >>> df1 - a g - 0 1.2 r - 1 3.4 q - 2 2.9 s - -We now create a glue :class:`~glue.core.data_collection.DataCollection` object (note that if you -are already using an active glue application, you do not need to do this, and -instead you can access the data collection using the ``.data_collection`` -attribute of the application:: - - >>> from glue.core import DataCollection - >>> dc = DataCollection() - >>> dc - DataCollection (0 data sets) - -At this point, you can add the data to the data collection by assigning it to an -item with the label you want to give the dataset:: - - >>> dc['dataframe'] = df1 - >>> dc - DataCollection (1 data set) - 0: dataframe - -Note that ``dc.append`` won't work here because it only works with glue -:class:`~glue.core.data.Data` objects. We can see that the data collection now has -one dataset. We can access this data either by index:: - - >>> dc[0] - Data (label: dataframe) - -or by label:: - - >>> dc['dataframe'] - Data (label: dataframe) - -Note that in both cases, this returns a glue :class:`~glue.core.data.Data` object:: - - >>> print(dc['dataframe']) - Data Set: dataframe - Number of dimensions: 1 - Shape: 3 - Main components: - - a - - g - Coordinate components: - - Pixel Axis 0 [x] - -To get back the kind of object that you put in, you need to call the -:meth:`~glue.core.data.BaseData.get_object` method:: - - >>> df2 = dc['dataframe'].get_object() - >>> type(df2) - - >>> df2 - a g - 0 1.2 r - 1 3.4 q - 2 2.9 s - -In this case, glue knew to return a :class:`~pandas.DataFrame` object by default -because this is what was used to initialize the data object. However, you can -also specify this explicitly, either to convert to a different kind of object, -or to convert a glue data object that was not initially created from a -:class:`~pandas.DataFrame` to a :class:`~pandas.DataFrame`:: - - >>> from glue.core import Data - >>> data = Data(label='simple') - >>> data['f'] = [21, 45, 56] - >>> df3 = data.get_object(cls=DataFrame) - >>> type(df3) - - >>> df3 - f - 0 21 - 1 45 - 2 56 - -To see what data classes are currently supported for the translation, you can -call :meth:`~glue.core.data.BaseData.get_object` with no arguments: - - >>> data.get_object() - Traceback (most recent call last): - ... - ValueError: Specify the object class to use with cls= - supported classes are: - - * pandas.core.frame.DataFrame - -The core glue application only supports translations with :class:`~pandas.DataFrame` -for now, but plugin packages may define translations to other domain-specific -data objects. - -Working with subsets --------------------- - -In the examples above, we saw how to translate certain kinds of non-glue objects -to glue objects, and translate these back. In some cases, you may want to -translate not the full dataset but a subset of the data back to a non-glue object. -For example, you may have passed a :class:`~pandas.DataFrame` to glue, made -a series of selections, and want to get the subset of points in the selection to -a :class:`~pandas.DataFrame`. Continuing from the prevous example where -the data collection contains a single dataset created from a :class:`~pandas.DataFrame`:: - - >>> dc - DataCollection (1 data set) - 0: dataframe - -We now make a selection based on the data values (here we make the selection -programmatically, but often you may be making it interatively in the data -viewers):: - - >>> dc.new_subset_group(subset_state=dc['dataframe'].id['a'] < 3, - ... label='my subset') - - -Now that the subset has been created, you can retrieve it as a :class:`~pandas.DataFrame` -using the :meth:`~glue.core.data.BaseData.get_subset_object` method:: - - >>> dfsub1 = dc['dataframe'].get_subset_object() - >>> type(dfsub1) - - >>> dfsub1 - a g - 0 1.2 r - 1 2.9 s - -Generally speaking, for datasets with 1-d fields, the translation functions will create -an object which has a subset of the original rows. For datasets with 2 or more dimensions, -the final dataset may have the same shape but with the values outside of the subset masked, -e.g. by NaN values. This behavior is left up to the individual translation functions. - -If multiple subsets are present, you can specify which one to retrieve using the ``subset_id`` -keyword argument:: - - >>> dc.new_subset_group(subset_state=dc['dataframe'].id['a'] > 2, - ... label='my other subset') - - >>> dfsub2 = dc['dataframe'].get_subset_object(subset_id='my other subset') - >>> dfsub2 - a g - 0 3.4 q - 1 2.9 s - -or you can also not set ``subset_id`` to see a list of available subsets:: - - >>> dc['dataframe'].get_subset_object() - Traceback (most recent call last): - ... - ValueError: Several subsets are present, specify which one to retrieve with subset_id= - valid options are: - - * 0 or 'my subset' - * 1 or 'my other subset' - -Translating the definition of the subsets ------------------------------------------ - -In the previous section on `Working with subsets`_, we translated the data in the glue -subsets to non-glue objects, but it is also possible to translate not the data values -but the more abtract representation of the selection. The core glue package does not -include any formats to translate these selections to currently, but if you have a -glue plugin installed that does, or if you have defined one yourself as described -in :ref:`custom-subset-translation`, you should be able to call the -:meth:`~glue.core.data.BaseData.get_selection_definition` method which takes a ``format=`` -keyword argument that specifies the translator to use (leaving this out will -show a list of available of translation functions available as for subsets):: - - >>> dc['dataframe'].get_selection_definition(subset_id='my subset', - ... format='simple-string') # doctest: +SKIP - "a > 2" - diff --git a/doc/python_guide/data_tutorial.rst b/doc/python_guide/data_tutorial.rst deleted file mode 100644 index 3d1e7c6f6..000000000 --- a/doc/python_guide/data_tutorial.rst +++ /dev/null @@ -1,300 +0,0 @@ -.. _data_tutorial: - -Working with Data objects -========================= - -If you are using the IPython terminal in the Glue application, or if you are -writing Python code that uses Glue, you will probably want to interact with -data. - -Data classes ------------- - -The core data container in Glue is the :class:`~glue.core.data.Data` class. -Each :class:`~glue.core.data.Data` instance can include any number of -n-dimensional *components*, each represented by the -:class:`~glue.core.component.Component` class. The actual data resides in the :class:`~glue.core.component.Component` objects. Because of this structure, a -:class:`~glue.core.data.Data` object can represent either a table, which is a -collection of 1-d :class:`~glue.core.component.Component` objects, or an -n-dimensional dataset, which might include one (but could include more) -n-dimensional :class:`~glue.core.component.Component` objects. - -Inside :class:`~glue.core.data.Data` objects, each -:class:`~glue.core.component.Component` is assigned a -:class:`~glue.core.component_id.ComponentID`. However, this is not necessarily a -unique ID for each and every component -- instead, different components -representing the same conceptual quantity can be given the same component ID. -Component IDs are central to the linking framework - -When using the Glue application, the :class:`~glue.core.data.Data` objects are -collected inside a :class:`~glue.core.data_collection.DataCollection`. - -We can represent this graphically like this: - -.. image:: images/glue_hierarchy.png - :width: 300 - :alt: Glue Hierarchy - :align: center - -The :class:`~glue.core.data_collection.DataCollection` can be accessed by the -``dc`` variable in the IPython terminal. If the Glue application is not open, you can also create your own :class:`~glue.core.data_collection.DataCollection` by doing:: - - >>> from glue.core import DataCollection - >>> dc = DataCollection() - -In the remainder of this page, we are going to assume that you have just -followed the :ref:`Getting Started ` tutorial, and have the -Astronomy data on the W5 region loaded. - -Alternatively, for the purposes of this tutorial, you can also load the same -data manually into a Python/IPython session using:: - - >>> from glue.core import DataCollection - >>> from glue.core.data_factories import load_data - >>> dc = DataCollection() - >>> dc.append(load_data('getting_started/w5.fits')) - >>> dc.append(load_data('getting_started/w5_psc.vot')) - -This sets up the ``dc`` object to be the same as what it would be in the -:ref:`Getting Started ` tutorial. - -.. _data_access_api: - -Using Data and DataCollection ------------------------------ - -Let's take a look at the :class:`~glue.core.data_collection.DataCollection`: - - >>> dc - DataCollection (2 data sets) - 0: w5[PRIMARY] - 1: w5_psc - -:class:`~glue.core.data_collection.DataCollection` behaves like a list -- you can access :class:`~glue.core.data.Data` objects by indexing them. Let's grab the first data object:: - - >>> data = dc[0] - >>> data - Data (label: w5[PRIMARY]) - >>> data.components - [Declination, PRIMARY, Pixel x, Pixel y, Right Ascension] - -:class:`~glue.core.data.Data` objects behave like dictionaries: you can retrieve the numerical data associated with each one with bracket-syntax:: - - >>> data['PRIMARY'] - array([[ 454.47747803, 454.18780518, 454.56842041, ..., 450.08349609, - 451.14971924, 450.25921631], - ..., - [ 442.0128479 , 442.54266357, 443.43310547, ..., 441.5506897 , - 442.89486694, 442.76904297]], dtype=float32) - -Numpy-style fancy-indexing is also supported:: - - >>> data['PRIMARY', 0:3, 0:2] - array([[ 454.47747803, 454.18780518], - [ 452.36376953, 452.8883667 ], - [ 451.77172852, 453.42767334]], dtype=float32) - -This is equivalent to:: - - >>> data['PRIMARY'][0:3, 0:2] - array([[ 454.47747803, 454.18780518], - [ 452.36376953, 452.8883667 ], - [ 451.77172852, 453.42767334]], dtype=float32) - -Note that the indexing syntax (e.g. ``['PRIMARY']``) gives you the Numpy array, and not the :class:`~glue.core.component.Component` object itself. The Numpy array is usually what you are interested in. However, you can retrieve the Component object if you like. To do this, you will first need to get the :class:`~glue.core.component_id.ComponentID` for the component you are interested in::: - - >>> primary_id = data.components[0] - >>> primary_id - Declination - >>> type(primary_id) - glue.core.component_id.ComponentID - -You can then fetch the component with -:meth:`~glue.core.data.Data.get_component`:: - - >>> component = data.get_component(primary_id) - >>> component.data - array([[ 58.84943461, 58.84956411, 58.84969336, ..., 58.84969336, - 58.84956411, 58.84943461], - ..., - [ 61.84155834, 61.84170457, 61.84185052, ..., 61.84185052, - 61.84170457, 61.84155834]]) - -.. note:: The item access syntax (square brackets) will not work if component - labels are not unique. In this case, you must first retrieve the - wanted ComponentID and use it to get the component object. - -Adding new attributes to datasets ---------------------------------- - -A common task is to combine two or more attributes in a dataset, and store the -result as a new attribute to visualize. To demonstrate this, let's use the W5 -catalog data:: - - >>> dc - DataCollection (2 data sets) - 0: w5[PRIMARY] - 1: w5_psc - >>> catalog = dc[1] - -We can examine the attributes in this dataset - - >>> print(catalog) - Data Set: w5_psc - Number of dimensions: 1 - Shape: 17771 - Components: - 0) ID - 1) Pixel Axis 0 - 2) World 0 - 3) RAJ2000 - 4) DEJ2000 - 5) Jmag - 6) Hmag - 7) Ksmag - ... - -As mentioned in `Using Data and DataCollection`_, :class:`~glue.core.data.Data` -objects behave like dictionaries mapping component names to numpy arrays. So -one way to define a new component is like this:: - - >>> j_minus_h = catalog['Jmag'] - catalog['Hmag'] - >>> catalog['jmh'] = j_minus_h - -If you are using the Glue application, this new attribute is immediately -available for visualizing. - -Using lazy attributes ---------------------- - -In the procedure above, the `j_minus_h` array was precomputed. An alternative -approach is to define a new attribute that gets evaluated on-the-fly. While -``data[attribute_name]`` returns a numpy array, ``data.id[attribute_name]`` -returns a :class:`~glue.core.component_id.ComponentID`, which you can use as a -lightweight proxy object that you can use to build simple arithmetic -expressions:: - - >>> jmh_lazy = catalog.id['Jmag'] - catalog.id['Hmag'] - >>> jmh_lazy - - >>> catalog['jmh2'] = jmh_lazy - -This new component is computed as needed on the fly, and can be more memory -efficient for particular applications. - -Defining new subsets --------------------- - -You can define new subsets from Python. An example might look like:: - - >>> state = catalog.id['Jmag'] > catalog.id['Hmag'] - >>> label = 'J > H' - >>> subset_group = dc.new_subset_group(label, state) - -If you using the Glue application, you can then change the visual properties of this subset using:: - - >>> subset_group.style.color = '#00ff00' - -.. note:: ``subset_group`` is not technically a subset, but a group of subsets. - This is beyond the scope of this tutorial, and explained in more - detail in :ref:`dev_selection` - -This method of creating subsets can be a powerful technique. For a demo of -using sending Scikit-learn-identified clusters back into Glue as subsets, see -`this notebook `_. - -The following example demonstrates how to access subsets defined graphically in data viewers. Let's say that you have two subsets that you defined in the scatter plot and histogram data viewers: - -.. image:: images/subset_01.png - :width: 60% - -We'll assume that you made a similar selection to these for demonstration purposes. You can now access the subsets from the built-in IPython console. To do this, assuming you have two subsets defined, you can do:: - - >>> red, faint_h = dc.subset_groups - -Let's also grab a component in the data:: - - >>> hmag = catalog.id['Hmag'] - -To find the intersection of the two subsets we have already defined (i.e., red -sources with faint H band magnitudes):: - - >>> new_state = red & faint_h - >>> label = "Red and faint" - >>> data_collection.new_subset_group(label=label, subset_state=new_state) - -The resulting intersection is shown in blue here: - -.. image:: images/subset_02.png - :width: 60% - -The boolean operators ``&``, ``^``, ``|``, and ``~`` act on subsets to define -new subsets represented by the intersection, exclusive union, union, and -inverse, respectively. - -You can also build subsets out of inequality constraints on component IDs:: - - >>> mid_mag = (hmag > 10) & (hmag < 15) - >>> dc.new_subset_group('between_10_15', mid_mag) - -This selects objects with H band magnitudes between 10 and 15: - -.. image:: images/subset_03.png - :width: 60% - -.. _data_creation: - -Accessing subset data ---------------------- - -Once you have defined subsets, you can access the subsets on specific datasets using the ``.subsets`` attribute on :class:`~glue.core.data.Data` objects. For instance, after the above selections, you might have something that looks like this:: - - >>> catalog.subsets - (Subset: between_10_15 (data: w5_psc), Subset: J > H (data: w5_psc)) - -Let's access the first subset:: - - >>> subset = catalog.subsets[0] - >>> subset - Subset: between_10_15 (data: w5_psc) - -You can access components of the subset as if it was a dataset:: - - >>> subset['Jmag'] - array([ 15.34000015, 10.89999962, 13.30000019, ..., 13.06000042, - 13.38000011, 14.18000031], dtype=float32) - -In this case, only the values in the selection are returned. If you prefer, you can also retrieve the subset as a boolean mask that can be applied to the original dataset:: - - >>> subset.to_mask() - Out[65]: array([ True, True, True, ..., True, True, True], dtype=bool) - -Creating a data object ----------------------- - -In the above examples, we have assumed that the data objects were loaded via -the Glue application. The readers/writers in Glue can also be accessed using -the functions in :mod:`glue.core.data_factories`:: - - >>> from glue.core.data_factories import (load_data, fits_reader, - ... tabular_data) - >>> fits_reader('image.fits') # reads a FITS image - >>> load_data('catalog.csv', factory=tabular_data) # reads a catalog - >>> load_data('catalog.csv') # guesses format - -If these functions do not fit your needs, you can also :ref:`write your own -data loader `, and use it from the Glue GUI. - -It is also possible to create :class:`~glue.core.data.Data` objects completely manually:: - - >>> from glue.core import Data - >>> data = Data(x=[1, 2, 3], y=[2, 3, 4], label="first dataset") - -The arguments to the class are the components you want to create, as well as -a label/name for the dataset. Each component can be given using the -``name=values`` syntax. The above example creates a -:class:`~glue.core.data.Data` with two components ``x`` and ``y``. - -You can then add the data object to the data collection using:: - - >>> dc.append(data) diff --git a/doc/python_guide/data_viewer_options.rst b/doc/python_guide/data_viewer_options.rst deleted file mode 100644 index f1e9b313f..000000000 --- a/doc/python_guide/data_viewer_options.rst +++ /dev/null @@ -1,106 +0,0 @@ -.. _programmatic: - -==================================== -Programmatically configuring viewers -==================================== - -Viewers in Glue are designed to be easily configured with Python. As much as -possible, viewer settings are controlled by simple properties on the ``state`` -attribute of data viewer objects. For example:: - - import numpy as np - - from glue.core import Data, DataCollection - from glue.app.qt.application import GlueApplication - from glue.viewers.scatter.qt import ScatterViewer - - # create some data - d = Data(x=np.random.random(100), y=np.random.random(100)) - dc = DataCollection([d]) - - # create a GUI session - ga = GlueApplication(dc) - - # plot x vs y, flip the x axis, log-scale y axis - scatter = ga.new_data_viewer(ScatterViewer) - scatter.add_data(d) - - # Modify viewer-level options - scatter.state.x_att = d.id['x'] - scatter.state.y_att = d.id['y'] - scatter.state.y_log = True - - # Modify settings for the (only) layer shown - scatter.state.layers[0].color = 'blue' - - # show the GUI - ga.start() - -Viewer Options -============== - -The ``state`` attribute for each viewer is an instance of a viewer state class. -Each viewer state object then has a ``layers`` attribute that can be used to -control individual layers in the viewer (as shown above). - -The following table lists for each built-in viewer the classes defining the state -for each viewer/layer type. By clicking on the name of the class, you will access -a page from the API documentation which will list the available attributes. - -=================== ========================= ======================= ======================== -Viewer Viewer state Data layer state Subset layer state -=================== ========================= ======================= ======================== -|scatter_viewer| |scatter_viewer_state| |scatter_layer_state| |scatter_layer_state| -|image_viewer| |image_viewer_state| |image_data_state| |image_subset_state| -|histogram_viewer| |histogram_viewer_state| |histogram_layer_state| |histogram_layer_state| -|profile_viewer| |profile_viewer_state| |profile_layer_state| |profile_layer_state| -=================== ========================= ======================= ======================== - -.. |scatter_viewer| replace:: :class:`~glue.viewers.scatter.qt.ScatterViewer` -.. |scatter_viewer_state| replace:: :class:`~glue.viewers.scatter.state.ScatterViewerState` -.. |scatter_layer_state| replace:: :class:`~glue.viewers.scatter.state.ScatterLayerState` - -.. |image_viewer| replace:: :class:`~glue.viewers.image.qt.ImageViewer` -.. |image_viewer_state| replace:: :class:`~glue.viewers.image.state.ImageViewerState` -.. |image_data_state| replace:: :class:`~glue.viewers.image.state.ImageLayerState` -.. |image_subset_state| replace:: :class:`~glue.viewers.image.state.ImageSubsetLayerState` - -.. |histogram_viewer| replace:: :class:`~glue.viewers.histogram.qt.HistogramViewer` -.. |histogram_viewer_state| replace:: :class:`~glue.viewers.histogram.state.HistogramViewerState` -.. |histogram_layer_state| replace:: :class:`~glue.viewers.histogram.state.HistogramLayerState` - -.. |profile_viewer| replace:: :class:`~glue.viewers.profile.qt.ProfileViewer` -.. |profile_viewer_state| replace:: :class:`~glue.viewers.profile.state.ProfileViewerState` -.. |profile_layer_state| replace:: :class:`~glue.viewers.profile.state.ProfileLayerState` - -Customizing Plots with Matplotlib -================================= - -If you want, you can directly manipulate the Matplotlib plot objects that -underlie Glue viewers. This can be useful if you want to create static plots with -custom annotation, styles, etc. - -From the GUI ------------- -Open the IPython terminal window. The ``application.viewers`` variable -is a list of lists of all the -open viewer windows. Each inner list contains the data viewers -open on a single tab. Every viewer has an ``axes`` attribute, -which points to a :class:`Matplotlib Axes ` -object:: - - viewer = application.viewers[0][0] - ax = viewer.axes - ax.set_title('Custom title') - ax.figure.canvas.draw() # update the plot - -From a script -------------- - -Save the current glue session via ``File->Save Session``. You can -reload this session programmatically as follows:: - - from glue.app.qt.application import GlueApplication - app = GlueApplication.restore('output.glu', show=False) - viewer = app.viewers[0][0] - ax = viewer.axes diff --git a/doc/python_guide/glue_from_python.rst b/doc/python_guide/glue_from_python.rst deleted file mode 100644 index 367101c62..000000000 --- a/doc/python_guide/glue_from_python.rst +++ /dev/null @@ -1,197 +0,0 @@ -.. _qglue: - -Starting Glue from Python -========================= - -In addition to using Glue as a standalone program, you can import glue -as a library from Python. There are (at least) two good reasons to do this: - -#. You are working with multidimensional data in python, and want to use Glue - for quick interactive visualization. -#. You find yourself repeatedly loading the same sets of data each time you - run Glue. You want to write a startup script to automate this process. - -Quickly send data to Glue with qglue ------------------------------------- - -The easiest way to send python variables to Glue is to use -:func:`~glue.qglue`:: - - from glue import qglue - -For example, say you are working with a `Pandas `_ DataFrame:: - - >>> df - - Int64Index: 500 entries, 0 to 499 - Data columns (total 3 columns): - x 500 non-null values - y 500 non-null values - z 500 non-null values - dtypes: float64(3) - -You can easily start up Glue with this data using:: - - >>> app = qglue(xyz=df) - -This will send this data to Glue, and label it ``xyz``. - -:func:`~glue.qglue` accepts many data types as inputs. Let's see some examples:: - - import numpy as np - import pandas as pd - from astropy.table import Table - - x = [1, 2, 3] - y = [2, 3, 4] - - u = [10, 20, 30, 40] - v = [20, 40, 60, 80] - - pandas_data = pd.DataFrame({'x': x, 'y': y}) - dict_data = {'u': u, 'v': v} - recarray_data = np.rec.array([(0, 1), (2, 3)], - dtype=[('a', 'i'), ('b', 'i')]) - astropy_table = Table({'x': x, 'y': y}) - bad_data = {'x': x, 'u':u} - -* ``qglue(xy=pandas_data)``: - constructs a dataset labeled ``xy``, with two components (``x`` and ``y``) - -* ``qglue(uv=dict_data)``: - construct a dataset labeled ``uv``, with two components (``u`` and ``v``) - -* ``qglue(xy=pandas_data, uv=dict_data)``: - constructs both of the previous two data sets. - -* ``qglue(rec=recarray_data, astro=astropy_table)``: - constructs two datasets: ``rec`` (components ``a`` and ``b``), and - ``astro`` (components ``x`` and ``y``) - -* ``qglue(bad=bad_data)``: - doesn't work, because the two components ``x`` and ``u`` have - different shapes. - -.. note:: Reminder: in Glue, :class:`~glue.core.data.Data` sets are collections - of one or more :class:`~glue.core.component.Component` objects. - Components in a dataset are basically arrays of the same shape. For - more information, see :ref:`data_tutorial` - -.. note:: Datasets cannot be given the label ``links``. - -Linking data with ``qglue`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The :ref:`Data Linking ` tutorial discusses how Glue uses the -concept of links to compare different datasets. From the GUI, links -are defined using the :ref:`Link Manager `. It is -also possible to define some of these links with ``qglue``. - -The ``links`` keyword for ``qglue`` accepts a list of link descriptions. Each link description has the following format:: - - (component_list_a, component_set_b, forward_func, back_func) - -* ``component_list_a`` and ``component_list_b`` are lists of component names. - In the first example above, the ``x`` component in the ``xyz`` dataset is - named ``'xyz.x'``. - -* ``forward_func`` is a function which accepts one or more numpy arrays as - input, and returns one or more numpy arrays as output. It computes the - quantities in ``component_set_b``, given the quantities in - ``component_list_a``. - -* ``back_func`` performs the reverse operation. - -Here's an example:: - - def pounds_to_kilos(lbs): - return lbs / 2.2 - - def kilos_to_pounds(kilos): - return kilos * 2.2 - - def lengths_to_area(width, height): - return width * height - - link1 = (['data1.m_lb'], ['data_2.m_kg'], pounds_to_kilos, kilos_to_pounds) - link2 = (['data1.width', 'data1.height'], ['data2.area'], lengths_to_area) - qglue(data1=data1, data2=data2, links=[link1, link2]) - -The first link converts between the masses in two different data sets, recorded -in different units. The second link is a 1-way link that computes the area of -items in dataset 1, based on their width and height (there is no way to compute -the width and height from the area measurements in dataset 2, so the reverse -function is not provided). These links would enable the following interaction, -for example: - -#. Overplot histograms of the mass distribution of both datasets -#. Define a region in a plot of mass vs area for data 2, and apply that filter - to dataset 1 - -.. note:: If you start Glue from a non-notebook IPython session, you will - encounter an error like ``Multiple incompatible subclass instances of - IPKernelApp are being created``. The solution to this is to start - Glue from a non-IPython shell, or from the notebook (see next - section). - -.. _notebook: - -Using qglue with the IPython/Jupyter Notebook -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -You can call :func:`~glue.qglue` from the IPython/Jupyter notebook -normally. However, the default behavior is for Glue to block the execution of -the notebook while the UI is running. If you would like to be able to use the -notebook and Glue at the same time, run this cell before starting glue:: - - %gui qt - -This must be executed in a separate cell, before starting Glue. - -.. _add_data_qglue: - -Adding data to glue when started using qglue -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Once glue has been launched, you can continue to add data to it using the -:meth:`~glue.core.application_base.Application.add_data` method:: - - >>> app = qglue(data1=array1) - >>> app.add_data(data2=array2) - -You can also pass filenames to :meth:`~glue.core.application_base.Application.add_data`:: - - >>> app.add_data('myimage.fits') - -Manual data construction ------------------------- - -If ``qglue`` is not flexible enough for your needs, you can build data objects -using the general Glue data API described in :ref:`data_tutorial`. - -Here's a simple script to load data and pass it to Glue: - -.. literalinclude:: scripts/w5.py - -Some remarks: - - * :func:`~glue.core.data_factories.load_data` constructs Glue Data objects - from files. It uses the file extension as a hint for file type - * Individual data objects are bundled inside a - :class:`~glue.core.data_collection.DataCollection` - * The :class:`~glue.core.link_helpers.LinkSame` function indicates that two - attributes in different data sets describe the same quantity - * ``GlueApplication`` takes a ``DataCollection`` as input, and starts the GUI - via ``start()`` - -Starting Glue from a script ---------------------------- -.. _startup_scripts: - -If you call glue with a python script as input, Glue will simply -run that script:: - - $ glue startup_script.py - -Likewise, if you are using the pre-built Mac application, you can right-click -on a script and open the file with Glue. diff --git a/doc/python_guide/images/glue_hierarchy.png b/doc/python_guide/images/glue_hierarchy.png deleted file mode 100644 index 09d469a17..000000000 Binary files a/doc/python_guide/images/glue_hierarchy.png and /dev/null differ diff --git a/doc/python_guide/images/ipython_button.png b/doc/python_guide/images/ipython_button.png deleted file mode 100644 index 18d8e6e69..000000000 Binary files a/doc/python_guide/images/ipython_button.png and /dev/null differ diff --git a/doc/python_guide/images/liveupdate.gif b/doc/python_guide/images/liveupdate.gif deleted file mode 100644 index 28bb7860d..000000000 Binary files a/doc/python_guide/images/liveupdate.gif and /dev/null differ diff --git a/doc/python_guide/images/subset_01.png b/doc/python_guide/images/subset_01.png deleted file mode 100644 index 5213a48d6..000000000 Binary files a/doc/python_guide/images/subset_01.png and /dev/null differ diff --git a/doc/python_guide/images/subset_02.png b/doc/python_guide/images/subset_02.png deleted file mode 100644 index 442adc05d..000000000 Binary files a/doc/python_guide/images/subset_02.png and /dev/null differ diff --git a/doc/python_guide/images/subset_03.png b/doc/python_guide/images/subset_03.png deleted file mode 100644 index b6e2c1e02..000000000 Binary files a/doc/python_guide/images/subset_03.png and /dev/null differ diff --git a/doc/python_guide/ipython_terminal.rst b/doc/python_guide/ipython_terminal.rst deleted file mode 100644 index 4d31a4f87..000000000 --- a/doc/python_guide/ipython_terminal.rst +++ /dev/null @@ -1,26 +0,0 @@ -Using the IPython terminal in Glue -================================== - -Glue includes a button to open an IPython terminal window: - -.. image:: images/ipython_button.png - :align: center - -This gives you programmatic access to Glue data. A number of variables are -available by default (these are also listed when you open the terminal): - - * ``dc`` / ``data_collection`` refer to the central - :class:`~glue.core.data_collection.DataCollection`, which is an object that - holds all of the datasets, subsets, and data links - - * ``hub`` is the main communication hub. - - * ``application`` is the top level - :class:`~glue.app.qt.application.GlueApplication`, which has access to plot - windows (among other things) - -Additionally, you can drag datasets and subsets into the terminal window, to -easily assign them new variable names. - -Most of your interactions will likely be with data objects and the data -collection, so let's take a look at these in the next section! diff --git a/doc/python_guide/liveupdate.rst b/doc/python_guide/liveupdate.rst deleted file mode 100644 index b702e458c..000000000 --- a/doc/python_guide/liveupdate.rst +++ /dev/null @@ -1,19 +0,0 @@ -Watching data for changes -========================= - -A new, experimental feature enables Glue to monitor the -data files you have loaded for changes, and to auto-refresh -plots when needed. This can be useful if your data update periodically, -or if your data are produced by an analysis pipeline whose parameters you -are iteratively refining. - -To enable this feature, add the following line to your :ref:`config.py ` script:: - - from glue.config import auto_refresh - auto_refresh(True) - -.. figure:: images/liveupdate.gif - :align: center - -.. note:: This currently only works if file updates do not change the shape of - the underlying data. diff --git a/doc/python_guide/scripts/w5.py b/doc/python_guide/scripts/w5.py deleted file mode 100644 index 30c36d38e..000000000 --- a/doc/python_guide/scripts/w5.py +++ /dev/null @@ -1,17 +0,0 @@ -from glue.core.data_factories import load_data -from glue.core import DataCollection -from glue.core.link_helpers import LinkSame -from glue.app.qt.application import GlueApplication - -# load 2 datasets from files -image = load_data('w5.fits') -catalog = load_data('w5_psc.vot') -dc = DataCollection([image, catalog]) - -# link positional information -dc.add_link(LinkSame(image.id['Right Ascension'], catalog.id['RAJ2000'])) -dc.add_link(LinkSame(image.id['Declination'], catalog.id['DEJ2000'])) - -# start Glue -app = GlueApplication(dc) -app.start() diff --git a/doc/readme.gif b/doc/readme.gif deleted file mode 100644 index 03b9cc8f5..000000000 Binary files a/doc/readme.gif and /dev/null differ diff --git a/doc/redirect.py b/doc/redirect.py deleted file mode 100644 index 7f993228e..000000000 --- a/doc/redirect.py +++ /dev/null @@ -1,58 +0,0 @@ -# This Sphinx plugin comes from https://github.com/openstack/nova-specs and was -# originally licensed under a Creative Commons Attribution 3.0 Unported License. -# The full text for this license can be found here: -# -# https://creativecommons.org/licenses/by/3.0/legalcode - - -# A simple sphinx plugin which creates HTML redirections from old names -# to new names. It does this by looking for files named "redirect" in -# the documentation source and using the contents to create simple HTML -# redirection pages for changed filenames. - -import os.path - -from sphinx.util.console import bold -from sphinx.util import logging -logger = logging.getLogger(__name__) - - -def setup(app): - from sphinx.application import Sphinx - if not isinstance(app, Sphinx): - return - app.connect('build-finished', emit_redirects) - - -def process_redirect_file(app, path, ent): - parent_path = path.replace(app.builder.srcdir, app.builder.outdir) - with open(os.path.join(path, ent)) as redirects: - for line in redirects.readlines(): - from_path, to_path = line.rstrip().split(' ') - from_path = from_path.replace('.rst', '.html') - to_path = to_path.replace('.rst', '.html') - - redirected_filename = os.path.join(parent_path, from_path) - redirected_directory = os.path.dirname(redirected_filename) - if not os.path.exists(redirected_directory): - os.makedirs(redirected_directory) - with open(redirected_filename, 'w') as f: - f.write('' - % to_path) - - -def emit_redirects(app, exc): - logger.info(bold('scanning %s for redirects...') % app.builder.srcdir) - - def process_directory(path): - for ent in os.listdir(path): - p = os.path.join(path, ent) - if os.path.isdir(p): - process_directory(p) - elif ent == 'redirects': - logger.info(' found redirects at %s' % p) - process_redirect_file(app, path, ent) - - process_directory(app.builder.srcdir) - logger.info('...done') diff --git a/doc/redirects b/doc/redirects deleted file mode 100644 index fbeb999bf..000000000 --- a/doc/redirects +++ /dev/null @@ -1,24 +0,0 @@ -api.rst developer_guide/api.rst -architecture.rst index.rst -developer_guide.rst index.rst -getting_started.rst getting_started/index.rst -configuration.rst customizing_guide/configuration.rst -custom_subsets.rst python_guide/glue_from_python.rst -dendro.rst gui_guide/dendro.rst -gui_guide.rst gui_guide/index.rst -link_tutorial.rst gui_guide/link_tutorial.rst -merging.rst gui_guide/merging.rst -slice.rst gui_guide/slice.rst -spectrum.rst gui_guide/spectrum.rst -custom_viewer.rst customizing_guide/custom_viewer.rst -customization.rst customizing_guide/customization.rst -data_tutorial.rst python_guide/data_tutorial.rst -data_viewer_options.rst python_guide/data_viewer_options.rst -glue_from_python.rst python_guide/glue_from_python.rst -python_guide.rst index.rst -liveupdate.rst python_guide/liveupdate.rst -python_guide/custom_viewer.rst ../customizing_guide/custom_viewer.rst -python_guide/customization.rst ../customizing_guide/customization.rst -gui_guide/custom_subsets.rst ../python_guide/glue_from_python.rst -gui_guide/configuration.rst ../customizing_guide/configuration.rst -whatsnew/experimental_3d.rst ../gui_guide/3d_viewers.rst diff --git a/doc/spelling_wordlist.txt b/doc/spelling_wordlist.txt deleted file mode 100644 index 8d84c003b..000000000 --- a/doc/spelling_wordlist.txt +++ /dev/null @@ -1,372 +0,0 @@ -Agg -Aladin -Astropy -Calabretta -ColorRole -Colormaps -ContextMenuPolicy -CursorShape -DS -Dampier -DataFrame -Dendrograms -Docstrings -Fallback -Farenheit -FindChildOption -FindChildOptions -FindChildrenRecursively -FocusPolicy -FocusReason -Freeform -Galactocentric -Geosciences -GestureType -Ginga -Greisen -Heatmap -Hyperparameters -IPython -Indices -InputMethodQuery -Isosurface -Iterable -Janskys -Jupyter -LayoutDirection -MDI -Matplotlib -Metaclass -Metadata -Miniconda -NUMPY -NaN -NN -Numpy -Overplot -Plugin -Plugins -PKx -Programmatically -QAction -QActions -QBitmap -QByteArray -QColor -QCursor -QEvent -QFont -QIcon -QKeySequence -QLabel -QLayout -QLocale -QObject -QPainter -QPalette -QPixmap -QPoint -QRect -QRegion -QSize -QSslKey -QStyle -QThread -QUrl -QWidget -QWindow -Refactor -Roadmap -Scikit -ToolBarArea -ToolBarAreas -ToolButtonStyle -Ubuntu -Uninstalling -Unsubscribe -Valdes -WidgetAttribute -WindowFlags -WindowModality -WindowStates -WindowType -ZNK -aastex -actionAt -actionTriggered -addAction -addToolBar -aladin -app -arcsec -arcsecond -arcseconds -arg -args -astropy -att -autoconnect -binsize -bm -boolean -broadcasted -cartesian -casalike -cds -checkbox -checkboxes -childAt -cid -cids -cls -cmap -colormap -colormaps -combobox -componentIDs -conda -config -coord -coords -csv -customContextMenuRequested -daophot -datacubes -dataset -datasets -datatype -datetime -dendrogram -dendrograms -destroySubWindows -destroyWindow -docstring -downsampled -drilldown -dropdown -dt -dtype -dy -eeefff -eg -errorbars -exc -exportfits -exts -filetype -findChild -findChildren -floodfill -fmt -fontsize -frontend -func -functools -geospatial -getitem -glueconda -glueviz -grabMouse -grp -hdf -heatmap -hexbin -hyperparameter -hyperparameters -iconSizeChanged -img -imshow -indices -init -initializeWindow -inplace -ints -ipac -isosurface -isosurfaces -itemData -iterable -iterables -iteratively -jittering -jupyter -kewyords -keybind -kwargs -labeldata -linestyle -linewidth -ly -matplotlib -memmap -menubar -messagebox -metaclass -metadata -mixin -movableChanged -mpl -msg -namedtuples -nanmax -nanmean -nanmedian -nanmin -nans -nansum -ndarray -ndim -nonpartial -np -npy -npz -nrow -numpy -objectName -objectNameChanged -objectNames -online -outmode -overplot -overplotted -overriden -param -params -phi -plugin -plugins -poly -polyfit -popup -pos -pre -programmatically -px -py -qapp -qcolor -qglue -qurl -rasterize -rasterized -refactored -relim -renderless -reprojection -resample -rgb -rng -roi -samp -scatterplot -scatterplots -scikit -screenshot -sep -setFixedSize -setFocus -setGeometry -setMask -setParent -setText -setValue -sextractor -simpleforms -stddev -subclassed -targetOffset -tb -timerType -toolbar -toolbars -tooltip -topLevelChanged -txt -ui -unbroadcast -uninstall -unregistration -unsubscribe -url -userData -util -utils -validator -vertices -visibilityChanged -vispy -vmax -vmin -voidptr -voxels -vx -vy -wcs -windowIconChanged -windowIconTextChanged -windowTitleChanged -workflow -workflows -wwt -xatt -xaxis -xc -xdelta -xfunc -xmax -xmin -xor -xval -xy -yatt -yaxis -yc -ydelta -yfunc -ymax -ymin -yval -zorder -astrodendro -subclass -subclasses -checkable -Whelan -dimensionality -gridded -analytics -backend -specviz -customizations -Checkable -affine -Affine -getter -setter -precomputed -dialogs -Jitter -logarithmically -Scipy -Plotly -hypercube -hypercubes -yt -changelog -opacities -unregister -allowedAreasChanged -orientationChanged -toolButtonStyleChanged -pixmap -centric -initializer -customizable -renormalized -booleans -idx -disambiguated -scalings -pathname -plo -resampled diff --git a/doc/tutorial_files/extinction.fits b/doc/tutorial_files/extinction.fits deleted file mode 100644 index 8abb31df0..000000000 Binary files a/doc/tutorial_files/extinction.fits and /dev/null differ diff --git a/doc/tutorial_files/yso.tbl b/doc/tutorial_files/yso.tbl deleted file mode 100644 index 69503256f..000000000 --- a/doc/tutorial_files/yso.tbl +++ /dev/null @@ -1,394 +0,0 @@ -\ORIGIN = 'CORES-TO-DISKS (c2d) LEGACY TEAM PIPELINE' -\DATE = '' -\COMMENT = '' -| c2d_ID | ra | D_ra | dec | D_dec | Q_pos | Q_merge | id2mass | Prob_Galc | alpha | D_alpha | alpha_chi2 | alpha_nfit | object_type | Av | D_Av | mag_IR1 | D_mag_IR1 | Av_chi2 | Av_nfit | J_flux_c | J_D_flux_c | J_date_c | J_Q_det_c | H_flux_c | H_D_flux_c | H_date_c | H_Q_det_c | Ks_flux_c | Ks_D_flux_c | Ks_date_c | Ks_Q_det_c | IR1_flux_1 | IR1_D_flux_1 | IR1_date_1 | IR1_Q_det_1 | IR1_flux_2 | IR1_D_flux_2 | IR1_date_2 | IR1_Q_det_2 | IR1_flux_c | IR1_D_flux_c | IR1_date_c | IR1_Q_det_c | IR1_Q_flux_m | IR1_imtype | IR1_src_area | IR1_amajor | IR1_aminor | IR1_tilt | IR2_flux_1 | IR2_D_flux_1 | IR2_date_1 | IR2_Q_det_1 | IR2_flux_2 | IR2_D_flux_2 | IR2_date_2 | IR2_Q_det_2 | IR2_flux_c | IR2_D_flux_c | IR2_date_c | IR2_Q_det_c | IR2_Q_flux_m | IR2_imtype | IR2_src_area | IR2_amajor | IR2_aminor | IR2_tilt | IR3_flux_1 | IR3_D_flux_1 | IR3_date_1 | IR3_Q_det_1 | IR3_flux_2 | IR3_D_flux_2 | IR3_date_2 | IR3_Q_det_2 | IR3_flux_c | IR3_D_flux_c | IR3_date_c | IR3_Q_det_c | IR3_Q_flux_m | IR3_imtype | IR3_src_area | IR3_amajor | IR3_aminor | IR3_tilt | IR4_flux_1 | IR4_D_flux_1 | IR4_date_1 | IR4_Q_det_1 | IR4_flux_2 | IR4_D_flux_2 | IR4_date_2 | IR4_Q_det_2 | IR4_flux_c | IR4_D_flux_c | IR4_date_c | IR4_Q_det_c | IR4_Q_flux_m | IR4_imtype | IR4_src_area | IR4_amajor | IR4_aminor | IR4_tilt | MP1_flux_1 | MP1_D_flux_1 | MP1_date_1 | MP1_Q_det_1 | MP1_flux_2 | MP1_D_flux_2 | MP1_date_2 | MP1_Q_det_2 | MP1_flux_c | MP1_D_flux_c | MP1_date_c | MP1_Q_det_c | MP1_Q_flux_m | MP1_imtype | MP1_src_area | MP1_amajor | MP1_aminor | MP1_tilt | MP2_flux_1 | MP2_D_flux_1 | MP2_date_1 | MP2_Q_det_1 | MP2_flux_2 | MP2_D_flux_2 | MP2_date_2 | MP2_Q_det_2 | MP2_flux_c | MP2_D_flux_c | MP2_date_c | MP2_Q_det_c | MP2_Q_flux_m | MP2_imtype | MP2_src_area | MP2_amajor | MP2_aminor | MP2_tilt | -| char | real | real | real | real | char | char | char | real | real | real | real | int | char | real | real | real | real | real | int | real | real | char | char | real | real | char | char | real | real | char | char | real | real | char | char | real | real | char | char | real | real | char | char | char | int | real | real | real | real | real | real | char | char | real | real | char | char | real | real | char | char | char | int | real | real | real | real | real | real | char | char | real | real | char | char | real | real | char | char | char | int | real | real | real | real | real | real | char | char | real | real | char | char | real | real | char | char | char | int | real | real | real | real | real | real | char | char | real | real | char | char | real | real | char | char | char | int | real | real | real | real | real | real | char | char | real | real | char | char | real | real | char | char | char | int | real | real | real | real | -| | deg | deg | deg | deg | | | | | | | | | | | | | | | | mJy | mJy | dateTtime | | mJy | mJy | dateTtime | | mJy | mJy | dateTtime | | mJy | mJy | dateTtime | | mJy | mJy | dateTtime | | mJy | mJy | dateTtime | | | | pixel_sq | pixels | pixels | deg | mJy | mJy | dateTtime | | mJy | mJy | dateTtime | | mJy | mJy | dateTtime | | | | pixel_sq | pixels | pixels | deg | mJy | mJy | dateTtime | | mJy | mJy | dateTtime | | mJy | mJy | dateTtime | | | | pixel_sq | pixels | pixels | deg | mJy | mJy | dateTtime | | mJy | mJy | dateTtime | | mJy | mJy | dateTtime | | | | pixel_sq | pixels | pixels | deg | mJy | mJy | dateTtime | | mJy | mJy | dateTtime | | mJy | mJy | dateTtime | | | | pixel_sq | pixels | pixels | deg | mJy | mJy | dateTtime | | mJy | mJy | dateTtime | | mJy | mJy | dateTtime | | | | pixel_sq | pixels | pixels | deg | -| null | -999. | -999. | -999. | -999. | Q | null | null | 999.00 | -9.99e+02 | -9.99e+02 | -9.99e+02 | 0 | null | -999.00 | -999.00 | -999.00 | -999.0000 | -999.00 | 0 | -9.99e+02 | -9.99e+02 | null | null | -9.99e+02 | -9.99e+02 | null | null | -9.99e+02 | -9.99e+02 | null | null | -9.99e+02 | -9.99e+02 | null | null | -9.99e+02 | -9.99e+02 | null | null | -9.99e+02 | -9.99e+02 | null | null | Q | null | -999.00 | -999.0 | -999.0 | -999.0 | -9.99e+02 | -9.99e+02 | null | null | -9.99e+02 | -9.99e+02 | null | null | -9.99e+02 | -9.99e+02 | null | null | Q | null | -999.00 | -999.0 | -999.0 | -999.0 | -9.99e+02 | -9.99e+02 | null | null | -9.99e+02 | -9.99e+02 | null | null | -9.99e+02 | -9.99e+02 | null | null | Q | null | -999.00 | -999.0 | -999.0 | -999.0 | -9.99e+02 | -9.99e+02 | null | null | -9.99e+02 | -9.99e+02 | null | null | -9.99e+02 | -9.99e+02 | null | null | Q | null | -999.00 | -999.0 | -999.0 | -999.0 | -9.99e+02 | -9.99e+02 | null | null | -9.99e+02 | -9.99e+02 | null | null | -9.99e+02 | -9.99e+02 | null | null | Q | null | -999.00 | -999.0 | -999.0 | -999.0 | -9.99e+02 | -9.99e+02 | null | null | -9.99e+02 | -9.99e+02 | null | null | -9.99e+02 | -9.99e+02 | null | null | Q | null | -999.00 | -999.0 | -999.0 | -999.0 | - SSTc2d J032522.2+304518 051.3423057 2.69e-08 +30.7548731 2.69e-08 A A null -5.00 1.50e+00 1.03e-01 2.52e+01 5 YSOc_red -999.00 -999.00 -999.00 -999.0000 -999.00 0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 9.50e-02 2.98e-02 2004-09-07T10:34:29.464 C Q -2 3.09 1.5 1.5 -45.0 1.10e+00 1.85e-01 2004-09-07T10:34:29.464 B 6.81e-01 9.37e-02 2004-09-07T10:34:29.464 A 6.31e-01 6.50e-02 2004-09-07T10:34:29.464 A B 7 2.97 1.7 1.7 -45.0 4.90e-01 7.28e-02 2004-09-07T10:34:29.464 B 5.80e-01 8.44e-02 2004-09-07T10:34:29.464 B 4.80e-01 5.56e-02 2004-09-07T10:34:29.464 A A 7 3.92 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 3.67e-01 9.17e-02 2004-09-07T10:34:29.464 C Q -2 4.98 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 3.13e+01 4.51e+00 2004-09-20T12:36:43.680 B Q -2 8.04 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 0.00e+00 0.00e+00 undetected Y Q 0 0.00 0.0 0.0 0.0 - SSTc2d J032522.3+304514 051.3430184 1.51e-06 +30.7538668 1.51e-06 A A null -5.00 2.34e+00 6.71e-02 4.05e+01 5 YSOc_red -999.00 -999.00 -999.00 -999.0000 -999.00 0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 3.15e-01 3.51e-02 2004-09-07T10:34:29.464 A 3.39e-01 4.68e-02 2004-09-07T10:34:29.464 A 3.34e-01 3.73e-02 2004-09-07T10:34:29.464 A A 7 3.09 1.5 1.5 -45.0 1.89e+00 1.51e-01 2004-09-07T10:34:29.464 A 2.29e+00 2.53e-01 2004-09-07T10:34:29.464 A 3.28e+00 2.89e-01 2004-09-07T10:34:29.464 K A 7 2.27 1.7 1.7 -45.0 5.20e+00 4.33e-01 2004-09-07T10:34:29.464 A 5.01e+00 3.70e-01 2004-09-07T10:34:29.464 A 5.19e+00 3.56e-01 2004-09-07T10:34:29.464 A A 2 8.16 3.0 2.4 -61.4 1.09e+01 5.97e-01 2004-09-07T10:34:29.464 A 1.09e+01 5.98e-01 2004-09-07T10:34:29.464 A 1.09e+01 5.67e-01 2004-09-07T10:34:29.464 A A 1 4.98 1.8 1.8 -45.0 4.63e+02 4.34e+01 2004-09-19T22:12:44.497 A 5.01e+02 4.65e+01 2004-09-20T03:00:42.863 A 4.79e+02 4.45e+01 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 1.29e+04 1.21e+03 2004-09-19T08:53:04.473 P Q 0 0.00 0.0 0.0 0.0 - SSTc2d J032522.6+304510 051.3442342 4.93e-09 +30.7527561 4.93e-09 A C null -2.37 1.16e+00 9.52e-02 1.66e+01 5 YSOc_red -999.00 -999.00 -999.00 -999.0000 -999.00 0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 2.34e-01 2.64e-02 2004-09-07T10:34:29.464 K 1.57e-01 2.48e-02 2004-09-07T10:34:29.464 B 3.16e-01 2.46e-02 2004-09-07T10:34:29.464 K B 7 1.77 1.5 1.5 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 4.56e-01 1.31e-01 2004-09-07T10:34:29.464 C Q -2 2.97 1.7 1.7 -45.0 -9.99e+02 -9.99e+02 null U 8.71e-01 9.30e-02 2004-09-07T10:34:29.464 A 8.47e-01 8.68e-02 2004-09-07T10:34:29.464 A Q 7 3.92 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 6.59e-01 9.10e-02 2004-09-07T10:34:29.464 A Q -2 4.98 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 2.86e+01 4.81e+00 2004-09-20T12:36:43.680 B Q -2 8.04 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 0.00e+00 0.00e+00 undetected Y Q 0 0.00 0.0 0.0 0.0 - SSTc2d J032536.5+304522 051.4020445 1.70e-08 +30.7561528 1.70e-08 A A null -5.00 2.62e+00 6.78e-02 6.33e+01 5 YSOc_red -999.00 -999.00 -999.00 -999.0000 -999.00 0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U 8.03e-01 1.19e-01 2004-09-07T10:34:29.464 B 7.11e-01 1.41e-01 2004-09-07T10:34:29.464 B Q -2 3.09 1.5 1.5 -45.0 1.08e+01 7.01e-01 2004-09-07T10:34:29.464 A 1.02e+01 7.49e-01 2004-09-07T10:34:29.464 A 1.11e+01 7.40e-01 2004-09-07T10:34:29.464 A A 1 2.97 1.7 1.7 -45.0 4.58e+01 2.38e+00 2004-09-07T10:34:29.464 A 4.41e+01 2.30e+00 2004-09-07T10:34:29.464 A 4.45e+01 2.22e+00 2004-09-07T10:34:29.464 A A 1 3.92 1.8 1.8 -45.0 1.34e+02 6.65e+00 2004-09-07T10:34:29.464 A 1.36e+02 7.35e+00 2004-09-07T10:34:29.464 A 1.36e+02 7.10e+00 2004-09-07T10:34:29.464 A A 1 4.98 1.8 1.8 -45.0 3.98e+03 3.76e+02 2004-09-19T22:12:44.497 A 4.11e+03 3.90e+02 2004-09-20T03:00:42.863 A 3.94e+03 3.71e+02 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 2.37e+04 2.22e+03 2004-09-19T08:53:04.473 S Q 2 0.00 0.0 0.0 0.0 - SSTc2d J032538.8+304406 051.4117940 2.56e-08 +30.7350532 2.56e-08 A A null -5.00 2.16e+00 6.31e-02 3.51e+01 5 YSOc_red -999.00 -999.00 -999.00 -999.0000 -999.00 0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 3.07e+00 2.55e-01 2004-09-07T10:34:29.464 A 3.28e+00 4.67e-01 2004-09-07T10:34:29.464 A 3.05e+00 2.26e-01 2004-09-07T10:34:29.464 A A 7 3.09 1.5 1.5 -45.0 1.12e+01 1.36e+00 2004-09-07T10:34:29.464 A 1.13e+01 1.37e+00 2004-09-07T10:34:29.464 A 1.15e+01 1.18e+00 2004-09-07T10:34:29.464 A A 2 10.16 3.9 2.3 -50.8 1.55e+01 1.58e+00 2004-09-07T10:34:29.464 A 1.54e+01 1.36e+00 2004-09-07T10:34:29.464 A 1.60e+01 1.18e+00 2004-09-07T10:34:29.464 A A 2 7.23 2.9 2.2 -89.5 2.02e+01 1.41e+00 2004-09-07T10:34:29.464 A 1.89e+01 1.45e+00 2004-09-07T10:34:29.464 A 2.04e+01 1.60e+00 2004-09-07T10:34:29.464 A A -2 4.98 1.8 1.8 -45.0 1.49e+03 1.40e+02 2004-09-19T22:12:44.497 A 1.51e+03 1.41e+02 2004-09-20T03:00:42.863 A 1.79e+03 1.67e+02 2004-09-20T12:36:43.680 A A 7 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 3.10e+04 2.90e+03 2004-09-19T08:53:04.473 S Q 0 0.00 0.0 0.0 0.0 - SSTc2d J032539.1+304358 051.4130155 9.87e-07 +30.7328223 9.87e-07 A A null -5.00 2.36e+00 6.08e-02 2.38e+02 5 YSOc_red -999.00 -999.00 -999.00 -999.0000 -999.00 0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 2.35e+00 1.66e-01 2004-09-07T10:34:29.464 A 2.46e+00 1.98e-01 2004-09-07T10:34:29.464 A 2.40e+00 1.41e-01 2004-09-07T10:34:29.464 A A 1 3.09 1.5 1.5 -45.0 2.29e+01 1.55e+00 2004-09-07T10:34:29.464 A 2.33e+01 1.42e+00 2004-09-07T10:34:29.464 A 2.33e+01 1.39e+00 2004-09-07T10:34:29.464 A A 1 2.97 1.7 1.7 -45.0 7.38e+01 3.81e+00 2004-09-07T10:34:29.464 A 7.36e+01 3.73e+00 2004-09-07T10:34:29.464 A 7.38e+01 3.65e+00 2004-09-07T10:34:29.464 A A 1 3.92 1.8 1.8 -45.0 1.33e+02 6.72e+00 2004-09-07T10:34:29.464 A 1.34e+02 6.65e+00 2004-09-07T10:34:29.464 A 1.33e+02 6.59e+00 2004-09-07T10:34:29.464 A A 2 6.80 2.5 2.4 -60.9 1.16e+03 1.36e+02 2004-09-19T22:12:44.497 A 1.01e+03 9.40e+01 2004-09-20T03:00:42.863 A 1.16e+03 1.25e+02 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 0.00e+00 0.00e+00 undetected Y Q 0 0.00 0.0 0.0 0.0 - SSTc2d J032637.5+301528 051.6561277 4.92e-07 +30.2578016 4.92e-07 A A 03263742+3015283 -5.00 1.09e+00 5.12e-02 5.34e+01 6 YSOc_red -999.00 -999.00 -999.00 -999.0000 -999.00 0 1.81e-01 -9.99e+02 1998-01-20T15:59:37 U 4.97e-01 -9.99e+02 1998-01-20T15:59:37 U 1.19e+00 8.47e-02 1998-01-20T15:59:37 A 3.43e+00 2.46e-01 2004-09-07T10:34:29.464 A 3.60e+00 2.64e-01 2004-09-07T10:34:29.464 A 3.62e+00 2.31e-01 2004-09-07T10:34:29.464 A A 1 3.09 1.5 1.5 -45.0 9.15e+00 4.74e-01 2004-09-07T10:34:29.464 A 9.25e+00 4.82e-01 2004-09-07T10:34:29.464 A 9.27e+00 4.63e-01 2004-09-07T10:34:29.464 A A 1 2.97 1.7 1.7 -45.0 1.11e+01 5.59e-01 2004-09-07T10:34:29.464 A 1.09e+01 5.41e-01 2004-09-07T10:34:29.464 A 1.11e+01 5.38e-01 2004-09-07T10:34:29.464 A A 1 3.92 1.8 1.8 -45.0 1.20e+01 5.68e-01 2004-09-07T10:34:29.464 A 1.20e+01 5.66e-01 2004-09-07T10:34:29.464 A 1.20e+01 5.61e-01 2004-09-07T10:34:29.464 A A 1 4.98 1.8 1.8 -45.0 4.08e+02 3.78e+01 2004-09-19T22:12:44.497 A 3.91e+02 3.64e+01 2004-09-20T03:00:42.863 A 3.96e+02 3.67e+01 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 4.30e+03 4.06e+02 2004-09-19T08:53:04.473 A Q 0 0.00 0.0 0.0 0.0 - SSTc2d J032738.2+301359 051.9093741 5.47e-07 +30.2329495 5.47e-07 A A 03273825+3013585 -5.00 -1.90e-01 4.84e-02 5.02e+00 6 YSOc_star+dust(IR4) 38.70 1.19 6.36 0.1080 1.72 6 2.35e-01 4.86e-02 1999-11-26T20:10:19 D 7.99e+00 3.17e-01 1999-11-26T20:10:19 A 3.64e+01 8.37e-01 1999-11-26T20:10:19 A 6.11e+01 3.17e+00 2004-09-07T10:34:29.464 A 6.30e+01 3.25e+00 2004-09-07T10:34:29.464 A 6.25e+01 3.14e+00 2004-09-07T10:34:29.464 A A 1 3.09 1.5 1.5 -45.0 7.20e+01 3.76e+00 2004-09-07T10:34:29.464 A 7.12e+01 3.77e+00 2004-09-07T10:34:29.464 A 7.20e+01 3.65e+00 2004-09-07T10:34:29.464 A A 1 2.97 1.7 1.7 -45.0 7.88e+01 3.81e+00 2004-09-07T10:34:29.464 A 7.93e+01 3.85e+00 2004-09-07T10:34:29.464 A 7.91e+01 3.77e+00 2004-09-07T10:34:29.464 A A 1 3.92 1.8 1.8 -45.0 9.33e+01 4.69e+00 2004-09-07T10:34:29.464 A 9.22e+01 4.50e+00 2004-09-07T10:34:29.464 A 9.31e+01 4.63e+00 2004-09-07T10:34:29.464 A A 1 4.98 1.8 1.8 -45.0 3.64e+02 3.37e+01 2004-09-19T22:12:44.497 A 3.35e+02 3.13e+01 2004-09-20T03:00:42.863 A 3.43e+02 3.19e+01 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 1.12e+03 1.14e+02 2004-09-19T08:53:04.473 A Q 0 0.00 0.0 0.0 0.0 - SSTc2d J032738.8+301258 051.9117838 1.60e-08 +30.2160953 1.60e-08 A C null -5.00 9.50e-01 9.60e-02 9.71e+01 5 YSOc_red -999.00 -999.00 -999.00 -999.0000 -999.00 0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 1.13e+00 7.70e-02 2004-09-07T10:34:29.464 K 7.97e-01 1.03e-01 2004-09-07T10:34:29.464 A 8.18e-01 7.92e-02 2004-09-07T10:34:29.464 A B 2 6.76 4.1 2.1 -60.7 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 1.29e+00 1.88e-01 2004-09-07T10:34:29.464 B Q -2 2.97 1.7 1.7 -45.0 -9.99e+02 -9.99e+02 null U 1.14e+00 1.38e-01 2004-09-07T10:34:29.464 A 1.10e+00 9.28e-02 2004-09-07T10:34:29.464 A Q 1 3.92 1.8 1.8 -45.0 5.44e-01 7.27e-02 2004-09-07T10:34:29.464 A -9.99e+02 -9.99e+02 null U 5.76e-01 5.90e-02 2004-09-07T10:34:29.464 A Q 7 4.98 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 8.25e+01 1.34e+01 2004-09-20T12:36:43.680 B Q -2 8.04 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 0.00e+00 0.00e+00 undetected Y Q 0 0.00 0.0 0.0 0.0 - SSTc2d J032739.1+301303 051.9128437 8.73e-07 +30.2175414 8.73e-07 A A null -5.00 2.68e+00 6.15e-02 1.65e+02 5 YSOc_red -999.00 -999.00 -999.00 -999.0000 -999.00 0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 6.82e-01 5.74e-02 2004-09-07T10:34:29.464 K -9.99e+02 -9.99e+02 null U 5.80e-01 4.36e-02 2004-09-07T10:34:29.464 A Q 7 1.77 1.5 1.5 -45.0 7.56e+00 4.83e-01 2004-09-07T10:34:29.464 A 7.64e+00 4.74e-01 2004-09-07T10:34:29.464 A 8.68e+00 6.85e-01 2004-09-07T10:34:29.464 K A 1 2.27 1.7 1.7 -45.0 1.87e+01 1.08e+00 2004-09-07T10:34:29.464 A 1.84e+01 1.01e+00 2004-09-07T10:34:29.464 A 2.04e+01 1.05e+00 2004-09-07T10:34:29.464 K A 7 2.54 1.8 1.8 -45.0 2.46e+01 1.20e+00 2004-09-07T10:34:29.464 A 2.41e+01 1.44e+00 2004-09-07T10:34:29.464 A 2.44e+01 1.17e+00 2004-09-07T10:34:29.464 A A 1 4.98 1.8 1.8 -45.0 1.95e+03 1.85e+02 2004-09-19T22:12:44.497 A 1.67e+03 1.62e+02 2004-09-20T03:00:42.863 A 1.71e+03 1.62e+02 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 2.14e+04 2.06e+03 2004-09-19T08:53:04.473 P Q 0 0.00 0.0 0.0 0.0 - SSTc2d J032741.5+302017 051.9228069 4.28e-07 +30.3379885 4.28e-07 A A 03274148+3020166 -5.00 -8.50e-01 4.78e-02 1.73e+01 6 YSOc_star+dust(IR2) 9.04 1.40 8.72 0.1590 0.93 4 4.96e+01 1.05e+00 1999-11-26T20:10:11 A 7.61e+01 1.89e+00 1999-11-26T20:10:11 A 7.27e+01 1.41e+00 1999-11-26T20:10:11 A 5.51e+01 2.80e+00 2004-09-07T10:34:29.464 A 5.43e+01 2.80e+00 2004-09-07T10:34:29.464 A 5.46e+01 2.71e+00 2004-09-07T10:34:29.464 A A 1 3.09 1.5 1.5 -45.0 4.71e+01 2.33e+00 2004-09-07T10:34:29.464 A 4.64e+01 2.39e+00 2004-09-07T10:34:29.464 A 4.66e+01 2.29e+00 2004-09-07T10:34:29.464 A A 1 2.97 1.7 1.7 -45.0 4.37e+01 2.10e+00 2004-09-07T10:34:29.464 A 4.28e+01 2.07e+00 2004-09-07T10:34:29.464 A 4.32e+01 2.05e+00 2004-09-07T10:34:29.464 A A 1 3.92 1.8 1.8 -45.0 5.11e+01 2.44e+00 2004-09-07T10:34:29.464 A 5.07e+01 2.42e+00 2004-09-07T10:34:29.464 A 5.08e+01 2.39e+00 2004-09-07T10:34:29.464 A A 1 4.98 1.8 1.8 -45.0 1.02e+02 9.50e+00 2004-09-19T22:12:44.497 A 1.08e+02 9.97e+00 2004-09-20T03:00:42.863 A 1.05e+02 9.72e+00 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 1.62e+02 1.75e+01 2004-09-19T08:53:04.473 A Q 0 0.00 0.0 0.0 0.0 - SSTc2d J032743.2+301229 051.9301209 8.78e-07 +30.2080268 8.78e-07 A A null -5.00 2.39e+00 6.04e-02 6.29e+01 5 YSOc_red -999.00 -999.00 -999.00 -999.0000 -999.00 0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 9.49e-01 6.11e-02 2004-09-07T10:34:29.464 K 1.05e+00 7.87e-02 2004-09-07T10:34:29.464 K 7.65e-01 5.76e-02 2004-09-07T10:34:29.464 A A 2 4.45 2.7 2.1 -85.0 5.17e+00 3.52e-01 2004-09-07T10:34:29.464 A 5.75e+00 5.73e-01 2004-09-07T10:34:29.464 K 5.39e+00 3.71e-01 2004-09-07T10:34:29.464 A A 1 2.27 1.7 1.7 -45.0 1.22e+01 6.75e-01 2004-09-07T10:34:29.464 A 1.21e+01 6.88e-01 2004-09-07T10:34:29.464 A 1.22e+01 6.44e-01 2004-09-07T10:34:29.464 A A 1 3.92 1.8 1.8 -45.0 2.59e+01 1.31e+00 2004-09-07T10:34:29.464 A 2.75e+01 1.35e+00 2004-09-07T10:34:29.464 A 2.75e+01 1.32e+00 2004-09-07T10:34:29.464 A A 1 4.98 1.8 1.8 -45.0 8.15e+02 7.60e+01 2004-09-19T22:12:44.497 A 7.36e+02 6.94e+01 2004-09-20T03:00:42.863 A 7.55e+02 7.05e+01 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 7.39e+03 6.88e+02 2004-09-19T08:53:04.473 A Q 0 0.00 0.0 0.0 0.0 - SSTc2d J032747.7+301205 051.9486420 7.97e-07 +30.2012577 7.97e-07 A A 03274767+3012043 -5.00 -9.00e-02 4.99e-02 1.52e+01 6 YSOc_star+dust(IR1) 14.79 2.37 6.85 0.3550 0.65 3 5.86e+01 1.40e+00 1999-11-26T20:10:19 A 1.70e+02 5.02e+00 1999-11-26T20:10:19 A 2.67e+02 5.65e+00 1999-11-26T20:10:19 A 3.91e+02 2.43e+01 2004-09-07T10:34:29.464 A 4.04e+02 2.52e+01 2004-09-07T10:34:29.464 A 4.08e+02 2.43e+01 2004-09-07T10:34:29.464 A A 9 3.09 1.5 1.5 -45.0 5.19e+02 3.27e+01 2004-09-07T10:34:29.464 A 5.17e+02 3.30e+01 2004-09-07T10:34:29.464 A 5.10e+02 3.12e+01 2004-09-07T10:34:29.464 A A 7 2.97 1.7 1.7 -45.0 7.87e+02 4.10e+01 2004-09-07T10:34:29.464 A 7.69e+02 3.88e+01 2004-09-07T10:34:29.464 A 7.86e+02 3.90e+01 2004-09-07T10:34:29.464 A A 9 3.92 1.8 1.8 -45.0 1.24e+03 6.54e+01 2004-09-07T10:34:29.464 A 1.31e+03 8.02e+01 2004-09-07T10:34:29.464 A 1.27e+03 6.45e+01 2004-09-07T10:34:29.464 A A 1 4.98 1.8 1.8 -45.0 1.64e+03 1.55e+02 2004-09-19T22:12:44.497 A 1.69e+03 1.59e+02 2004-09-20T03:00:42.863 A 1.67e+03 1.56e+02 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 1.81e+03 1.75e+02 2004-09-19T08:53:04.473 P Q 0 0.00 0.0 0.0 0.0 - SSTc2d J032800.1+300847 052.0003793 5.30e-07 +30.1463963 5.30e-07 A A 03280010+3008469 -5.00 -9.50e-01 4.81e-02 3.33e+01 6 YSOc_star+dust(IR2) 11.63 1.40 8.88 0.1590 1.10 4 2.47e+01 5.70e-01 1999-11-26T20:11:28 A 4.30e+01 1.19e+00 1999-11-26T20:11:28 A 4.94e+01 1.00e+00 1999-11-26T20:11:28 A 4.03e+01 2.07e+00 2004-09-07T10:34:29.464 A 4.03e+01 2.12e+00 2004-09-07T10:34:29.464 A 4.04e+01 2.01e+00 2004-09-07T10:34:29.464 A A 1 3.09 1.5 1.5 -45.0 3.58e+01 1.90e+00 2004-09-07T10:34:29.464 A 3.65e+01 1.87e+00 2004-09-07T10:34:29.464 A 3.59e+01 1.80e+00 2004-09-07T10:34:29.464 A A 1 2.97 1.7 1.7 -45.0 2.93e+01 1.44e+00 2004-09-07T10:34:29.464 A 2.90e+01 1.42e+00 2004-09-07T10:34:29.464 A 2.91e+01 1.39e+00 2004-09-07T10:34:29.464 A A 1 3.92 1.8 1.8 -45.0 2.59e+01 1.32e+00 2004-09-07T10:34:29.464 A 2.64e+01 1.31e+00 2004-09-07T10:34:29.464 A 2.63e+01 1.28e+00 2004-09-07T10:34:29.464 A A 1 4.98 1.8 1.8 -45.0 8.04e+01 7.45e+00 2004-09-19T22:12:44.497 A 8.22e+01 7.62e+00 2004-09-20T03:00:42.863 A 8.15e+01 7.56e+00 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 1.64e+02 1.79e+01 2004-09-19T08:53:04.473 A Q 0 0.00 0.0 0.0 0.0 - SSTc2d J032800.4+300801 052.0016387 4.63e-07 +30.1336816 4.63e-07 A A 03280041+3008012 -5.00 9.50e-01 4.85e-02 2.73e+01 6 YSOc_red -999.00 -999.00 -999.00 -999.0000 -999.00 0 1.42e-01 -9.99e+02 1999-11-26T20:11:28 U 5.31e-01 7.58e-02 1999-11-26T20:11:28 B 2.41e+00 1.11e-01 1999-11-26T20:11:28 A 1.31e+01 6.62e-01 2004-09-07T10:34:29.464 A 1.35e+01 6.93e-01 2004-09-07T10:34:29.464 A 1.33e+01 6.57e-01 2004-09-07T10:34:29.464 A A 1 3.09 1.5 1.5 -45.0 2.70e+01 1.36e+00 2004-09-07T10:34:29.464 A 2.74e+01 1.38e+00 2004-09-07T10:34:29.464 A 2.73e+01 1.34e+00 2004-09-07T10:34:29.464 A A 1 2.97 1.7 1.7 -45.0 4.51e+01 2.18e+00 2004-09-07T10:34:29.464 A 4.43e+01 2.12e+00 2004-09-07T10:34:29.464 A 4.47e+01 2.12e+00 2004-09-07T10:34:29.464 A A 1 3.92 1.8 1.8 -45.0 7.01e+01 3.44e+00 2004-09-07T10:34:29.464 A 7.09e+01 3.43e+00 2004-09-07T10:34:29.464 A 7.02e+01 3.36e+00 2004-09-07T10:34:29.464 A A 1 4.98 1.8 1.8 -45.0 3.66e+02 3.39e+01 2004-09-19T22:12:44.497 A 3.66e+02 3.39e+01 2004-09-20T03:00:42.863 A 3.68e+02 3.41e+01 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 6.81e+02 6.54e+01 2004-09-19T08:53:04.473 A Q 0 0.00 0.0 0.0 0.0 - SSTc2d J032814.6+302956 052.0609975 3.71e-08 +30.4987883 3.71e-08 A C 03281460+3029548 -2.38 4.50e-01 5.86e-02 3.12e+02 6 YSOc_PAH-em -999.00 -999.00 -999.00 -999.0000 -999.00 0 2.38e+00 1.38e-01 1999-11-26T20:11:54 A 3.32e+00 2.57e-01 1999-11-26T20:11:54 A 3.36e+00 2.20e-01 1999-11-26T20:11:54 E -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 1.55e+00 3.14e-01 2004-09-07T10:34:29.464 C Q -2 3.09 1.5 1.5 -45.0 -9.99e+02 -9.99e+02 null U 1.13e+00 1.60e-01 2004-09-07T10:34:29.464 A 9.56e-01 7.67e-02 2004-09-07T10:34:29.464 A Q 7 2.97 1.7 1.7 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 1.60e+00 1.28e-01 2004-09-07T10:34:29.464 A Q 7 3.92 1.8 1.8 -45.0 3.66e+01 2.07e+00 2004-09-07T10:34:29.464 K 1.30e+01 9.43e-01 2004-09-07T10:34:29.464 K 3.36e+01 1.83e+00 2004-09-07T10:34:29.464 K E 7 2.54 1.8 1.8 -45.0 1.66e+01 1.65e+00 2004-09-19T22:12:44.497 A -9.99e+02 -9.99e+02 null U 1.69e+01 1.59e+00 2004-09-20T12:36:43.680 A Q 7 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 5.84e+02 5.53e+01 2004-09-19T08:53:04.473 W Q 0 0.00 0.0 0.0 0.0 - SSTc2d J032832.6+311105 052.1356586 1.00e-06 +31.1847634 1.00e-06 A A 03283258+3111040 -5.00 7.80e-01 5.38e-02 2.15e+01 6 YSOc_red -999.00 -999.00 -999.00 -999.0000 -999.00 0 1.74e-01 -9.99e+02 2000-10-01T20:39:50 U 6.89e-01 -9.99e+02 2000-10-01T20:39:50 U 1.07e+00 1.14e-01 2000-10-01T20:39:50 B 1.31e+00 1.29e-01 2004-02-10T08:34:30.264 A 9.59e-01 7.75e-02 2004-02-10T08:34:30.264 A 1.01e+00 6.97e-02 2005-09-16T09:56:12.848 A B 2 4.85 3.0 2.0 -51.5 2.19e+00 1.79e-01 2004-02-10T08:34:30.264 A 2.07e+00 1.17e-01 2004-02-10T08:34:30.264 K 2.28e+00 1.28e-01 2004-02-10T08:34:30.264 K A 1 2.27 1.7 1.7 -45.0 2.95e+00 1.71e-01 2004-02-10T08:34:30.264 A 2.53e+00 1.64e-01 2004-02-10T08:34:30.264 A 2.57e+00 1.29e-01 2005-09-16T09:56:12.848 A A 1 2.54 1.8 1.8 -45.0 4.18e+00 2.11e-01 2004-02-10T08:34:30.264 A 3.89e+00 2.04e-01 2004-02-10T08:34:30.264 A 4.01e+00 1.99e-01 2004-02-10T08:34:30.264 A A 1 4.98 1.8 1.8 -45.0 5.40e+01 5.04e+00 2004-09-19T22:12:44.497 A 5.65e+01 5.24e+00 2004-09-20T03:00:42.863 A 5.53e+01 5.13e+00 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 1.79e+02 1.95e+01 2004-09-19T08:53:04.473 P Q 0 0.00 0.0 0.0 0.0 - SSTc2d J032834.5+310051 052.1437026 6.54e-07 +31.0141937 6.54e-07 A A 03283450+3100510 -5.00 8.30e-01 4.88e-02 4.41e+01 6 YSOc_star+dust(IR1) 17.72 2.40 10.07 0.3590 1.57 3 1.59e+00 9.39e-02 2000-10-01T20:39:59 A 5.43e+00 2.45e-01 2000-10-01T20:39:59 A 1.05e+01 3.18e-01 2000-10-01T20:39:59 A 5.50e+01 2.71e+00 2004-02-10T08:34:30.264 A 5.52e+01 2.78e+00 2004-02-10T08:34:30.264 A 5.47e+01 2.66e+00 2004-02-10T08:34:30.264 A A 7 1.77 1.5 1.5 -45.0 -9.99e+02 -9.99e+02 null N 1.15e+02 6.85e+00 2004-02-10T08:34:30.264 A 1.15e+02 6.86e+00 2004-02-10T08:34:30.264 A Q 1 2.97 1.7 1.7 -45.0 2.18e+02 1.06e+01 2004-02-10T08:34:30.264 A 2.22e+02 1.08e+01 2004-02-10T08:34:30.264 A 2.21e+02 1.09e+01 2004-02-10T08:34:30.264 A A 2 6.90 3.4 2.6 -54.5 -9.99e+02 -9.99e+02 null N 2.42e+02 1.22e+01 2004-02-10T08:34:30.264 A 2.42e+02 1.22e+01 2004-02-10T08:34:30.264 A Q 1 4.98 1.8 1.8 -45.0 1.12e+03 1.06e+02 2004-09-19T22:12:44.497 A 1.29e+03 1.20e+02 2004-09-20T03:00:42.863 A 1.16e+03 1.09e+02 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 2.70e+03 2.54e+02 2004-09-19T08:53:04.473 A Q 0 0.00 0.0 0.0 0.0 - SSTc2d J032834.5+310705 052.1438718 4.81e-07 +31.1181943 4.81e-07 A A null -5.00 5.40e-01 5.34e-02 9.30e+01 5 YSOc -999.00 -999.00 -999.00 -999.0000 -999.00 0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 3.81e+00 1.96e-01 2004-02-10T08:34:30.264 A 4.74e+00 2.52e-01 2004-02-10T08:34:30.264 A 4.59e+00 2.23e-01 2005-09-16T09:56:12.848 A B 1 1.77 1.5 1.5 -45.0 1.07e+01 5.34e-01 2004-02-10T08:34:30.264 A 1.33e+01 6.67e-01 2004-02-10T08:34:30.264 A 1.18e+01 5.98e-01 2004-02-10T08:34:30.264 A B 1 2.97 1.7 1.7 -45.0 1.96e+01 9.42e-01 2004-02-10T08:34:30.264 A 2.43e+01 1.19e+00 2004-02-10T08:34:30.264 A 2.42e+01 1.13e+00 2005-09-16T09:56:12.848 A B 1 2.54 1.8 1.8 -45.0 3.17e+01 1.51e+00 2004-02-10T08:34:30.264 A 3.77e+01 1.83e+00 2004-02-10T08:34:30.264 A 3.44e+01 1.67e+00 2004-02-10T08:34:30.264 A B 1 4.98 1.8 1.8 -45.0 5.83e+01 5.44e+00 2004-09-19T22:12:44.497 A 6.10e+01 5.65e+00 2004-09-20T03:00:42.863 A 6.04e+01 5.59e+00 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 1.59e+02 1.74e+01 2004-09-19T08:53:04.473 A Q 0 0.00 0.0 0.0 0.0 - SSTc2d J032835.0+302010 052.1459376 8.11e-07 +30.3360744 8.11e-07 A A 03283504+3020096 -5.00 1.50e-01 4.96e-02 1.52e+02 6 YSOc_star+dust(IR4) 12.42 1.18 12.25 0.1090 1.25 6 8.65e-01 7.09e-02 2000-10-01T20:40:42 A 1.77e+00 1.03e-01 2000-10-01T20:40:42 A 2.22e+00 1.11e-01 2000-10-01T20:40:42 A 1.47e+00 7.73e-02 2004-09-07T10:34:29.464 A 1.44e+00 7.69e-02 2004-09-07T10:34:29.464 A 1.46e+00 7.37e-02 2004-09-07T10:34:29.464 A A 1 3.09 1.5 1.5 -45.0 1.22e+00 6.19e-02 2004-09-07T10:34:29.464 A 1.13e+00 6.02e-02 2004-09-07T10:34:29.464 A 1.18e+00 5.86e-02 2004-09-07T10:34:29.464 A A 1 2.97 1.7 1.7 -45.0 9.93e-01 7.47e-02 2004-09-07T10:34:29.464 A 1.05e+00 7.01e-02 2004-09-07T10:34:29.464 A 1.02e+00 6.46e-02 2004-09-07T10:34:29.464 A A 1 3.92 1.8 1.8 -45.0 1.40e+00 8.76e-02 2004-09-07T10:34:29.464 A 1.36e+00 8.53e-02 2004-09-07T10:34:29.464 A 1.38e+00 7.64e-02 2004-09-07T10:34:29.464 A A 1 4.98 1.8 1.8 -45.0 4.10e+01 3.80e+00 2004-09-19T22:12:44.497 A 4.16e+01 3.86e+00 2004-09-20T03:00:42.863 A 4.17e+01 3.86e+00 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 1.37e+02 1.71e+01 2004-09-19T08:53:04.473 P Q 0 0.00 0.0 0.0 0.0 - SSTc2d J032837.1+311331 052.1545476 4.22e-08 +31.2252339 4.22e-08 A A 03283706+3113310 -5.00 2.35e+00 7.58e-02 3.99e+01 5 YSOc_red -999.00 -999.00 -999.00 -999.0000 -999.00 0 5.85e-01 -9.99e+02 2000-10-01T20:39:50 U 1.18e+00 1.41e-01 2000-10-01T20:39:50 B 1.01e+01 -9.99e+02 2000-10-01T20:39:50 U 3.55e+01 3.49e+00 2004-02-10T08:34:30.264 A 3.44e+01 3.04e+00 2004-02-10T08:34:30.264 A 3.01e+01 1.75e+00 2005-09-16T09:56:12.848 A A 1 2.74 1.8 2.0 -55.2 1.48e+02 1.24e+01 2004-02-10T08:34:30.264 A 1.43e+02 1.25e+01 2004-02-10T08:34:30.264 A 8.92e+01 5.47e+00 2004-02-10T08:34:30.264 K A 7 2.27 1.7 1.7 -45.0 2.88e+02 1.78e+01 2004-02-10T08:34:30.264 A 2.81e+02 1.65e+01 2004-02-10T08:34:30.264 A 2.67e+02 1.33e+01 2005-09-16T09:56:12.848 A A 2 8.00 1.9 5.5 -31.1 7.40e+02 3.74e+01 2004-02-10T08:34:30.264 A 7.35e+02 3.82e+01 2004-02-10T08:34:30.264 A 7.22e+02 4.13e+01 2004-02-10T08:34:30.264 A A 9 4.98 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 6.86e+03 1.15e+03 2004-09-20T12:36:43.680 B Q -2 8.04 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 5.38e+04 5.02e+03 2004-09-19T08:53:04.473 P Q 0 0.00 0.0 0.0 0.0 - SSTc2d J032839.1+310602 052.1628996 1.50e-06 +31.1004773 1.50e-06 A A null -1.48 1.68e+00 5.77e-02 4.24e+00 5 YSOc_red -999.00 -999.00 -999.00 -999.0000 -999.00 0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 1.21e-01 1.05e-02 2004-02-10T08:34:30.264 A 1.41e-01 1.07e-02 2004-02-10T08:34:30.264 A 1.25e-01 7.83e-03 2005-09-16T09:56:12.848 A A 1 1.77 1.5 1.5 -45.0 3.29e-01 2.55e-02 2004-02-10T08:34:30.264 A 3.12e-01 2.52e-02 2004-02-10T08:34:30.264 A 2.72e-01 1.58e-02 2005-09-16T09:56:12.848 A A 1 2.27 1.7 1.7 -45.0 5.07e-01 3.98e-02 2004-02-10T08:34:30.264 A 5.18e-01 3.95e-02 2004-02-10T08:34:30.264 A 4.34e-01 2.86e-02 2005-09-16T09:56:12.848 A A 7 2.54 1.8 1.8 -45.0 1.27e+00 8.81e-02 2004-02-10T08:34:30.264 A 1.27e+00 8.28e-02 2004-02-10T08:34:30.264 A 9.39e-01 5.14e-02 2005-09-16T09:56:12.848 A A 1 2.54 1.8 1.8 -45.0 2.38e+01 2.22e+00 2004-09-19T22:12:44.497 A 2.48e+01 2.31e+00 2004-09-20T03:00:42.863 A 2.44e+01 2.27e+00 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 9.40e+01 1.11e+01 2004-09-19T08:53:04.473 P Q 0 0.00 0.0 0.0 0.0 - SSTc2d J032839.7+311732 052.1654377 5.03e-07 +31.2922025 5.03e-07 A A 03283968+3117321 -5.00 5.70e-01 4.88e-02 2.82e+01 6 YSOc_red -999.00 -999.00 -999.00 -999.0000 -999.00 0 7.69e-02 -9.99e+02 2000-10-01T20:39:42 U 2.84e-01 9.24e-02 2000-10-01T20:39:42 D 2.29e+00 1.05e-01 2000-10-01T20:39:42 A 1.27e+01 6.18e-01 2004-02-10T08:34:30.264 A 1.48e+01 7.30e-01 2004-02-10T08:34:30.264 A 1.32e+01 6.75e-01 2004-02-10T08:34:30.264 A B 1 3.09 1.5 1.5 -45.0 2.20e+01 1.09e+00 2004-02-10T08:34:30.264 A 2.51e+01 1.25e+00 2004-02-10T08:34:30.264 A 2.29e+01 1.16e+00 2004-02-10T08:34:30.264 A A 1 2.97 1.7 1.7 -45.0 3.12e+01 1.51e+00 2004-02-10T08:34:30.264 A 3.34e+01 1.61e+00 2004-02-10T08:34:30.264 A 3.22e+01 1.54e+00 2004-02-10T08:34:30.264 A A 1 3.92 1.8 1.8 -45.0 4.09e+01 1.99e+00 2004-02-10T08:34:30.264 A 4.27e+01 2.08e+00 2004-02-10T08:34:30.264 A 4.20e+01 2.02e+00 2004-02-10T08:34:30.264 A A 1 4.98 1.8 1.8 -45.0 1.82e+02 1.69e+01 2004-09-19T22:12:44.497 A 1.90e+02 1.76e+01 2004-09-20T03:00:42.863 A 1.85e+02 1.71e+01 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 3.63e+02 3.55e+01 2004-09-19T08:53:04.473 A Q 0 0.00 0.0 0.0 0.0 - SSTc2d J032843.3+311733 052.1803310 8.59e-07 +31.2924790 8.59e-07 A A 03284325+3117330 -5.00 3.60e-01 5.27e-02 1.41e+01 6 YSOc_star+dust(IR1) 16.63 2.39 7.92 0.3590 1.36 3 1.47e+01 3.11e-01 2000-10-01T20:39:42 A 4.63e+01 2.22e+00 2000-10-01T20:39:42 A 8.42e+01 2.87e+00 2000-10-01T20:39:42 A 1.93e+02 9.98e+00 2004-02-10T08:34:30.264 A 1.87e+02 1.15e+01 2004-02-10T08:34:30.264 A 1.78e+02 1.10e+01 2004-02-10T08:34:30.264 A A 7 3.09 1.5 1.5 -45.0 3.35e+02 1.68e+01 2004-02-10T08:34:30.264 A 3.71e+02 2.98e+01 2004-02-10T08:34:30.264 K 3.51e+02 1.94e+01 2004-02-10T08:34:30.264 A A 7 2.27 1.7 1.7 -45.0 4.90e+02 2.48e+01 2004-02-10T08:34:30.264 A 5.37e+02 3.03e+01 2004-02-10T08:34:30.264 A 4.88e+02 2.87e+01 2004-02-10T08:34:30.264 A A 1 3.92 1.8 1.8 -45.0 7.42e+02 5.67e+01 2004-02-10T08:34:30.264 A 7.99e+02 4.61e+01 2004-02-10T08:34:30.264 A 7.71e+02 4.52e+01 2004-02-10T08:34:30.264 A A 7 4.98 1.8 1.8 -45.0 1.95e+03 2.02e+02 2004-09-19T22:12:44.497 A 2.00e+03 2.15e+02 2004-09-20T03:00:42.863 A 1.88e+03 1.88e+02 2004-09-20T12:36:43.680 A A 7 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 2.08e+03 2.00e+02 2004-09-19T08:53:04.473 P Q 0 0.00 0.0 0.0 0.0 - SSTc2d J032845.3+310542 052.1887309 8.10e-07 +31.0949764 8.10e-07 A A 03284530+3105420 -5.00 1.11e+00 5.20e-02 1.18e+02 6 YSOc_red -999.00 -999.00 -999.00 -999.0000 -999.00 0 6.39e-02 -9.99e+02 2000-10-01T20:39:59 U 4.59e-01 -9.99e+02 2000-10-01T20:39:59 U 7.21e-01 1.00e-01 2000-10-01T20:39:59 B 1.52e+00 9.29e-02 2004-02-10T08:34:30.264 A 1.53e+00 1.10e-01 2004-02-10T08:34:30.264 A 1.22e+00 6.35e-02 2005-09-16T09:56:12.848 A A 1 1.91 1.9 1.3 -56.0 3.51e+00 1.99e-01 2004-02-10T08:34:30.264 A 3.49e+00 2.32e-01 2004-02-10T08:34:30.264 A 2.73e+00 1.34e-01 2005-09-16T09:56:12.848 A A 1 2.27 1.7 1.7 -45.0 3.27e+00 1.78e-01 2004-02-10T08:34:30.264 A 3.18e+00 1.77e-01 2004-02-10T08:34:30.264 A 2.67e+00 1.34e-01 2005-09-16T09:56:12.848 A A 1 2.54 1.8 1.8 -45.0 4.00e+00 2.05e-01 2004-02-10T08:34:30.264 A 3.55e+00 1.87e-01 2004-02-10T08:34:30.264 A 2.69e+00 1.31e-01 2005-09-16T09:56:12.848 A A 1 2.54 1.8 1.8 -45.0 2.15e+02 1.99e+01 2004-09-19T22:12:44.497 A 2.17e+02 2.01e+01 2004-09-20T03:00:42.863 A 2.13e+02 1.99e+01 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 1.52e+03 1.50e+02 2004-09-19T08:53:04.473 P Q 0 0.00 0.0 0.0 0.0 - SSTc2d J032845.4+310544 052.1891475 2.59e-09 +31.0955889 2.59e-09 A C null -5.00 1.65e+00 1.15e-01 2.77e+01 5 YSOc_red -999.00 -999.00 -999.00 -999.0000 -999.00 0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 2.78e-01 3.92e-02 2004-02-10T08:34:30.264 A 1.63e-01 3.85e-02 2004-09-07T10:34:29.464 C 1.80e-01 2.93e-02 2004-09-07T10:34:29.464 B B 7 3.09 1.5 1.5 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 2.86e-01 3.16e-02 2005-09-16T09:56:12.848 A Q 7 2.97 1.7 1.7 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 2.36e-01 4.08e-02 2005-09-16T09:56:12.848 B Q -2 2.54 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 1.95e-01 4.37e-02 2005-09-16T09:56:12.848 C Q -2 2.54 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 3.20e+01 5.74e+00 2004-09-20T12:36:43.680 B Q -2 8.04 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 0.00e+00 0.00e+00 undetected Y Q 0 0.00 0.0 0.0 0.0 - SSTc2d J032845.4+310545 052.1891814 2.02e-09 +31.0958687 2.02e-09 A C null -5.00 1.50e+00 1.02e-01 2.71e+01 5 YSOc_red -999.00 -999.00 -999.00 -999.0000 -999.00 0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 1.10e-01 1.66e-02 2005-09-16T09:56:12.848 B Q -2 1.77 1.5 1.5 -45.0 3.10e-01 4.42e-02 2004-02-10T08:34:30.264 A -9.99e+02 -9.99e+02 null U 3.10e-01 2.82e-02 2004-02-10T08:34:30.264 A Q 7 2.97 1.7 1.7 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 1.58e-01 3.19e-02 2005-09-16T09:56:12.848 C Q -2 2.54 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 1.29e-01 3.82e-02 2005-09-16T09:56:12.848 C Q -2 2.54 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 1.91e+01 2.99e+00 2004-09-20T12:36:43.680 B Q -2 8.04 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 0.00e+00 0.00e+00 undetected Y Q 0 0.00 0.0 0.0 0.0 - SSTc2d J032850.6+304245 052.2108972 4.61e-07 +30.7124038 4.61e-07 A A 03285063+3042446 -5.00 -1.70e-01 4.78e-02 1.79e+01 6 YSOc_star+dust(IR1) 16.07 2.40 10.98 0.3590 1.90 3 1.07e+00 6.98e-02 2000-10-01T20:40:25 A 2.96e+00 1.31e-01 2000-10-01T20:40:25 A 5.40e+00 1.44e-01 2000-10-01T20:40:25 A 8.93e+00 4.43e-01 2004-09-07T10:34:29.464 A 9.05e+00 4.44e-01 2004-09-07T10:34:29.464 A 9.02e+00 4.35e-01 2004-09-07T10:34:29.464 A A 1 3.09 1.5 1.5 -45.0 9.95e+00 4.86e-01 2004-09-07T10:34:29.464 A 9.88e+00 4.87e-01 2004-09-07T10:34:29.464 A 9.92e+00 4.76e-01 2004-09-07T10:34:29.464 A A 1 2.97 1.7 1.7 -45.0 -9.99e+02 -9.99e+02 null U 9.38e+00 5.96e-01 2004-09-07T10:34:29.464 A 9.41e+00 4.47e-01 2004-09-07T10:34:29.464 A Q 1 3.92 1.8 1.8 -45.0 1.20e+01 5.77e-01 2004-09-07T10:34:29.464 A 1.18e+01 5.70e-01 2004-09-07T10:34:29.464 A 1.19e+01 5.63e-01 2004-09-07T10:34:29.464 A A 1 4.98 1.8 1.8 -45.0 6.40e+01 5.93e+00 2004-09-19T22:12:44.497 A 6.05e+01 5.64e+00 2004-09-20T03:00:42.863 A 6.20e+01 5.75e+00 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 7.42e+01 9.25e+00 2004-09-19T08:53:04.473 A Q 0 0.00 0.0 0.0 0.0 - SSTc2d J032854.0+311809 052.2248142 5.36e-07 +31.3025817 5.36e-07 A A 03285392+3118092 -5.00 -5.40e-01 4.89e-02 1.16e+01 6 YSOc_star+dust(IR3) 22.26 1.25 8.47 0.1270 1.61 5 1.88e+00 1.04e-01 2000-10-01T20:39:42 A 1.27e+01 4.21e-01 2000-10-01T20:39:42 A 2.97e+01 6.56e-01 2000-10-01T20:39:42 A 2.47e+01 1.24e+00 2004-02-10T08:34:30.264 A 2.86e+01 1.54e+00 2004-02-10T08:34:30.264 A 2.54e+01 1.44e+00 2004-02-10T08:34:30.264 A A 1 3.09 1.5 1.5 -45.0 2.49e+01 1.24e+00 2004-02-10T08:34:30.264 A 2.76e+01 1.39e+00 2004-02-10T08:34:30.264 A 2.56e+01 1.31e+00 2004-02-10T08:34:30.264 A A 1 2.97 1.7 1.7 -45.0 2.51e+01 1.23e+00 2004-02-10T08:34:30.264 A 2.66e+01 1.32e+00 2004-02-10T08:34:30.264 A 2.58e+01 1.25e+00 2004-02-10T08:34:30.264 A A 1 3.92 1.8 1.8 -45.0 3.34e+01 1.62e+00 2004-02-10T08:34:30.264 A 3.15e+01 1.54e+00 2004-02-10T08:34:30.264 A 3.22e+01 1.55e+00 2004-02-10T08:34:30.264 A A 1 4.98 1.8 1.8 -45.0 8.58e+01 7.93e+00 2004-09-19T22:12:44.497 A 8.51e+01 7.87e+00 2004-09-20T03:00:42.863 A 8.60e+01 7.95e+00 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 3.31e+02 4.11e+01 2004-09-19T08:53:04.473 S Q 0 0.00 0.0 0.0 0.0 - SSTc2d J032856.6+311836 052.2360243 5.89e-07 +31.3098730 5.89e-07 A A 03285663+3118356 -5.00 -7.80e-01 4.84e-02 3.01e+01 6 YSOc_star+dust(IR2) 16.98 1.40 7.72 0.1600 1.03 4 1.92e+01 3.89e-01 2000-10-01T20:39:42 A 5.40e+01 1.14e+00 2000-10-01T20:39:42 A 8.77e+01 1.70e+00 2000-10-01T20:39:42 A 8.15e+01 4.20e+00 2004-02-10T08:34:30.264 A 8.22e+01 4.27e+00 2004-02-10T08:34:30.264 A 8.34e+01 4.24e+00 2004-02-10T08:34:30.264 A A 1 3.09 1.5 1.5 -45.0 7.43e+01 4.11e+00 2004-02-10T08:34:30.264 A 8.06e+01 4.58e+00 2004-02-10T08:34:30.264 A 8.11e+01 4.61e+00 2004-02-10T08:34:30.264 A A 1 2.97 1.7 1.7 -45.0 5.99e+01 2.90e+00 2004-02-10T08:34:30.264 A 6.23e+01 3.12e+00 2004-02-10T08:34:30.264 A 6.09e+01 2.95e+00 2004-02-10T08:34:30.264 A A 1 3.92 1.8 1.8 -45.0 6.47e+01 3.13e+00 2004-02-10T08:34:30.264 A 6.54e+01 3.26e+00 2004-02-10T08:34:30.264 A 6.47e+01 3.15e+00 2004-02-10T08:34:30.264 A A 1 4.98 1.8 1.8 -45.0 2.06e+02 1.93e+01 2004-09-19T22:12:44.497 A 2.28e+02 2.11e+01 2004-09-20T03:00:42.863 A 2.15e+02 2.00e+01 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 6.29e+02 6.57e+01 2004-09-19T08:53:04.473 P Q 0 0.00 0.0 0.0 0.0 - SSTc2d J032856.6+310737 052.2358162 5.74e-09 +31.1269318 5.74e-09 A A null -5.00 1.20e-01 6.12e-02 8.76e+01 5 YSOc_red -999.00 -999.00 -999.00 -999.0000 -999.00 0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 7.48e-01 7.57e-02 2004-02-10T08:34:30.264 A 8.07e-01 8.06e-02 2004-02-10T08:34:30.264 A 7.56e-01 5.10e-02 2005-09-16T09:56:12.848 A A 7 1.77 1.5 1.5 -45.0 2.49e+00 2.43e-01 2004-02-10T08:34:30.264 A 2.39e+00 2.07e-01 2004-02-10T08:34:30.264 A 2.50e+00 1.56e-01 2005-09-16T09:56:12.848 A A 2 6.35 3.9 2.1 -84.6 1.39e+00 1.44e-01 2004-02-10T08:34:30.264 A 1.29e+00 1.42e-01 2004-02-10T08:34:30.264 A 2.03e+00 1.28e-01 2005-09-16T09:56:12.848 K A 7 2.54 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 6.58e-01 7.43e-02 2005-09-16T09:56:12.848 A Q -2 2.54 1.8 1.8 -45.0 1.17e+01 1.11e+00 2004-09-19T22:12:44.497 A 1.14e+01 1.13e+00 2004-09-20T03:00:42.863 A 1.16e+01 1.09e+00 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 1.11e+02 1.33e+01 2004-09-19T08:53:04.473 S Q 0 0.00 0.0 0.0 0.0 - SSTc2d J032856.3+312228 052.2346594 4.96e-07 +31.3744142 4.96e-07 A A 03285630+3122279 -5.00 -1.40e-01 4.84e-02 1.76e+00 6 YSOc_star+dust(IR2) 24.83 1.43 8.97 0.1620 0.50 4 7.35e-01 6.37e-02 2000-10-01T20:39:42 A 5.56e+00 2.15e-01 2000-10-01T20:39:42 A 1.22e+01 3.03e-01 2000-10-01T20:39:42 A 1.55e+01 8.00e-01 2004-02-10T08:34:30.264 A 1.66e+01 9.12e-01 2004-02-10T08:34:30.264 A 1.62e+01 8.36e-01 2004-02-10T08:34:30.264 A A 1 3.09 1.5 1.5 -45.0 1.99e+01 1.03e+00 2004-02-10T08:34:30.264 A 1.75e+01 9.00e-01 2004-02-10T08:34:30.264 A 1.77e+01 9.00e-01 2004-02-10T08:34:30.264 A A 1 2.97 1.7 1.7 -45.0 2.43e+01 1.15e+00 2004-02-10T08:34:30.264 A 2.15e+01 1.04e+00 2004-02-10T08:34:30.264 A 2.32e+01 1.10e+00 2004-02-10T08:34:30.264 A A 1 3.92 1.8 1.8 -45.0 3.38e+01 1.60e+00 2004-02-10T08:34:30.264 A 2.94e+01 1.43e+00 2004-02-10T08:34:30.264 A 3.08e+01 1.47e+00 2004-02-10T08:34:30.264 A B 1 4.98 1.8 1.8 -45.0 8.92e+01 8.29e+00 2004-09-19T22:12:44.497 A 9.17e+01 8.51e+00 2004-09-20T03:00:42.863 A 9.05e+01 8.42e+00 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 5.46e+02 6.40e+01 2004-09-19T08:53:04.473 A Q 0 0.00 0.0 0.0 0.0 - SSTc2d J032857.2+311419 052.2383644 4.49e-10 +31.2386438 4.49e-10 A A 03285720+3114189 -5.00 -1.82e+00 6.67e-02 6.04e+01 6 YSOc_star+dust(MP1) 1.19 1.10 7.49 0.1000 0.15 7 8.43e+02 1.94e+01 2000-10-01T20:39:50 A 7.96e+02 1.61e+01 2000-10-01T20:39:50 A 5.74e+02 1.06e+01 2000-10-01T20:39:50 A 2.27e+02 1.27e+01 2004-02-10T08:34:30.264 A 2.11e+02 1.34e+01 2004-02-10T08:34:30.264 A 2.53e+02 1.29e+01 2005-09-16T09:56:12.848 A A 9 1.77 1.5 1.5 -45.0 1.64e+02 8.26e+00 2004-02-10T08:34:30.264 A 1.16e+02 9.45e+00 2004-02-10T08:34:30.264 A 1.72e+02 8.88e+00 2004-02-10T08:34:30.264 A B 7 2.97 1.7 1.7 -45.0 1.17e+02 6.39e+00 2004-02-10T08:34:30.264 A 9.88e+01 5.08e+00 2004-02-10T08:34:30.264 A 1.08e+02 5.16e+00 2005-09-16T09:56:12.848 A B 1 2.54 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 6.13e+01 6.20e+00 2004-02-10T08:34:30.264 A Q -2 4.98 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 2.99e+02 4.82e+01 2004-09-20T12:36:43.680 B Q -2 8.04 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 0.00e+00 0.00e+00 undetected Y Q 0 0.00 0.0 0.0 0.0 - SSTc2d J032857.4+311416 052.2390096 7.82e-07 +31.2377395 7.82e-07 A A null -5.00 1.60e+00 5.53e-02 5.97e+01 5 YSOc_red -999.00 -999.00 -999.00 -999.0000 -999.00 0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 2.85e+01 1.64e+00 2004-02-10T08:34:30.264 A 2.17e+01 1.72e+00 2004-02-10T08:34:30.264 A 3.16e+01 1.54e+00 2005-09-16T09:56:12.848 A B 1 3.09 1.5 1.5 -45.0 1.17e+02 5.94e+00 2004-02-10T08:34:30.264 A 9.69e+01 7.42e+00 2004-02-10T08:34:30.264 A 1.04e+02 5.82e+00 2004-02-10T08:34:30.264 A B 7 2.97 1.7 1.7 -45.0 2.54e+02 1.22e+01 2004-02-10T08:34:30.264 A 2.71e+02 1.32e+01 2004-02-10T08:34:30.264 A 2.62e+02 1.24e+01 2005-09-16T09:56:12.848 A A 7 6.43 2.9 2.8 -48.5 3.84e+02 2.00e+01 2004-02-10T08:34:30.264 A 3.30e+02 2.09e+01 2004-02-10T08:34:30.264 A 3.75e+02 2.28e+01 2004-02-10T08:34:30.264 A A 7 4.98 1.8 1.8 -45.0 4.41e+03 4.21e+02 2004-09-19T22:12:44.497 A 4.27e+03 4.05e+02 2004-09-20T03:00:42.863 A 4.30e+03 4.04e+02 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 3.00e+04 2.78e+03 2004-09-19T08:53:04.473 S Q 0 0.00 0.0 0.0 0.0 - SSTc2d J032858.4+312218 052.2434444 8.68e-07 +31.3715356 8.68e-07 A A 03285842+3122175 -5.00 8.30e-01 5.08e-02 3.47e+01 6 YSOc_red -999.00 -999.00 -999.00 -999.0000 -999.00 0 6.85e-02 -9.99e+02 2000-10-01T20:39:42 U 1.23e+00 9.82e-02 2000-10-01T20:39:42 A 1.21e+01 2.91e-01 2000-10-01T20:39:42 A 3.95e+01 1.97e+00 2004-02-10T08:34:30.264 A 4.57e+01 2.98e+00 2004-02-10T08:34:30.264 A 4.03e+01 2.20e+00 2004-02-10T08:34:30.264 A A 1 3.09 1.5 1.5 -45.0 9.78e+01 5.10e+00 2004-02-10T08:34:30.264 A 1.44e+02 9.37e+00 2004-02-10T08:34:30.264 A 8.52e+01 7.70e+00 2004-02-10T08:34:30.264 A C 1 2.97 1.7 1.7 -45.0 1.63e+02 7.75e+00 2004-02-10T08:34:30.264 A 1.98e+02 1.36e+01 2004-02-10T08:34:30.264 A 1.75e+02 8.89e+00 2004-02-10T08:34:30.264 A B 2 6.80 2.5 2.4 -79.2 2.10e+02 1.05e+01 2004-02-10T08:34:30.264 A 1.60e+02 1.74e+01 2004-02-10T08:34:30.264 A 2.56e+02 1.61e+01 2004-02-10T08:34:30.264 A B 7 4.98 1.8 1.8 -45.0 9.53e+02 8.87e+01 2004-09-19T22:12:44.497 A 8.62e+02 8.00e+01 2004-09-20T03:00:42.863 A 8.61e+02 8.08e+01 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 9.27e+02 1.00e+02 2004-09-19T08:53:04.473 A Q 0 0.00 0.0 0.0 0.0 - SSTc2d J032906.1+303039 052.2752091 7.83e-07 +30.5108863 7.83e-07 A A 03290604+3030387 -1.65 8.70e-01 5.08e-02 1.59e+01 6 YSOc_red -999.00 -999.00 -999.00 -999.0000 -999.00 0 2.97e-01 4.59e-02 1999-11-26T20:23:60 C 5.00e-01 -9.99e+02 1999-11-26T20:23:60 U 6.05e-01 6.30e-02 1999-11-26T20:23:60 B 6.09e-01 3.33e-02 2004-09-07T10:34:29.464 A 6.29e-01 3.48e-02 2004-09-07T10:34:29.464 A 6.22e-01 3.18e-02 2004-09-07T10:34:29.464 A A 1 3.09 1.5 1.5 -45.0 9.81e-01 5.17e-02 2004-09-07T10:34:29.464 A 1.03e+00 5.45e-02 2004-09-07T10:34:29.464 A 1.01e+00 5.03e-02 2004-09-07T10:34:29.464 A A 1 2.97 1.7 1.7 -45.0 1.92e+00 1.18e-01 2004-09-07T10:34:29.464 A 1.74e+00 1.06e-01 2004-09-07T10:34:29.464 A 1.84e+00 1.02e-01 2004-09-07T10:34:29.464 A A 1 3.92 1.8 1.8 -45.0 4.09e+00 2.08e-01 2004-09-07T10:34:29.464 A 4.03e+00 2.03e-01 2004-09-07T10:34:29.464 A 4.03e+00 1.95e-01 2004-09-07T10:34:29.464 A A 1 4.98 1.8 1.8 -45.0 2.10e+01 1.96e+00 2004-09-19T22:12:44.497 A 2.10e+01 1.97e+00 2004-09-20T03:00:42.863 A 2.11e+01 1.96e+00 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 1.13e+02 1.84e+01 2004-09-19T08:53:04.473 P Q 0 0.00 0.0 0.0 0.0 - SSTc2d J032910.7+311821 052.2944829 2.23e-06 +31.3057251 2.23e-06 A A null -5.00 1.95e+00 6.48e-02 4.17e+01 5 YSOc_red -999.00 -999.00 -999.00 -999.0000 -999.00 0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U 2.01e+00 2.56e-01 2004-02-10T08:34:30.264 A 1.84e+00 1.54e-01 2004-02-10T08:34:30.264 A Q 7 3.09 1.5 1.5 -45.0 -9.99e+02 -9.99e+02 null U 7.28e+00 7.46e-01 2004-02-10T08:34:30.264 A 6.69e+00 6.50e-01 2004-02-10T08:34:30.264 A Q 7 2.97 1.7 1.7 -45.0 1.11e+01 9.00e-01 2004-02-10T08:34:30.264 A 1.12e+01 6.65e-01 2004-02-10T08:34:30.264 A 1.03e+01 8.28e-01 2004-02-10T08:34:30.264 A A 2 8.97 3.6 2.2 -84.1 1.24e+01 1.03e+00 2004-02-10T08:34:30.264 A 1.21e+01 8.79e-01 2004-02-10T08:34:30.264 A 1.09e+01 7.79e-01 2004-02-10T08:34:30.264 A A 2 7.61 3.2 2.1 -57.1 7.61e+02 7.07e+01 2004-09-19T22:12:44.497 A 7.60e+02 7.20e+01 2004-09-20T03:00:42.863 A 7.71e+02 7.22e+01 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 2.06e+04 1.93e+03 2004-09-19T08:53:04.473 P Q 2 0.00 0.0 0.0 0.0 - SSTc2d J032911.0+311826 052.2957986 4.11e-09 +31.3072254 4.11e-09 A C null -5.00 6.90e-01 1.62e-01 8.51e+00 5 YSOc_red -999.00 -999.00 -999.00 -999.0000 -999.00 0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U 7.13e-01 1.26e-01 2004-02-10T08:34:30.264 B 8.29e-01 1.11e-01 2004-02-10T08:34:30.264 A Q 7 3.09 1.5 1.5 -45.0 1.91e+00 4.37e-01 2004-02-10T08:34:30.264 C 1.30e+00 2.21e-01 2004-02-10T08:34:30.264 B 1.28e+00 2.55e-01 2004-02-10T08:34:30.264 B A -2 2.97 1.7 1.7 -45.0 1.78e+00 2.02e-01 2004-02-10T08:34:30.264 A -9.99e+02 -9.99e+02 null U 1.47e+00 2.14e-01 2004-02-10T08:34:30.264 B Q -2 3.92 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 1.08e+00 2.40e-01 2004-02-10T08:34:30.264 C Q -2 4.98 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 3.67e+01 1.09e+01 2004-09-20T12:36:43.680 C Q -2 8.04 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 0.00e+00 0.00e+00 undetected Y Q 0 0.00 0.0 0.0 0.0 - SSTc2d J032912.1+311305 052.3002587 3.40e-09 +31.2181768 3.40e-09 A A null -5.00 9.80e-01 8.84e-02 8.10e+01 5 YSOc_red -999.00 -999.00 -999.00 -999.0000 -999.00 0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 2.96e-02 6.92e-03 2005-09-16T09:56:12.848 C Q -2 1.77 1.5 1.5 -45.0 1.09e+00 8.51e-02 2004-02-10T08:34:30.264 A 1.01e+00 7.36e-02 2004-02-10T08:34:30.264 A 9.82e-01 6.63e-02 2004-02-10T08:34:30.264 A A 7 2.97 1.7 1.7 -45.0 6.29e-01 6.17e-02 2004-02-10T08:34:30.264 A 6.52e-01 6.26e-02 2004-02-10T08:34:30.264 A 6.27e-01 4.20e-02 2005-09-16T09:56:12.848 A A 1 2.54 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 3.04e-01 8.71e-02 2004-02-10T08:34:30.264 C Q -2 4.98 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 1.81e+01 2.35e+00 2004-09-20T12:36:43.680 A Q -2 8.04 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 1.22e+04 1.15e+03 2004-09-19T08:53:04.473 W Q 0 0.00 0.0 0.0 0.0 - SSTc2d J032912.1+311302 052.3002650 1.17e-06 +31.2171435 1.17e-06 A A null -5.00 5.50e-01 5.62e-02 4.11e+02 5 YSOc_red -999.00 -999.00 -999.00 -999.0000 -999.00 0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 8.11e-01 5.24e-02 2004-02-10T08:34:30.264 K 7.03e-01 5.09e-02 2004-02-10T08:34:30.264 A 6.83e-01 3.55e-02 2005-09-16T09:56:12.848 A A 1 0.90 0.6 1.9 -64.5 1.29e+01 6.71e-01 2004-02-10T08:34:30.264 A 1.23e+01 6.92e-01 2004-02-10T08:34:30.264 A 1.27e+01 6.81e-01 2004-02-10T08:34:30.264 A A 1 2.97 1.7 1.7 -45.0 5.10e+00 3.03e-01 2004-02-10T08:34:30.264 A 4.83e+00 2.85e-01 2004-02-10T08:34:30.264 A 4.86e+00 2.47e-01 2005-09-16T09:56:12.848 A A 1 2.54 1.8 1.8 -45.0 3.29e+00 1.90e-01 2004-02-10T08:34:30.264 A 3.09e+00 1.82e-01 2004-02-10T08:34:30.264 A 3.43e+00 2.09e-01 2004-02-10T08:34:30.264 A A 1 4.98 1.8 1.8 -45.0 3.71e+01 3.57e+00 2004-09-19T22:12:44.497 A 3.80e+01 3.67e+00 2004-09-20T03:00:42.863 A 3.77e+01 3.57e+00 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 0.00e+00 0.00e+00 undetected Y Q 0 0.00 0.0 0.0 0.0 - SSTc2d J032913.0+311814 052.3040246 6.82e-07 +31.3039761 6.82e-07 A A 03291294+3118146 -5.00 1.05e+00 5.09e-02 2.59e+02 6 YSOc -999.00 -999.00 -999.00 -999.0000 -999.00 0 5.80e-02 -9.99e+02 1999-11-26T20:23:08 U 7.96e-02 -9.99e+02 1999-11-26T20:23:08 U 1.51e+00 6.94e-02 1999-11-26T20:23:08 A 5.68e+01 2.97e+00 2004-02-10T08:34:30.264 A 5.64e+01 2.97e+00 2004-02-10T08:34:30.264 A 4.56e+01 2.85e+00 2004-02-10T08:34:30.264 A A 1 3.09 1.5 1.5 -45.0 1.82e+02 1.16e+01 2004-02-10T08:34:30.264 K 1.52e+02 1.02e+01 2004-02-10T08:34:30.264 A 1.80e+02 9.14e+00 2004-02-10T08:34:30.264 A A 1 2.27 1.7 1.7 -45.0 2.89e+02 1.43e+01 2004-02-10T08:34:30.264 A 2.30e+02 1.50e+01 2004-02-10T08:34:30.264 A 2.74e+02 1.40e+01 2004-02-10T08:34:30.264 A B 2 10.54 3.1 3.0 -81.9 3.31e+02 1.57e+01 2004-02-10T08:34:30.264 A 3.14e+02 1.88e+01 2004-02-10T08:34:30.264 A 3.20e+02 1.78e+01 2004-02-10T08:34:30.264 A A 2 37.23 6.2 5.3 -67.8 7.34e+02 6.80e+01 2004-09-19T22:12:44.497 A 7.23e+02 6.73e+01 2004-09-20T03:00:42.863 A 7.35e+02 6.81e+01 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 4.29e+03 4.06e+02 2004-09-19T08:53:04.473 P Q 0 0.00 0.0 0.0 0.0 - SSTc2d J032917.2+312746 052.3215278 1.69e-06 +31.4629087 1.69e-06 A A null -5.00 1.75e+00 6.25e-02 6.69e+01 5 YSOc_red -999.00 -999.00 -999.00 -999.0000 -999.00 0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 4.37e-01 3.27e-02 2004-02-10T08:34:30.264 A 4.38e-01 3.51e-02 2004-02-10T08:34:30.264 A 4.21e-01 3.06e-02 2004-02-10T08:34:30.264 A A 7 3.09 1.5 1.5 -45.0 1.23e+00 9.17e-02 2004-02-10T08:34:30.264 A 1.23e+00 7.87e-02 2004-02-10T08:34:30.264 A 1.22e+00 9.87e-02 2004-02-10T08:34:30.264 A A 1 2.97 1.7 1.7 -45.0 1.26e+00 9.59e-02 2004-02-10T08:34:30.264 A 1.43e+00 1.01e-01 2004-02-10T08:34:30.264 K 1.24e+00 8.31e-02 2004-02-10T08:34:30.264 A A 2 3.88 2.6 1.9 -64.9 8.11e-01 8.71e-02 2004-02-10T08:34:30.264 A 7.91e-01 8.52e-02 2004-02-10T08:34:30.264 A 8.18e-01 9.83e-02 2004-02-10T08:34:30.264 A A 1 4.98 1.8 1.8 -45.0 1.11e+02 1.03e+01 2004-09-19T22:12:44.497 A 1.08e+02 1.00e+01 2004-09-20T03:00:42.863 A 1.11e+02 1.03e+01 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 1.95e+03 2.18e+02 2004-09-19T08:53:04.473 P Q 0 0.00 0.0 0.0 0.0 - SSTc2d J032917.5+312748 052.3229300 1.08e-08 +31.4634218 1.08e-08 A A null -1.58 1.01e+00 1.32e-01 3.29e+00 5 YSOc_red -999.00 -999.00 -999.00 -999.0000 -999.00 0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 2.88e-01 2.07e-02 2004-02-10T08:34:30.264 K 1.85e-01 2.38e-02 2004-02-10T08:34:30.264 A 1.25e-01 1.96e-02 2004-02-10T08:34:30.264 B B 7 1.77 1.5 1.5 -45.0 4.73e-01 3.89e-02 2004-02-10T08:34:30.264 K 7.29e-01 5.24e-02 2004-02-10T08:34:30.264 K 2.54e-01 4.81e-02 2004-02-10T08:34:30.264 B B -2 2.27 1.7 1.7 -45.0 -9.99e+02 -9.99e+02 null U 2.45e-01 4.85e-02 2004-02-10T08:34:30.264 B 2.49e-01 3.72e-02 2004-02-10T08:34:30.264 B Q 7 3.92 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 3.01e-01 9.14e-02 2004-02-10T08:34:30.264 C Q 7 4.98 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 6.44e+00 1.33e+00 2004-09-20T12:36:43.680 C Q -2 8.04 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 0.00e+00 0.00e+00 undetected Y Q 0 0.00 0.0 0.0 0.0 - SSTc2d J032920.4+311834 052.3351508 5.94e-07 +31.3095014 5.94e-07 A A 03292042+3118342 -5.00 -1.40e-01 5.04e-02 3.77e+00 6 YSOc_star+dust(IR1) 23.82 2.37 7.87 0.3560 2.47 3 2.76e+00 1.15e-01 1999-11-26T20:23:08 A 1.61e+01 4.89e-01 1999-11-26T20:23:08 A 4.31e+01 9.14e-01 1999-11-26T20:23:08 A 1.22e+02 6.33e+00 2004-02-10T08:34:30.264 A 1.00e+02 5.82e+00 2004-02-10T08:34:30.264 A 8.84e+01 6.16e+00 2004-02-10T08:34:30.264 A B 7 3.09 1.5 1.5 -45.0 1.25e+02 6.26e+00 2004-02-10T08:34:30.264 A 1.10e+02 6.67e+00 2004-02-10T08:34:30.264 A 1.18e+02 6.21e+00 2004-02-10T08:34:30.264 A A 1 2.97 1.7 1.7 -45.0 1.26e+02 6.07e+00 2004-02-10T08:34:30.264 A 1.15e+02 5.72e+00 2004-02-10T08:34:30.264 A 1.19e+02 5.91e+00 2004-02-10T08:34:30.264 A A 1 3.92 1.8 1.8 -45.0 1.54e+02 7.34e+00 2004-02-10T08:34:30.264 A 1.54e+02 7.90e+00 2004-02-10T08:34:30.264 A 1.57e+02 7.69e+00 2004-02-10T08:34:30.264 A A 1 4.98 1.8 1.8 -45.0 4.31e+02 4.03e+01 2004-09-19T22:12:44.497 A 4.61e+02 4.28e+01 2004-09-20T03:00:42.863 A 4.41e+02 4.11e+01 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 2.14e+02 2.68e+01 2004-09-19T08:53:04.473 S Q 0 0.00 0.0 0.0 0.0 - SSTc2d J032923.5+313330 052.3478412 1.25e-06 +31.5582063 1.25e-06 A A null -5.00 1.51e+00 6.16e-02 8.59e+01 5 YSOc_red -999.00 -999.00 -999.00 -999.0000 -999.00 0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 7.73e-01 6.78e-02 2004-02-10T08:34:30.264 A 7.59e-01 7.00e-02 2004-02-10T08:34:30.264 A Q 7 3.09 1.5 1.5 -45.0 1.46e+00 1.76e-01 2004-02-10T08:34:30.264 A 2.26e+00 1.30e-01 2004-02-10T08:34:30.264 K 2.26e+00 1.30e-01 2004-02-10T08:34:30.264 K B 7 2.27 1.7 1.7 -45.0 2.62e+00 2.57e-01 2004-02-10T08:34:30.264 A 2.62e+00 2.11e-01 2004-02-10T08:34:30.264 A 2.47e+00 1.97e-01 2004-02-10T08:34:30.264 A A 1 3.92 1.8 1.8 -45.0 3.11e+00 1.66e-01 2004-02-10T08:34:30.264 A 2.94e+00 1.59e-01 2004-02-10T08:34:30.264 A 3.03e+00 1.54e-01 2004-02-10T08:34:30.264 A A 1 4.98 1.8 1.8 -45.0 1.92e+02 1.78e+01 2004-09-19T22:12:44.497 A 1.89e+02 1.76e+01 2004-09-20T03:00:42.863 A 1.91e+02 1.77e+01 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 1.15e+03 1.10e+02 2004-09-19T08:53:04.473 P Q 0 0.00 0.0 0.0 0.0 - SSTc2d J032929.3+311835 052.3719770 5.52e-07 +31.3096320 5.52e-07 A A 03292925+3118347 -5.00 -6.60e-01 4.93e-02 1.60e+02 6 YSOc_star+dust(IR4) 6.82 1.14 10.21 0.1070 0.61 6 1.47e+01 3.25e-01 1999-11-26T20:23:08 A 2.89e+01 7.45e-01 1999-11-26T20:23:08 A 2.75e+01 5.56e-01 1999-11-26T20:23:08 A 1.49e+01 7.79e-01 2004-02-10T08:34:30.264 A 1.57e+01 8.16e-01 2004-02-10T08:34:30.264 A 1.36e+01 8.30e-01 2004-02-10T08:34:30.264 A A 1 3.09 1.5 1.5 -45.0 1.08e+01 5.21e-01 2004-02-10T08:34:30.264 A 1.04e+01 5.12e-01 2004-02-10T08:34:30.264 A 1.02e+01 5.39e-01 2004-02-10T08:34:30.264 A A 1 2.97 1.7 1.7 -45.0 7.68e+00 3.85e-01 2004-02-10T08:34:30.264 A 7.54e+00 3.79e-01 2004-02-10T08:34:30.264 A 7.62e+00 3.72e-01 2004-02-10T08:34:30.264 A A 1 3.92 1.8 1.8 -45.0 6.84e+00 3.35e-01 2004-02-10T08:34:30.264 A 6.95e+00 3.42e-01 2004-02-10T08:34:30.264 A 6.94e+00 3.31e-01 2004-02-10T08:34:30.264 A A 1 4.98 1.8 1.8 -45.0 9.41e+01 8.71e+00 2004-09-19T22:12:44.497 A 9.36e+01 8.66e+00 2004-09-20T03:00:42.863 A 9.52e+01 8.81e+00 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 2.91e+02 2.97e+01 2004-09-19T08:53:04.473 A Q 0 0.00 0.0 0.0 0.0 - SSTc2d J032930.4+311903 052.3766823 5.27e-07 +31.3175860 5.27e-07 A A 03293038+3119034 -5.00 -8.60e-01 4.89e-02 9.36e+00 6 YSOc_star+dust(IR2) 6.42 1.42 10.21 0.1640 0.34 4 2.28e+01 4.63e-01 1999-11-26T20:23:08 A 2.84e+01 8.38e-01 1999-11-26T20:23:08 A 2.59e+01 5.25e-01 1999-11-26T20:23:08 A 1.71e+01 8.56e-01 2004-02-10T08:34:30.264 A 1.94e+01 9.80e-01 2004-02-10T08:34:30.264 A 1.61e+01 8.98e-01 2004-02-10T08:34:30.264 A A 1 3.09 1.5 1.5 -45.0 1.62e+01 8.14e-01 2004-02-10T08:34:30.264 A 1.70e+01 8.76e-01 2004-02-10T08:34:30.264 A 1.62e+01 8.46e-01 2004-02-10T08:34:30.264 A A 1 2.97 1.7 1.7 -45.0 1.58e+01 7.71e-01 2004-02-10T08:34:30.264 A 1.64e+01 7.94e-01 2004-02-10T08:34:30.264 A 1.60e+01 7.72e-01 2004-02-10T08:34:30.264 A A 1 3.92 1.8 1.8 -45.0 1.92e+01 9.22e-01 2004-02-10T08:34:30.264 A 1.92e+01 9.45e-01 2004-02-10T08:34:30.264 A 1.92e+01 9.17e-01 2004-02-10T08:34:30.264 A A 1 4.98 1.8 1.8 -45.0 2.79e+01 2.61e+00 2004-09-19T22:12:44.497 A 2.76e+01 2.59e+00 2004-09-20T03:00:42.863 A 2.79e+01 2.60e+00 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 1.24e+02 2.88e+01 2004-09-19T08:53:04.473 P Q 0 0.00 0.0 0.0 0.0 - SSTc2d J032946.0+310439 052.4416698 4.27e-07 +31.0775010 4.27e-07 A A null -5.00 -9.80e-01 5.27e-02 1.28e+01 5 YSOc_star+dust(IR4) 29.58 6.37 7.83 0.3700 0.02 3 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 3.20e+01 1.55e+00 2004-02-10T08:34:30.264 A 3.37e+01 1.66e+00 2004-02-10T08:34:30.264 A 3.31e+01 1.56e+00 2005-09-16T09:56:12.848 A A 1 1.77 1.5 1.5 -45.0 -9.99e+02 -9.99e+02 null N 3.01e+01 1.66e+00 2004-02-10T08:34:30.264 A 2.85e+01 1.36e+00 2005-09-16T09:56:12.848 A Q 3 2.27 1.7 1.7 -45.0 2.51e+01 1.20e+00 2004-02-10T08:34:30.264 A 2.57e+01 1.27e+00 2004-02-10T08:34:30.264 A 2.47e+01 1.16e+00 2005-09-16T09:56:12.848 A A 3 2.54 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null N 2.64e+01 1.36e+00 2004-02-10T08:34:30.264 A 2.50e+01 1.17e+00 2005-09-16T09:56:12.848 A Q 1 2.54 1.8 1.8 -45.0 4.16e+01 3.86e+00 2004-09-19T22:12:44.497 A 4.18e+01 3.87e+00 2004-09-20T03:00:42.863 A 4.15e+01 3.85e+00 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 9.55e+01 1.30e+01 2004-09-19T08:53:04.473 P Q 0 0.00 0.0 0.0 0.0 - SSTc2d J032954.0+312053 052.4751774 5.32e-07 +31.3480271 5.32e-07 A A 03295403+3120529 -5.00 -5.20e-01 4.95e-02 2.28e+01 6 YSOc_star+dust(IR2) 9.26 1.44 9.44 0.1690 0.95 4 2.66e+01 5.65e-01 1999-11-26T20:26:44 A 3.59e+01 9.58e-01 1999-11-26T20:26:44 A 3.86e+01 8.90e-01 1999-11-26T20:26:44 A 3.11e+01 1.56e+00 2004-02-10T08:34:30.264 A 3.37e+01 1.77e+00 2004-02-10T08:34:30.264 A 2.78e+01 1.73e+00 2004-02-10T08:34:30.264 A A 1 3.09 1.5 1.5 -45.0 2.73e+01 1.34e+00 2004-02-10T08:34:30.264 A 3.12e+01 1.58e+00 2004-02-10T08:34:30.264 A 2.87e+01 1.48e+00 2004-02-10T08:34:30.264 A A 1 2.97 1.7 1.7 -45.0 2.56e+01 1.25e+00 2004-02-10T08:34:30.264 A 2.83e+01 1.40e+00 2004-02-10T08:34:30.264 A 2.69e+01 1.31e+00 2004-02-10T08:34:30.264 A A 1 3.92 1.8 1.8 -45.0 3.30e+01 1.58e+00 2004-02-10T08:34:30.264 A 3.43e+01 1.71e+00 2004-02-10T08:34:30.264 A 3.36e+01 1.64e+00 2004-02-10T08:34:30.264 A A 1 4.98 1.8 1.8 -45.0 1.14e+02 1.05e+01 2004-09-19T22:12:44.497 A 1.12e+02 1.04e+01 2004-09-20T03:00:42.863 A 1.14e+02 1.05e+01 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 1.99e+02 2.01e+01 2004-09-19T08:53:04.473 A Q 0 0.00 0.0 0.0 0.0 - SSTc2d J033015.1+302349 052.5630745 5.27e-07 +30.3970499 5.27e-07 A A 03301515+3023493 -5.00 1.70e+00 4.97e-02 2.68e+01 6 YSOc_red -999.00 -999.00 -999.00 -999.0000 -999.00 0 8.14e-02 -9.99e+02 1998-01-22T14:26:27 U 3.89e-01 -9.99e+02 1998-01-22T14:26:27 U 1.32e+00 8.14e-02 1998-01-22T14:26:27 A 7.17e+00 3.83e-01 2004-09-07T10:34:29.464 A 7.33e+00 4.15e-01 2004-09-07T10:34:29.464 A 7.31e+00 3.87e-01 2004-09-07T10:34:29.464 A A 1 3.09 1.5 1.5 -45.0 1.99e+01 1.11e+00 2004-09-07T10:34:29.464 A 1.87e+01 1.01e+00 2004-09-07T10:34:29.464 A 1.88e+01 9.70e-01 2004-09-07T10:34:29.464 A A 1 2.97 1.7 1.7 -45.0 2.17e+01 1.06e+00 2004-09-07T10:34:29.464 A 2.21e+01 1.07e+00 2004-09-07T10:34:29.464 A 2.20e+01 1.05e+00 2004-09-07T10:34:29.464 A A 1 3.92 1.8 1.8 -45.0 4.40e+01 2.11e+00 2004-09-07T10:34:29.464 A 4.58e+01 2.17e+00 2004-09-07T10:34:29.464 A 4.49e+01 2.12e+00 2004-09-07T10:34:29.464 A A 1 4.98 1.8 1.8 -45.0 1.88e+03 1.76e+02 2004-09-19T22:12:44.497 A 1.83e+03 1.70e+02 2004-09-20T03:00:42.863 A 1.76e+03 1.64e+02 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 5.63e+03 5.26e+02 2004-09-19T08:53:04.473 A Q 0 0.00 0.0 0.0 0.0 - SSTc2d J033027.1+302830 052.6130942 4.51e-07 +30.4749334 4.51e-07 A A 03302715+3028297 -5.00 -0.00e+00 4.78e-02 2.02e+02 6 YSOc_red -999.00 -999.00 -999.00 -999.0000 -999.00 0 1.81e+00 7.68e-02 1998-01-22T14:26:36 A 5.59e+00 1.85e-01 1998-01-22T14:26:36 A 1.16e+01 2.57e-01 1998-01-22T14:26:36 A 1.44e+01 7.24e-01 2004-09-07T10:34:29.464 A 1.48e+01 7.54e-01 2004-09-07T10:34:29.464 A 1.47e+01 7.22e-01 2004-09-07T10:34:29.464 A A 1 3.09 1.5 1.5 -45.0 1.50e+01 7.39e-01 2004-09-07T10:34:29.464 A 1.51e+01 7.65e-01 2004-09-07T10:34:29.464 A 1.51e+01 7.32e-01 2004-09-07T10:34:29.464 A A 1 2.97 1.7 1.7 -45.0 1.05e+01 5.11e-01 2004-09-07T10:34:29.464 A 1.08e+01 5.24e-01 2004-09-07T10:34:29.464 A 1.07e+01 5.10e-01 2004-09-07T10:34:29.464 A A 1 3.92 1.8 1.8 -45.0 9.07e+00 4.39e-01 2004-09-07T10:34:29.464 A 9.66e+00 4.68e-01 2004-09-07T10:34:29.464 A 9.37e+00 4.44e-01 2004-09-07T10:34:29.464 A A 1 4.98 1.8 1.8 -45.0 4.27e+02 3.94e+01 2004-09-19T22:12:44.497 A 4.21e+02 3.93e+01 2004-09-20T03:00:42.863 A 4.26e+02 3.94e+01 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 9.29e+02 8.76e+01 2004-09-19T08:53:04.473 P Q 0 0.00 0.0 0.0 0.0 - SSTc2d J033032.7+302627 052.6361574 1.81e-06 +30.4407147 1.81e-06 A A null -5.00 2.08e+00 6.58e-02 3.35e+01 5 YSOc_red -999.00 -999.00 -999.00 -999.0000 -999.00 0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 6.41e-02 8.59e-03 2004-09-07T10:34:29.464 A 5.66e-02 8.09e-03 2004-09-07T10:34:29.464 B 5.86e-02 6.58e-03 2004-09-07T10:34:29.464 A A 2 5.51 2.7 1.8 -79.3 1.88e-01 1.58e-02 2004-09-07T10:34:29.464 A 2.09e-01 1.60e-02 2004-09-07T10:34:29.464 A 1.96e-01 1.23e-02 2004-09-07T10:34:29.464 A A 1 2.97 1.7 1.7 -45.0 -9.99e+02 -9.99e+02 null U 1.84e-01 4.90e-02 2004-09-07T10:34:29.464 C 1.89e-01 3.58e-02 2004-09-07T10:34:29.464 B Q 7 3.92 1.8 1.8 -45.0 1.54e-01 3.86e-02 2004-09-07T10:34:29.464 C 1.64e-01 4.82e-02 2004-09-07T10:34:29.464 C 1.56e-01 3.14e-02 2004-09-07T10:34:29.464 C A 1 4.98 1.8 1.8 -45.0 3.12e+01 2.89e+00 2004-09-19T22:12:44.497 A 2.98e+01 2.77e+00 2004-09-20T03:00:42.863 A 3.05e+01 2.82e+00 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 3.10e+02 2.99e+01 2004-09-19T08:53:04.473 P Q 0 0.00 0.0 0.0 0.0 - SSTc2d J033035.5+311559 052.6478060 7.35e-07 +31.2662672 7.35e-07 A A 03303548+3115586 -2.69 -1.90e-01 4.90e-02 8.26e+01 6 YSOc_star+dust(IR3) 9.29 1.26 12.52 0.1250 1.65 5 1.49e+00 7.00e-02 1998-01-22T14:27:27 A 2.21e+00 1.08e-01 1998-01-22T14:27:27 A 2.24e+00 8.66e-02 1998-01-22T14:27:27 A 1.41e+00 6.94e-02 2004-09-07T10:34:29.464 A 1.42e+00 7.16e-02 2004-09-07T10:34:29.464 A 1.42e+00 6.85e-02 2004-09-07T10:34:29.464 A A 1 3.09 1.5 1.5 -45.0 1.19e+00 6.23e-02 2004-09-07T10:34:29.464 A 1.25e+00 6.57e-02 2004-09-07T10:34:29.464 A 1.22e+00 6.08e-02 2004-09-07T10:34:29.464 A A 1 2.97 1.7 1.7 -45.0 1.15e+00 8.12e-02 2004-09-07T10:34:29.464 A -9.99e+02 -9.99e+02 null U 1.18e+00 6.94e-02 2004-09-07T10:34:29.464 A Q 1 3.92 1.8 1.8 -45.0 1.47e+00 8.86e-02 2004-09-07T10:34:29.464 A 1.52e+00 9.19e-02 2004-09-07T10:34:29.464 A 1.49e+00 7.96e-02 2004-09-07T10:34:29.464 A A 1 4.98 1.8 1.8 -45.0 1.66e+01 1.56e+00 2004-09-19T22:12:44.497 A 1.69e+01 1.58e+00 2004-09-20T03:00:42.863 A 1.67e+01 1.56e+00 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 1.34e+02 1.93e+01 2004-09-19T08:53:04.473 P Q 0 0.00 0.0 0.0 0.0 - SSTc2d J033035.9+303024 052.6496785 8.16e-07 +30.5067799 8.16e-07 A A 03303593+3030244 -5.00 -1.06e+00 4.97e-02 3.50e+00 6 YSOc_star+dust(IR1) 6.75 2.36 6.82 0.3540 0.05 3 4.07e+02 6.74e+00 1998-01-22T14:26:36 A 6.16e+02 1.25e+01 1998-01-22T14:26:36 A 6.10e+02 1.18e+01 1998-01-22T14:26:36 A 5.71e+02 3.21e+01 2004-09-07T10:34:29.464 A 5.73e+02 3.24e+01 2004-09-07T10:34:29.464 A 5.87e+02 3.54e+01 2004-09-07T10:34:29.464 K A 7 1.77 1.5 1.5 -45.0 4.82e+02 2.89e+01 2004-09-07T10:34:29.464 A 4.70e+02 2.84e+01 2004-09-07T10:34:29.464 A 4.80e+02 2.79e+01 2004-09-07T10:34:29.464 A A 7 2.97 1.7 1.7 -45.0 4.57e+02 2.35e+01 2004-09-07T10:34:29.464 A 4.62e+02 2.40e+01 2004-09-07T10:34:29.464 A 4.64e+02 2.32e+01 2004-09-07T10:34:29.464 A A 2 16.26 4.1 3.5 -80.2 4.65e+02 2.46e+01 2004-09-07T10:34:29.464 A 4.69e+02 2.44e+01 2004-09-07T10:34:29.464 A 4.70e+02 2.38e+01 2004-09-07T10:34:29.464 A A 1 4.98 1.8 1.8 -45.0 5.52e+02 5.12e+01 2004-09-19T22:12:44.497 A 5.76e+02 5.32e+01 2004-09-20T03:00:42.863 A 5.63e+02 5.21e+01 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 1.81e+02 1.82e+01 2004-09-19T08:53:04.473 A Q 0 0.00 0.0 0.0 0.0 - SSTc2d J033037.0+303128 052.6540241 5.26e-07 +30.5243734 5.26e-07 A A 03303697+3031276 -5.00 -5.40e-01 4.81e-02 4.92e+00 6 YSOc_star+dust(IR2) 17.51 1.40 7.10 0.1600 1.07 4 2.99e+01 4.96e-01 1998-01-22T14:26:36 A 8.95e+01 1.73e+00 1998-01-22T14:26:36 A 1.45e+02 2.27e+00 1998-01-22T14:26:36 A 1.43e+02 7.45e+00 2004-09-07T10:34:29.464 A 1.43e+02 7.55e+00 2004-09-07T10:34:29.464 A 1.44e+02 7.30e+00 2004-09-07T10:34:29.464 A A 1 3.09 1.5 1.5 -45.0 1.68e+02 8.58e+00 2004-09-07T10:34:29.464 A 1.71e+02 8.64e+00 2004-09-07T10:34:29.464 A 1.69e+02 8.38e+00 2004-09-07T10:34:29.464 A A 1 2.97 1.7 1.7 -45.0 2.08e+02 1.02e+01 2004-09-07T10:34:29.464 A 2.04e+02 9.86e+00 2004-09-07T10:34:29.464 A 2.06e+02 9.83e+00 2004-09-07T10:34:29.464 A A 2 9.52 3.0 2.8 -61.1 2.69e+02 1.36e+01 2004-09-07T10:34:29.464 A 2.67e+02 1.35e+01 2004-09-07T10:34:29.464 A 2.68e+02 1.31e+01 2004-09-07T10:34:29.464 A A 1 4.98 1.8 1.8 -45.0 3.11e+02 2.88e+01 2004-09-19T22:12:44.497 A 3.18e+02 2.94e+01 2004-09-20T03:00:42.863 A 3.15e+02 2.91e+01 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 1.21e+02 1.36e+01 2004-09-19T08:53:04.473 A Q 0 0.00 0.0 0.0 0.0 - SSTc2d J033038.2+303212 052.6591871 8.64e-07 +30.5366450 8.64e-07 A A 03303821+3032118 -5.00 3.50e-01 5.07e-02 2.12e+02 6 YSOc_star+dust(IR2) 9.12 1.46 12.97 0.1640 0.37 4 9.44e-01 6.00e-02 1998-01-22T14:26:36 A 1.42e+00 9.30e-02 1998-01-22T14:26:36 A 1.62e+00 7.30e-02 1998-01-22T14:26:36 A 1.03e+00 5.53e-02 2004-09-07T10:34:29.464 A 1.07e+00 5.64e-02 2004-09-07T10:34:29.464 A 1.06e+00 5.60e-02 2004-09-07T10:34:29.464 A A 1 3.09 1.5 1.5 -45.0 1.00e+00 5.40e-02 2004-09-07T10:34:29.464 A 1.01e+00 5.23e-02 2004-09-07T10:34:29.464 A 1.01e+00 5.07e-02 2004-09-07T10:34:29.464 A A 1 2.97 1.7 1.7 -45.0 8.57e-01 6.38e-02 2004-09-07T10:34:29.464 A 7.99e-01 5.87e-02 2004-09-07T10:34:29.464 A 8.31e-01 5.27e-02 2004-09-07T10:34:29.464 A A 1 3.92 1.8 1.8 -45.0 6.75e-01 5.92e-02 2004-09-07T10:34:29.464 A 7.42e-01 5.95e-02 2004-09-07T10:34:29.464 A 7.00e-01 4.75e-02 2004-09-07T10:34:29.464 A A 1 4.98 1.8 1.8 -45.0 5.55e+01 5.14e+00 2004-09-19T22:12:44.497 A 5.61e+01 5.19e+00 2004-09-20T03:00:42.863 A 5.59e+01 5.17e+00 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 2.38e+02 2.59e+01 2004-09-19T08:53:04.473 P Q 0 0.00 0.0 0.0 0.0 - SSTc2d J033044.0+303247 052.6832635 6.25e-07 +30.5463939 6.25e-07 A A 03304399+3032469 -5.00 -6.40e-01 4.92e-02 3.67e+00 6 YSOc_star+dust(IR1) 10.03 2.36 7.40 0.3540 0.16 3 1.08e+02 2.18e+00 1998-01-22T14:38:41 A 2.17e+02 4.40e+00 1998-01-22T14:38:41 A 2.57e+02 4.26e+00 1998-01-22T14:38:41 A 2.82e+02 1.45e+01 2004-09-07T10:34:29.464 A 2.88e+02 1.48e+01 2004-09-07T10:34:29.464 A 2.93e+02 1.78e+01 2004-09-07T10:34:29.464 K A 7 1.77 1.5 1.5 -45.0 2.95e+02 2.23e+01 2004-09-07T10:34:29.464 K 3.08e+02 2.25e+01 2004-09-07T10:34:29.464 K 2.92e+02 1.53e+01 2004-09-07T10:34:29.464 A A 9 2.27 1.7 1.7 -45.0 2.98e+02 1.58e+01 2004-09-07T10:34:29.464 A 2.93e+02 1.59e+01 2004-09-07T10:34:29.464 A 2.95e+02 1.60e+01 2004-09-07T10:34:29.464 A A 1 3.92 1.8 1.8 -45.0 3.24e+02 1.57e+01 2004-09-07T10:34:29.464 A 3.25e+02 1.59e+01 2004-09-07T10:34:29.464 A 3.27e+02 1.56e+01 2004-09-07T10:34:29.464 A A 7 4.98 1.8 1.8 -45.0 6.96e+02 6.46e+01 2004-09-19T22:12:44.497 A 6.72e+02 6.23e+01 2004-09-20T03:00:42.863 A 6.81e+02 6.30e+01 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 7.94e+02 7.43e+01 2004-09-19T08:53:04.473 A Q 0 0.00 0.0 0.0 0.0 - SSTc2d J033052.5+305418 052.7187848 4.92e-07 +30.9049422 4.92e-07 A A 03305252+3054177 -5.00 -4.40e-01 4.79e-02 8.08e+00 6 YSOc_star+dust(IR2) 15.20 1.39 8.94 0.1590 1.31 4 9.90e+00 2.19e-01 1998-01-22T14:38:15 A 2.36e+01 3.69e-01 1998-01-22T14:38:15 A 3.22e+01 5.64e-01 1998-01-22T14:38:15 A 3.02e+01 1.54e+00 2004-09-07T10:34:29.464 A 3.09e+01 1.57e+00 2004-09-07T10:34:29.464 A 3.07e+01 1.52e+00 2004-09-07T10:34:29.464 A A 1 3.09 1.5 1.5 -45.0 3.17e+01 1.62e+00 2004-09-07T10:34:29.464 A 3.26e+01 1.60e+00 2004-09-07T10:34:29.464 A 3.22e+01 1.56e+00 2004-09-07T10:34:29.464 A A 1 2.97 1.7 1.7 -45.0 3.26e+01 1.60e+00 2004-09-07T10:34:29.464 A 3.21e+01 1.57e+00 2004-09-07T10:34:29.464 A 3.23e+01 1.55e+00 2004-09-07T10:34:29.464 A A 1 3.92 1.8 1.8 -45.0 4.97e+01 2.42e+00 2004-09-07T10:34:29.464 A 4.95e+01 2.41e+00 2004-09-07T10:34:29.464 A 4.95e+01 2.37e+00 2004-09-07T10:34:29.464 A A 1 4.98 1.8 1.8 -45.0 1.03e+02 9.61e+00 2004-09-19T22:12:44.497 A 1.08e+02 9.98e+00 2004-09-20T03:00:42.863 A 1.04e+02 9.66e+00 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 1.56e+02 1.79e+01 2004-09-19T08:53:04.473 A Q 0 0.00 0.0 0.0 0.0 - SSTc2d J033110.7+304941 052.7945081 4.98e-07 +30.8279478 4.98e-07 A A 03311069+3049405 -5.00 -6.90e-01 4.79e-02 8.05e+00 6 YSOc_star+dust(IR2) 9.30 1.39 9.48 0.1590 1.73 4 2.59e+01 4.52e-01 1998-01-22T14:38:24 A 3.62e+01 5.34e-01 1998-01-22T14:38:24 A 3.39e+01 5.31e-01 1998-01-22T14:38:24 A 2.78e+01 1.41e+00 2004-09-07T10:34:29.464 A 2.69e+01 1.40e+00 2004-09-07T10:34:29.464 A 2.73e+01 1.36e+00 2004-09-07T10:34:29.464 A A 1 3.09 1.5 1.5 -45.0 2.67e+01 1.34e+00 2004-09-07T10:34:29.464 A 2.61e+01 1.33e+00 2004-09-07T10:34:29.464 A 2.63e+01 1.29e+00 2004-09-07T10:34:29.464 A A 1 2.97 1.7 1.7 -45.0 2.67e+01 1.33e+00 2004-09-07T10:34:29.464 A 2.65e+01 1.32e+00 2004-09-07T10:34:29.464 A 2.66e+01 1.29e+00 2004-09-07T10:34:29.464 A A 1 3.92 1.8 1.8 -45.0 3.52e+01 1.71e+00 2004-09-07T10:34:29.464 A 3.50e+01 1.70e+00 2004-09-07T10:34:29.464 A 3.50e+01 1.67e+00 2004-09-07T10:34:29.464 A A 1 4.98 1.8 1.8 -45.0 5.96e+01 5.55e+00 2004-09-19T22:12:44.497 A 6.17e+01 5.72e+00 2004-09-20T03:00:42.863 A 6.04e+01 5.61e+00 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 8.07e+01 1.21e+01 2004-09-19T08:53:04.473 B Q 0 0.00 0.0 0.0 0.0 - SSTc2d J033114.7+304955 052.8112678 4.60e-07 +30.8320551 4.60e-07 A A 03311471+3049554 -5.00 1.00e-02 4.77e-02 4.57e+00 6 YSOc_star+dust(IR1) 12.03 2.36 10.28 0.3540 1.44 3 5.46e+00 1.26e-01 1998-01-22T14:40:51 A 1.06e+01 1.75e-01 1998-01-22T14:40:51 A 1.55e+01 2.85e-01 1998-01-22T14:40:51 A 3.80e+01 1.91e+00 2004-09-07T10:34:29.464 A 3.84e+01 1.96e+00 2004-09-07T10:34:29.464 A 3.80e+01 1.87e+00 2004-09-07T10:34:29.464 A A 1 3.09 1.5 1.5 -45.0 5.03e+01 2.46e+00 2004-09-07T10:34:29.464 A 5.12e+01 2.55e+00 2004-09-07T10:34:29.464 A 5.01e+01 2.41e+00 2004-09-07T10:34:29.464 A A 1 2.97 1.7 1.7 -45.0 6.37e+01 3.21e+00 2004-09-07T10:34:29.464 A 6.35e+01 3.22e+00 2004-09-07T10:34:29.464 A 6.36e+01 3.10e+00 2004-09-07T10:34:29.464 A A 1 3.92 1.8 1.8 -45.0 7.98e+01 3.84e+00 2004-09-07T10:34:29.464 A 8.03e+01 3.98e+00 2004-09-07T10:34:29.464 A 7.97e+01 3.77e+00 2004-09-07T10:34:29.464 A A 1 4.98 1.8 1.8 -45.0 2.25e+02 2.09e+01 2004-09-19T22:12:44.497 A 2.32e+02 2.15e+01 2004-09-20T03:00:42.863 A 2.26e+02 2.09e+01 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 2.20e+02 2.29e+01 2004-09-19T08:53:04.473 A Q 0 0.00 0.0 0.0 0.0 - SSTc2d J033118.3+304940 052.8262547 6.76e-07 +30.8276480 6.76e-07 A A 03311830+3049395 -5.00 -6.80e-01 4.90e-02 2.33e+00 6 YSOc_star+dust(IR1) 11.96 2.36 7.84 0.3540 0.93 3 4.98e+01 9.17e-01 1998-01-22T14:40:51 A 1.03e+02 1.51e+00 1998-01-22T14:40:51 A 1.45e+02 2.40e+00 1998-01-22T14:40:51 A 1.49e+02 8.74e+00 2004-09-07T10:34:29.464 A 1.50e+02 8.71e+00 2004-09-07T10:34:29.464 A 1.50e+02 8.19e+00 2004-09-07T10:34:29.464 A A 1 3.09 1.5 1.5 -45.0 1.48e+02 8.18e+00 2004-09-07T10:34:29.464 A 1.51e+02 8.23e+00 2004-09-07T10:34:29.464 A 1.50e+02 7.84e+00 2004-09-07T10:34:29.464 A A 1 2.97 1.7 1.7 -45.0 1.54e+02 7.60e+00 2004-09-07T10:34:29.464 A 1.52e+02 7.68e+00 2004-09-07T10:34:29.464 A 1.54e+02 7.42e+00 2004-09-07T10:34:29.464 A A 1 3.92 1.8 1.8 -45.0 2.09e+02 1.34e+01 2004-09-07T10:34:29.464 A 2.00e+02 1.02e+01 2004-09-07T10:34:29.464 A 2.02e+02 1.01e+01 2004-09-07T10:34:29.464 A A 2 21.76 4.8 4.0 -49.9 2.90e+02 2.69e+01 2004-09-19T22:12:44.497 A 2.96e+02 2.74e+01 2004-09-20T03:00:42.863 A 2.86e+02 2.67e+01 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 1.87e+02 2.19e+01 2004-09-19T08:53:04.473 A Q 0 0.00 0.0 0.0 0.0 - SSTc2d J033121.0+304530 052.8374027 2.12e-06 +30.7583498 2.12e-06 A A null -5.00 9.80e-01 6.15e-02 1.07e+02 5 YSOc_red -999.00 -999.00 -999.00 -999.0000 -999.00 0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 1.20e-01 1.05e-02 2004-09-07T10:34:29.464 A 1.36e-01 1.41e-02 2004-09-07T10:34:29.464 A 1.26e-01 1.00e-02 2004-09-07T10:34:29.464 A A 2 7.56 2.9 2.3 -70.6 6.15e-01 4.75e-02 2004-09-07T10:34:29.464 A 6.20e-01 4.78e-02 2004-09-07T10:34:29.464 A 8.54e-01 5.12e-02 2004-09-07T10:34:29.464 K A 1 2.27 1.7 1.7 -45.0 -9.99e+02 -9.99e+02 null U 8.06e-01 6.59e-02 2004-09-07T10:34:29.464 A 8.30e-01 5.63e-02 2004-09-07T10:34:29.464 A Q 1 3.92 1.8 1.8 -45.0 6.36e-01 5.92e-02 2004-09-07T10:34:29.464 A 6.71e-01 6.03e-02 2004-09-07T10:34:29.464 A 6.19e-01 4.58e-02 2004-09-07T10:34:29.464 A A 1 4.98 1.8 1.8 -45.0 1.44e+01 1.36e+00 2004-09-19T22:12:44.497 A 1.50e+01 1.42e+00 2004-09-20T03:00:42.863 A 1.47e+01 1.37e+00 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 3.88e+03 3.66e+02 2004-09-19T08:53:04.473 P Q 0 0.00 0.0 0.0 0.0 - SSTc2d J033128.9+303053 052.8702818 5.42e-07 +30.5147925 5.42e-07 A A 03312887+3030531 -5.00 -1.29e+00 4.81e-02 2.54e+00 6 YSOc_star+dust(IR1) 5.09 2.36 8.08 0.3540 0.03 3 1.92e+02 3.71e+00 1998-01-22T14:40:34 A 2.48e+02 4.33e+00 1998-01-22T14:40:34 A 2.26e+02 3.54e+00 1998-01-22T14:40:34 A 1.99e+02 1.05e+01 2004-09-07T10:34:29.464 A 1.99e+02 1.06e+01 2004-09-07T10:34:29.464 A 1.98e+02 1.02e+01 2004-09-07T10:34:29.464 A A 1 3.09 1.5 1.5 -45.0 1.67e+02 9.28e+00 2004-09-07T10:34:29.464 A 1.73e+02 9.26e+00 2004-09-07T10:34:29.464 A 1.70e+02 8.80e+00 2004-09-07T10:34:29.464 A A 1 2.97 1.7 1.7 -45.0 1.53e+02 7.46e+00 2004-09-07T10:34:29.464 A 1.51e+02 7.28e+00 2004-09-07T10:34:29.464 A 1.52e+02 7.42e+00 2004-09-07T10:34:29.464 A A 2 6.80 2.5 2.4 -77.8 1.33e+02 6.59e+00 2004-09-07T10:34:29.464 A 1.34e+02 6.55e+00 2004-09-07T10:34:29.464 A 1.34e+02 6.43e+00 2004-09-07T10:34:29.464 A A 1 4.98 1.8 1.8 -45.0 1.28e+02 1.18e+01 2004-09-19T22:12:44.497 A 1.29e+02 1.19e+01 2004-09-20T03:00:42.863 A 1.29e+02 1.19e+01 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 2.06e+02 2.39e+01 2004-09-19T08:53:04.473 A Q 0 0.00 0.0 0.0 0.0 - SSTc2d J033229.2+310241 053.1215569 5.93e-07 +31.0446790 5.93e-07 A A 03322914+3102406 -5.00 4.00e-01 4.96e-02 1.23e+00 6 YSOc_star+dust(IR1) 19.35 2.44 10.54 0.3640 2.07 3 7.11e-01 6.28e-02 1998-01-22T15:06:03 A 2.69e+00 1.56e-01 1998-01-22T15:06:03 A 5.81e+00 2.09e-01 1998-01-22T15:06:03 A 1.08e+01 7.06e-01 2004-09-07T07:03:48.646 A 1.10e+01 7.12e-01 2004-09-07T07:03:48.646 A 1.11e+01 6.60e-01 2004-09-07T07:03:48.646 A A 1 3.09 1.5 1.5 -45.0 1.75e+01 9.51e-01 2004-09-07T07:03:48.646 A 1.73e+01 9.29e-01 2004-09-07T07:03:48.646 A 1.72e+01 9.08e-01 2004-09-07T07:03:48.646 A A 1 2.97 1.7 1.7 -45.0 2.32e+01 1.16e+00 2004-09-07T07:03:48.646 A 2.28e+01 1.13e+00 2004-09-07T07:03:48.646 A 2.33e+01 1.13e+00 2004-09-07T07:03:48.646 A A 1 3.92 1.8 1.8 -45.0 3.28e+01 1.58e+00 2004-09-07T07:03:48.646 A 3.24e+01 1.59e+00 2004-09-07T07:03:48.646 A 3.28e+01 1.58e+00 2004-09-07T07:03:48.646 A A 1 4.98 1.8 1.8 -45.0 1.79e+02 1.66e+01 2004-09-18T21:03:27.740 A 1.82e+02 1.68e+01 2004-09-19T02:56:44.617 A 1.81e+02 1.67e+01 2004-09-19T12:00:6.179 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 1.13e+03 1.09e+02 2004-09-19T08:53:04.473 P Q 0 0.00 0.0 0.0 0.0 - SSTc2d J033234.0+310056 053.1418544 5.23e-07 +31.0154877 5.23e-07 A A 03323405+3100557 -5.00 -6.60e-01 4.84e-02 2.43e+01 6 YSOc_star+dust(IR2) 12.33 1.40 7.89 0.1600 0.84 4 4.86e+01 9.40e-01 1998-01-22T15:06:03 A 9.75e+01 1.98e+00 1998-01-22T15:06:03 A 1.15e+02 1.91e+00 1998-01-22T15:06:03 A 9.28e+01 4.84e+00 2004-09-07T07:03:48.646 A 9.50e+01 5.06e+00 2004-09-07T07:03:48.646 A 9.56e+01 4.84e+00 2004-09-07T07:03:48.646 A A 1 3.09 1.5 1.5 -45.0 8.64e+01 4.45e+00 2004-09-07T07:03:48.646 A 8.77e+01 4.98e+00 2004-09-07T07:03:48.646 A 8.70e+01 4.64e+00 2004-09-07T07:03:48.646 A A 1 2.97 1.7 1.7 -45.0 7.79e+01 3.82e+00 2004-09-07T07:03:48.646 A 7.84e+01 3.77e+00 2004-09-07T07:03:48.646 A 7.89e+01 3.76e+00 2004-09-07T07:03:48.646 A A 1 3.92 1.8 1.8 -45.0 9.19e+01 4.46e+00 2004-09-07T07:03:48.646 A 9.17e+01 4.58e+00 2004-09-07T07:03:48.646 A 9.24e+01 4.68e+00 2004-09-07T07:03:48.646 A A 1 4.98 1.8 1.8 -45.0 2.92e+02 2.70e+01 2004-09-18T21:03:27.740 A 2.97e+02 2.75e+01 2004-09-19T02:56:44.617 A 2.96e+02 2.74e+01 2004-09-19T12:00:6.179 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 2.91e+02 3.15e+01 2004-09-19T08:53:04.473 A Q 0 0.00 0.0 0.0 0.0 - SSTc2d J033241.6+311044 053.1734652 1.64e-08 +31.1789357 1.64e-08 A A null -5.00 -5.00e-01 7.49e-02 2.61e+00 5 YSOc -999.00 -999.00 -999.00 -999.0000 -999.00 0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 1.06e+01 7.88e-01 2004-09-07T07:03:48.646 A 1.07e+01 6.91e-01 2004-09-07T07:03:48.646 A 1.09e+01 6.19e-01 2004-09-07T07:03:48.646 A A 1 3.09 1.5 1.5 -45.0 -9.99e+02 -9.99e+02 null U 1.34e+01 9.30e-01 2004-09-07T07:03:48.646 A 1.37e+01 8.10e-01 2004-09-07T07:03:48.646 A Q 1 2.97 1.7 1.7 -45.0 1.60e+01 1.05e+00 2004-09-07T07:03:48.646 A 1.53e+01 9.87e-01 2004-09-07T07:03:48.646 A 1.59e+01 9.08e-01 2004-09-07T07:03:48.646 A A 3 3.92 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 1.90e+01 1.14e+00 2004-09-07T07:03:48.646 A Q -2 4.98 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 2.27e+01 3.67e+00 2004-09-19T12:00:6.179 B Q -2 8.04 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 0.00e+00 0.00e+00 undetected Y Q 0 0.00 0.0 0.0 0.0 - SSTc2d J033241.7+311046 053.1737701 5.90e-07 +31.1795307 5.90e-07 A A 03324171+3110461 -5.00 -3.70e-01 4.93e-02 2.34e+01 6 YSOc_star+dust(IR2) 14.03 1.40 9.23 0.1600 1.48 4 1.04e+01 2.29e-01 1998-01-22T15:06:12 A 2.14e+01 4.74e-01 1998-01-22T15:06:12 A 2.75e+01 5.32e-01 1998-01-22T15:06:12 A 2.50e+01 1.31e+00 2004-09-07T07:03:48.646 A 2.53e+01 1.30e+00 2004-09-07T07:03:48.646 A 2.53e+01 1.27e+00 2004-09-07T07:03:48.646 A A 1 3.09 1.5 1.5 -45.0 2.90e+01 2.50e+00 2004-09-07T07:03:48.646 A 2.35e+01 1.18e+00 2004-09-07T07:03:48.646 A 2.43e+01 1.18e+00 2004-09-07T07:03:48.646 A A 1 2.97 1.7 1.7 -45.0 2.18e+01 1.15e+00 2004-09-07T07:03:48.646 A 2.11e+01 1.09e+00 2004-09-07T07:03:48.646 A 2.15e+01 1.08e+00 2004-09-07T07:03:48.646 A A 3 3.92 1.8 1.8 -45.0 3.16e+01 1.54e+00 2004-09-07T07:03:48.646 A 3.16e+01 1.55e+00 2004-09-07T07:03:48.646 A 4.12e+01 2.51e+00 2004-09-07T07:03:48.646 A A 1 4.98 1.8 1.8 -45.0 1.14e+02 1.06e+01 2004-09-18T21:03:27.740 A 1.09e+02 1.01e+01 2004-09-19T02:56:44.617 A 1.10e+02 1.02e+01 2004-09-19T12:00:6.179 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 9.52e+01 1.23e+01 2004-09-19T08:53:04.473 S Q 0 0.00 0.0 0.0 0.0 - SSTc2d J033247.2+305916 053.1966468 7.07e-07 +30.9878637 7.07e-07 A A 03324721+3059162 -5.00 -6.60e-01 4.83e-02 6.25e+00 6 YSOc_star+dust(IR2) 30.79 1.40 6.20 0.1600 1.53 4 2.58e+00 7.37e-02 1998-01-22T15:06:03 A 2.77e+01 5.61e-01 1998-01-22T15:06:03 A 8.26e+01 1.37e+00 1998-01-22T15:06:03 A 1.44e+02 7.46e+00 2004-09-07T07:03:48.646 A 1.47e+02 7.72e+00 2004-09-07T07:03:48.646 A 1.46e+02 7.42e+00 2004-09-07T07:03:48.646 A A 1 3.09 1.5 1.5 -45.0 1.68e+02 9.77e+00 2004-09-07T07:03:48.646 A 1.68e+02 9.65e+00 2004-09-07T07:03:48.646 A 1.73e+02 9.32e+00 2004-09-07T07:03:48.646 A A 1 2.97 1.7 1.7 -45.0 1.85e+02 9.97e+00 2004-09-07T07:03:48.646 A 1.81e+02 9.88e+00 2004-09-07T07:03:48.646 A 1.85e+02 9.60e+00 2004-09-07T07:03:48.646 A A 2 8.54 2.9 2.6 -61.6 1.85e+02 9.26e+00 2004-09-07T07:03:48.646 A 1.85e+02 9.27e+00 2004-09-07T07:03:48.646 A 1.90e+02 9.36e+00 2004-09-07T07:03:48.646 A A 2 13.84 3.7 3.3 -61.1 2.22e+02 2.07e+01 2004-09-18T21:03:27.740 A 2.28e+02 2.11e+01 2004-09-19T02:56:44.617 A 2.23e+02 2.07e+01 2004-09-19T12:00:6.179 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 1.90e+02 2.39e+01 2004-09-19T08:53:04.473 A Q 0 0.00 0.0 0.0 0.0 - SSTc2d J033257.8+310608 053.2410000 8.44e-07 +31.1022931 8.44e-07 A A null -2.61 3.00e-01 5.46e-02 3.90e+01 5 YSOc_red -999.00 -999.00 -999.00 -999.0000 -999.00 0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 7.86e-01 4.12e-02 2004-09-07T07:03:48.646 A 8.14e-01 4.42e-02 2004-09-07T07:03:48.646 A 8.11e-01 4.09e-02 2004-09-07T07:03:48.646 A A 1 3.09 1.5 1.5 -45.0 9.33e-01 4.81e-02 2004-09-07T07:03:48.646 A 9.44e-01 4.92e-02 2004-09-07T07:03:48.646 A 9.54e-01 4.70e-02 2004-09-07T07:03:48.646 A A 1 2.97 1.7 1.7 -45.0 1.07e+00 7.55e-02 2004-09-07T07:03:48.646 A 1.04e+00 6.96e-02 2004-09-07T07:03:48.646 A 1.06e+00 6.49e-02 2004-09-07T07:03:48.646 A A 1 3.92 1.8 1.8 -45.0 1.19e+00 7.44e-02 2004-09-07T07:03:48.646 A 1.17e+00 7.28e-02 2004-09-07T07:03:48.646 A 1.20e+00 6.63e-02 2004-09-07T07:03:48.646 A A 1 4.98 1.8 1.8 -45.0 1.45e+01 1.37e+00 2004-09-18T21:03:27.740 A 1.46e+01 1.38e+00 2004-09-19T02:56:44.617 A 1.46e+01 1.36e+00 2004-09-19T12:00:6.179 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 8.05e+01 1.30e+01 2004-09-19T08:53:04.473 P Q 0 0.00 0.0 0.0 0.0 - SSTc2d J033306.4+310805 053.2767016 4.31e-07 +31.1346001 4.31e-07 A A 03330642+3108045 -5.00 3.40e-01 4.79e-02 2.75e+00 6 YSOc_star+dust(IR1) 19.00 2.46 11.15 0.3650 1.00 3 4.10e-01 5.09e-02 1998-01-22T15:16:60 B 1.70e+00 8.15e-02 1998-01-22T15:16:60 A 3.35e+00 8.65e-02 1998-01-22T15:16:60 A 7.22e+00 3.63e-01 2004-09-07T07:03:48.646 A 7.28e+00 3.64e-01 2004-09-07T07:03:48.646 A 7.34e+00 3.58e-01 2004-09-07T07:03:48.646 A A 1 3.09 1.5 1.5 -45.0 9.50e+00 4.70e-01 2004-09-07T07:03:48.646 A 9.61e+00 4.71e-01 2004-09-07T07:03:48.646 A 9.66e+00 4.66e-01 2004-09-07T07:03:48.646 A A 1 2.97 1.7 1.7 -45.0 1.12e+01 5.42e-01 2004-09-07T07:03:48.646 A 1.12e+01 5.36e-01 2004-09-07T07:03:48.646 A 1.13e+01 5.35e-01 2004-09-07T07:03:48.646 A A 1 3.92 1.8 1.8 -45.0 2.02e+01 9.79e-01 2004-09-07T07:03:48.646 A 2.04e+01 9.87e-01 2004-09-07T07:03:48.646 A 2.06e+01 9.81e-01 2004-09-07T07:03:48.646 A A 1 4.98 1.8 1.8 -45.0 9.40e+01 8.70e+00 2004-09-18T21:03:27.740 A 8.87e+01 8.27e+00 2004-09-19T02:56:44.617 A 9.19e+01 8.51e+00 2004-09-19T12:00:6.179 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 4.44e+01 9.46e+00 2004-09-19T08:53:04.473 C Q 0 0.00 0.0 0.0 0.0 - SSTc2d J033309.6+310531 053.2898357 1.01e-06 +31.0919939 1.01e-06 A A 03330956+3105312 -1.88 1.13e+00 5.08e-02 1.25e+02 6 YSOc_PAH-em -999.00 -999.00 -999.00 -999.0000 -999.00 0 3.21e-01 5.06e-02 1998-01-22T15:16:60 C 8.32e-01 7.44e-02 1998-01-22T15:16:60 A 8.19e-01 6.33e-02 1998-01-22T15:16:60 A 5.19e-01 2.82e-02 2004-09-07T07:03:48.646 A 5.22e-01 2.90e-02 2004-09-07T07:03:48.646 A 5.28e-01 2.70e-02 2004-09-07T07:03:48.646 A A 1 3.09 1.5 1.5 -45.0 5.03e-01 2.92e-02 2004-09-07T07:03:48.646 A 5.01e-01 2.90e-02 2004-09-07T07:03:48.646 A 5.05e-01 2.68e-02 2004-09-07T07:03:48.646 A A 1 2.97 1.7 1.7 -45.0 5.63e-01 5.46e-02 2004-09-07T07:03:48.646 A 5.39e-01 5.59e-02 2004-09-07T07:03:48.646 A 5.64e-01 4.48e-02 2004-09-07T07:03:48.646 A A 1 3.92 1.8 1.8 -45.0 1.47e+00 9.24e-02 2004-09-07T07:03:48.646 A 1.52e+00 9.51e-02 2004-09-07T07:03:48.646 A 1.51e+00 8.40e-02 2004-09-07T07:03:48.646 A A 1 4.98 1.8 1.8 -45.0 7.96e+01 7.36e+00 2004-09-18T21:03:27.740 A 7.46e+01 6.96e+00 2004-09-19T02:56:44.617 A 7.73e+01 7.16e+00 2004-09-19T12:00:6.179 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 1.03e+02 1.33e+01 2004-09-19T08:53:04.473 P Q 0 0.00 0.0 0.0 0.0 - SSTc2d J033312.8+312124 053.3034883 7.06e-07 +31.3567331 7.06e-07 A A 03331284+3121241 -5.00 4.10e-01 4.95e-02 2.46e+00 6 YSOc_star+dust(IR1) 18.24 2.36 6.99 0.3540 0.91 3 2.24e+01 3.91e-01 1998-01-22T15:16:42 A 8.88e+01 1.31e+00 1998-01-22T15:16:42 A 1.67e+02 2.77e+00 1998-01-22T15:16:42 A 2.95e+02 1.64e+01 2004-09-07T07:03:48.646 A 2.99e+02 1.71e+01 2004-09-07T07:03:48.646 A 3.01e+02 1.63e+01 2004-09-07T07:03:48.646 A A 9 3.09 1.5 1.5 -45.0 4.28e+02 2.90e+01 2004-09-07T07:03:48.646 K 4.33e+02 2.89e+01 2004-09-07T07:03:48.646 K 4.35e+02 2.69e+01 2004-09-07T07:03:48.646 K A 7 2.27 1.7 1.7 -45.0 6.35e+02 3.23e+01 2004-09-07T07:03:48.646 A 6.25e+02 3.24e+01 2004-09-07T07:03:48.646 A 6.42e+02 3.18e+01 2004-09-07T07:03:48.646 A A 1 3.92 1.8 1.8 -45.0 1.08e+03 5.67e+01 2004-09-07T07:03:48.646 A 1.09e+03 5.76e+01 2004-09-07T07:03:48.646 A 1.12e+03 5.75e+01 2004-09-07T07:03:48.646 A A 1 4.98 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U 4.39e+03 4.14e+02 2004-09-19T02:56:44.617 A 4.04e+03 3.82e+02 2004-09-19T12:00:6.179 A Q 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 3.28e+03 3.21e+02 2004-09-19T08:53:04.473 A Q 0 0.00 0.0 0.0 0.0 - SSTc2d J033313.8+312005 053.3074863 8.45e-07 +31.3348028 8.45e-07 A A null -1.70 1.44e+00 5.53e-02 6.94e+00 5 YSOc_red -999.00 -999.00 -999.00 -999.0000 -999.00 0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 5.82e-01 3.36e-02 2004-09-07T07:03:48.646 A 5.90e-01 3.76e-02 2004-09-07T07:03:48.646 A 5.98e-01 3.30e-02 2004-09-07T07:03:48.646 A A 1 3.09 1.5 1.5 -45.0 1.28e+00 6.71e-02 2004-09-07T07:03:48.646 A 1.31e+00 7.12e-02 2004-09-07T07:03:48.646 A 1.30e+00 6.65e-02 2004-09-07T07:03:48.646 A A 1 2.97 1.7 1.7 -45.0 2.50e+00 1.41e-01 2004-09-07T07:03:48.646 A 2.41e+00 1.33e-01 2004-09-07T07:03:48.646 A 2.47e+00 1.29e-01 2004-09-07T07:03:48.646 A A 1 3.92 1.8 1.8 -45.0 5.36e+00 2.98e-01 2004-09-07T07:03:48.646 A 5.35e+00 2.69e-01 2004-09-07T07:03:48.646 A 5.45e+00 2.74e-01 2004-09-07T07:03:48.646 A A 1 4.98 1.8 1.8 -45.0 5.83e+01 5.45e+00 2004-09-18T21:03:27.740 A 6.01e+01 5.58e+00 2004-09-19T02:56:44.617 A 5.85e+01 5.44e+00 2004-09-19T12:00:6.179 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 5.55e+01 1.81e+01 2004-09-19T08:53:04.473 C Q 0 0.00 0.0 0.0 0.0 - SSTc2d J033314.4+310711 053.3099369 9.51e-07 +31.1196907 9.51e-07 A A null -2.77 2.22e+00 5.79e-02 5.59e+01 5 YSOc_red -999.00 -999.00 -999.00 -999.0000 -999.00 0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 1.63e-01 1.38e-02 2004-09-07T07:03:48.646 A 1.57e-01 1.27e-02 2004-09-07T07:03:48.646 A 1.62e-01 1.10e-02 2004-09-07T07:03:48.646 A A 1 3.09 1.5 1.5 -45.0 9.17e-01 5.25e-02 2004-09-07T07:03:48.646 A 9.41e-01 5.33e-02 2004-09-07T07:03:48.646 A 9.39e-01 4.96e-02 2004-09-07T07:03:48.646 A A 1 2.97 1.7 1.7 -45.0 2.04e+00 1.24e-01 2004-09-07T07:03:48.646 A 2.01e+00 1.20e-01 2004-09-07T07:03:48.646 A 2.05e+00 1.13e-01 2004-09-07T07:03:48.646 A A 1 3.92 1.8 1.8 -45.0 4.20e+00 2.13e-01 2004-09-07T07:03:48.646 A 4.06e+00 2.08e-01 2004-09-07T07:03:48.646 A 4.21e+00 2.05e-01 2004-09-07T07:03:48.646 A A 1 4.98 1.8 1.8 -45.0 1.13e+02 1.07e+01 2004-09-18T21:03:27.740 A 1.12e+02 1.04e+01 2004-09-19T02:56:44.617 A 1.11e+02 1.04e+01 2004-09-19T12:00:6.179 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 8.09e+02 8.35e+01 2004-09-19T08:53:04.473 P Q 0 0.00 0.0 0.0 0.0 - SSTc2d J033316.7+310755 053.3193828 5.19e-07 +31.1319892 5.19e-07 A A 03331667+3107548 -5.00 1.57e+00 5.02e-02 6.36e+01 6 YSOc_red -999.00 -999.00 -999.00 -999.0000 -999.00 0 8.86e-02 -9.99e+02 1998-01-22T15:16:60 U 3.77e-01 -9.99e+02 1998-01-22T15:16:60 U 1.38e+00 7.77e-02 1998-01-22T15:16:60 A 1.59e+01 9.39e-01 2004-09-07T07:03:48.646 A 1.63e+01 9.68e-01 2004-09-07T07:03:48.646 A 1.64e+01 9.08e-01 2004-09-07T07:03:48.646 A A 1 3.09 1.5 1.5 -45.0 4.86e+01 2.55e+00 2004-09-07T07:03:48.646 A 4.85e+01 2.55e+00 2004-09-07T07:03:48.646 A 4.93e+01 2.50e+00 2004-09-07T07:03:48.646 A A 1 2.97 1.7 1.7 -45.0 8.34e+01 4.02e+00 2004-09-07T07:03:48.646 A 8.38e+01 4.00e+00 2004-09-07T07:03:48.646 A 8.45e+01 4.00e+00 2004-09-07T07:03:48.646 A A 1 3.92 1.8 1.8 -45.0 1.11e+02 5.32e+00 2004-09-07T07:03:48.646 A 1.15e+02 5.51e+00 2004-09-07T07:03:48.646 A 1.15e+02 5.43e+00 2004-09-07T07:03:48.646 A A 1 4.98 1.8 1.8 -45.0 1.83e+03 1.72e+02 2004-09-18T21:03:27.740 A 1.86e+03 1.74e+02 2004-09-19T02:56:44.617 A 1.69e+03 1.60e+02 2004-09-19T12:00:6.179 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 6.45e+03 6.09e+02 2004-09-19T08:53:04.473 A Q 0 0.00 0.0 0.0 0.0 - SSTc2d J033317.9+310932 053.3243862 7.45e-07 +31.1588668 7.45e-07 A A null -4.35 3.33e+00 5.70e-02 7.78e+02 5 YSOc_red -999.00 -999.00 -999.00 -999.0000 -999.00 0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 1.64e-01 1.23e-02 2004-09-07T07:03:48.646 A 1.72e-01 1.25e-02 2004-09-07T07:03:48.646 A 1.71e-01 1.07e-02 2004-09-07T07:03:48.646 A A 1 3.09 1.5 1.5 -45.0 6.28e+00 3.36e-01 2004-09-07T07:03:48.646 A 6.36e+00 3.37e-01 2004-09-07T07:03:48.646 A 6.40e+00 3.25e-01 2004-09-07T07:03:48.646 A A 1 2.97 1.7 1.7 -45.0 3.65e+01 1.78e+00 2004-09-07T07:03:48.646 A 3.58e+01 1.84e+00 2004-09-07T07:03:48.646 A 3.64e+01 1.79e+00 2004-09-07T07:03:48.646 A A 1 3.92 1.8 1.8 -45.0 1.00e+02 5.21e+00 2004-09-07T07:03:48.646 A 1.02e+02 5.30e+00 2004-09-07T07:03:48.646 A 1.02e+02 5.14e+00 2004-09-07T07:03:48.646 A A 1 4.98 1.8 1.8 -45.0 7.10e+02 6.60e+01 2004-09-18T21:03:27.740 A 7.11e+02 6.62e+01 2004-09-19T02:56:44.617 A 6.70e+02 6.30e+01 2004-09-19T12:00:6.179 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 1.17e+04 1.19e+03 2004-09-19T08:53:04.473 P Q 0 0.00 0.0 0.0 0.0 - SSTc2d J033320.3+310721 053.3346459 4.61e-07 +31.1226326 4.61e-07 A A null -5.00 8.80e-01 5.36e-02 8.18e+01 5 YSOc_red -999.00 -999.00 -999.00 -999.0000 -999.00 0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 4.07e+00 2.07e-01 2004-09-07T07:03:48.646 A 4.17e+00 2.15e-01 2004-09-07T07:03:48.646 A 4.19e+00 2.07e-01 2004-09-07T07:03:48.646 A A 1 3.09 1.5 1.5 -45.0 1.63e+01 8.10e-01 2004-09-07T07:03:48.646 A 1.64e+01 7.98e-01 2004-09-07T07:03:48.646 A 1.66e+01 7.98e-01 2004-09-07T07:03:48.646 A A 1 2.97 1.7 1.7 -45.0 2.57e+01 1.25e+00 2004-09-07T07:03:48.646 A 2.56e+01 1.24e+00 2004-09-07T07:03:48.646 A 2.59e+01 1.23e+00 2004-09-07T07:03:48.646 A A 1 3.92 1.8 1.8 -45.0 3.18e+01 1.54e+00 2004-09-07T07:03:48.646 A 3.19e+01 1.54e+00 2004-09-07T07:03:48.646 A 3.24e+01 1.54e+00 2004-09-07T07:03:48.646 A A 1 4.98 1.8 1.8 -45.0 1.74e+02 1.61e+01 2004-09-18T21:03:27.740 A 1.73e+02 1.60e+01 2004-09-19T02:56:44.617 A 1.68e+02 1.57e+01 2004-09-19T12:00:6.179 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 3.50e+02 4.00e+01 2004-09-19T08:53:04.473 A Q 0 0.00 0.0 0.0 0.0 - SSTc2d J033327.3+310710 053.3637004 4.97e-07 +31.1195040 4.97e-07 A A null -5.00 1.93e+00 5.41e-02 1.06e+02 5 YSOc_red -999.00 -999.00 -999.00 -999.0000 -999.00 0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 2.38e+00 1.28e-01 2004-09-07T07:03:48.646 A 2.42e+00 1.33e-01 2004-09-07T07:03:48.646 A 2.43e+00 1.25e-01 2004-09-07T07:03:48.646 A A 1 3.09 1.5 1.5 -45.0 6.50e+00 3.16e-01 2004-09-07T07:03:48.646 A 6.72e+00 3.29e-01 2004-09-07T07:03:48.646 A 6.68e+00 3.20e-01 2004-09-07T07:03:48.646 A A 1 2.97 1.7 1.7 -45.0 7.76e+00 3.78e-01 2004-09-07T07:03:48.646 A 7.95e+00 3.83e-01 2004-09-07T07:03:48.646 A 7.93e+00 3.77e-01 2004-09-07T07:03:48.646 A A 1 3.92 1.8 1.8 -45.0 1.20e+01 5.78e-01 2004-09-07T07:03:48.646 A 1.29e+01 6.30e-01 2004-09-07T07:03:48.646 A 1.24e+01 5.86e-01 2004-09-07T07:03:48.646 A A 1 4.98 1.8 1.8 -45.0 1.69e+03 1.58e+02 2004-09-18T21:03:27.740 A 1.62e+03 1.52e+02 2004-09-19T02:56:44.617 A 1.57e+03 1.47e+02 2004-09-19T12:00:6.179 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 5.41e+03 5.10e+02 2004-09-19T08:53:04.473 P Q 0 0.00 0.0 0.0 0.0 - SSTc2d J033330.4+311051 053.3766930 6.27e-07 +31.1807141 6.27e-07 A A 03333041+3110504 -5.00 -9.10e-01 4.84e-02 3.80e+00 6 YSOc_star+dust(IR1) 11.82 2.36 6.38 0.3530 0.74 3 1.94e+02 3.93e+00 1998-01-22T15:20:10 A 4.08e+02 6.39e+00 1998-01-22T15:20:10 A 5.62e+02 8.28e+00 1998-01-22T15:20:10 A 8.19e+02 4.30e+01 2004-09-07T07:03:48.646 A 8.37e+02 4.45e+01 2004-09-07T07:03:48.646 A 8.38e+02 4.34e+01 2004-09-07T07:03:48.646 A A 9 3.09 1.5 1.5 -45.0 8.61e+02 4.88e+01 2004-09-07T07:03:48.646 A 8.87e+02 4.99e+01 2004-09-07T07:03:48.646 A 9.01e+02 5.03e+01 2004-09-07T07:03:48.646 A A 7 2.97 1.7 1.7 -45.0 8.45e+02 4.23e+01 2004-09-07T07:03:48.646 A 8.66e+02 4.33e+01 2004-09-07T07:03:48.646 A 8.79e+02 4.29e+01 2004-09-07T07:03:48.646 A A 1 3.92 1.8 1.8 -45.0 8.49e+02 4.16e+01 2004-09-07T07:03:48.646 A 8.49e+02 4.18e+01 2004-09-07T07:03:48.646 A 8.67e+02 4.15e+01 2004-09-07T07:03:48.646 A A 1 4.98 1.8 1.8 -45.0 7.98e+02 7.51e+01 2004-09-18T21:03:27.740 A 9.21e+02 8.55e+01 2004-09-19T02:56:44.617 A 8.34e+02 7.78e+01 2004-09-19T12:00:6.179 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 5.12e+02 5.11e+01 2004-09-19T08:53:04.473 A Q 0 0.00 0.0 0.0 0.0 - SSTc2d J033341.3+311341 053.4220358 4.55e-07 +31.2280639 4.55e-07 A A 03334129+3113410 -5.00 -5.70e-01 4.80e-02 5.32e+00 6 YSOc_star+dust(IR1) 18.12 2.36 8.81 0.3540 2.68 3 4.96e+00 1.14e-01 1998-01-22T15:20:18 A 1.58e+01 3.21e-01 1998-01-22T15:20:18 A 3.27e+01 6.03e-01 1998-01-22T15:20:18 A 4.78e+01 2.48e+00 2004-09-07T07:03:48.646 A 5.13e+01 2.61e+00 2004-09-07T07:03:48.646 A 4.97e+01 2.49e+00 2004-09-07T07:03:48.646 A A 1 3.09 1.5 1.5 -45.0 5.68e+01 2.85e+00 2004-09-07T07:03:48.646 A 5.64e+01 2.82e+00 2004-09-07T07:03:48.646 A 5.59e+01 2.72e+00 2004-09-07T07:03:48.646 A A 1 2.97 1.7 1.7 -45.0 5.52e+01 2.64e+00 2004-09-07T07:03:48.646 A 5.53e+01 2.63e+00 2004-09-07T07:03:48.646 A 5.58e+01 2.63e+00 2004-09-07T07:03:48.646 A A 1 3.92 1.8 1.8 -45.0 5.41e+01 2.68e+00 2004-09-07T07:03:48.646 A 5.43e+01 2.65e+00 2004-09-07T07:03:48.646 A 5.52e+01 2.65e+00 2004-09-07T07:03:48.646 A A 1 4.98 1.8 1.8 -45.0 1.29e+02 1.19e+01 2004-09-18T21:03:27.740 A 1.32e+02 1.22e+01 2004-09-19T02:56:44.617 A 1.28e+02 1.19e+01 2004-09-19T12:00:6.179 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 1.71e+02 2.20e+01 2004-09-19T08:53:04.473 A Q 0 0.00 0.0 0.0 0.0 - SSTc2d J033401.7+311440 053.5069217 4.29e-07 +31.2443803 4.29e-07 A A 03340166+3114396 -5.00 -9.20e-01 4.78e-02 4.40e+00 6 YSOc_star+dust(IR2) 9.19 1.39 8.26 0.1590 0.84 4 7.05e+01 1.36e+00 1998-01-22T15:20:18 A 1.16e+02 2.35e+00 1998-01-22T15:20:18 A 1.10e+02 2.03e+00 1998-01-22T15:20:18 A 8.21e+01 4.10e+00 2004-09-07T07:03:48.646 A 8.12e+01 4.22e+00 2004-09-07T07:03:48.646 A 8.30e+01 4.10e+00 2004-09-07T07:03:48.646 A A 1 3.09 1.5 1.5 -45.0 7.73e+01 3.79e+00 2004-09-07T07:03:48.646 A 7.99e+01 3.93e+00 2004-09-07T07:03:48.646 A 7.97e+01 3.87e+00 2004-09-07T07:03:48.646 A A 1 2.97 1.7 1.7 -45.0 8.61e+01 4.10e+00 2004-09-07T07:03:48.646 A 8.62e+01 4.13e+00 2004-09-07T07:03:48.646 A 8.69e+01 4.11e+00 2004-09-07T07:03:48.646 A A 1 3.92 1.8 1.8 -45.0 1.01e+02 4.87e+00 2004-09-07T07:03:48.646 A 1.01e+02 4.95e+00 2004-09-07T07:03:48.646 A 1.03e+02 4.92e+00 2004-09-07T07:03:48.646 A A 1 4.98 1.8 1.8 -45.0 9.83e+01 9.08e+00 2004-09-18T21:03:27.740 A 9.66e+01 8.94e+00 2004-09-19T02:56:44.617 A 9.78e+01 9.04e+00 2004-09-19T12:00:6.179 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 9.02e+01 1.55e+01 2004-09-19T08:53:04.473 B Q 0 0.00 0.0 0.0 0.0 - SSTc2d J033449.8+311550 053.7076835 1.07e-06 +31.2639642 1.07e-06 A A 03344987+3115498 -5.00 -1.38e+00 4.92e-02 2.70e+01 6 YSOc_star+dust(IR4) 7.50 1.13 8.53 0.1070 0.37 6 8.34e+01 1.69e+00 1998-01-22T15:34:16 A 1.18e+02 2.40e+00 1998-01-22T15:34:16 A 1.04e+02 2.00e+00 1998-01-22T15:34:16 A 6.34e+01 3.92e+00 2004-09-07T07:03:48.646 A 6.75e+01 4.12e+00 2004-09-07T07:03:48.646 A 6.71e+01 3.80e+00 2004-09-07T07:03:48.646 A A 1 3.09 1.5 1.5 -45.0 4.44e+01 2.68e+00 2004-09-07T07:03:48.646 A 4.50e+01 2.70e+00 2004-09-07T07:03:48.646 A 4.57e+01 2.55e+00 2004-09-07T07:03:48.646 A A 1 2.97 1.7 1.7 -45.0 3.44e+01 1.83e+00 2004-09-07T07:03:48.646 A 3.38e+01 1.82e+00 2004-09-07T07:03:48.646 A 3.44e+01 1.76e+00 2004-09-07T07:03:48.646 A A 1 3.92 1.8 1.8 -45.0 3.34e+01 1.71e+00 2004-09-07T07:03:48.646 A 3.36e+01 1.71e+00 2004-09-07T07:03:48.646 A 3.43e+01 1.69e+00 2004-09-07T07:03:48.646 A A 1 4.98 1.8 1.8 -45.0 4.99e+01 4.64e+00 2004-09-18T21:03:27.740 A 4.75e+01 4.46e+00 2004-09-19T02:56:44.617 A 4.86e+01 4.53e+00 2004-09-19T12:00:6.179 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 4.04e+01 1.12e+01 2004-09-19T08:53:04.473 C Q 0 0.00 0.0 0.0 0.0 - SSTc2d J034109.1+314438 055.2880359 6.02e-07 +31.7438571 6.02e-07 A A 03410913+3144378 -5.00 -7.20e-01 4.88e-02 8.56e+00 6 YSOc_star+dust(IR1) 13.71 2.36 7.08 0.3540 2.11 3 7.15e+01 1.38e+00 1998-10-05T20:47:46 A 1.53e+02 2.68e+00 1998-10-05T20:47:46 A 2.51e+02 4.16e+00 1998-10-05T20:47:46 A 3.95e+02 2.09e+01 2004-09-07T07:03:48.646 A 4.06e+02 2.21e+01 2004-09-07T07:03:48.646 A 4.12e+02 2.16e+01 2004-09-07T07:03:48.646 A A 7 3.09 1.5 1.5 -45.0 3.79e+02 2.94e+01 2004-09-07T07:03:48.646 K 3.65e+02 2.03e+01 2004-09-07T07:03:48.646 A 3.83e+02 2.51e+01 2004-09-07T07:03:48.646 K A 7 2.27 1.7 1.7 -45.0 3.87e+02 1.89e+01 2004-09-07T07:03:48.646 A 3.80e+02 1.86e+01 2004-09-07T07:03:48.646 A 3.92e+02 1.88e+01 2004-09-07T07:03:48.646 A A 1 3.92 1.8 1.8 -45.0 3.50e+02 1.71e+01 2004-09-07T07:03:48.646 A 3.51e+02 1.72e+01 2004-09-07T07:03:48.646 A 3.59e+02 1.71e+01 2004-09-07T07:03:48.646 A A 1 4.98 1.8 1.8 -45.0 7.87e+02 7.34e+01 2004-09-18T16:14:42.183 A 8.17e+02 7.61e+01 2004-09-18T22:34:28.703 A 7.67e+02 7.17e+01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 2.06e+03 1.97e+02 2004-09-19T08:53:04.473 A Q 0 0.00 0.0 0.0 0.0 - SSTc2d J034155.7+314811 055.4821417 3.94e-07 +31.8031751 3.94e-07 A A 03415572+3148112 -5.00 2.50e-01 4.76e-02 1.66e+01 6 YSOc_star+dust(IR1) 17.63 2.39 10.96 0.3570 0.73 3 6.61e-01 4.63e-02 1998-10-05T21:01:44 A 2.52e+00 9.52e-02 1998-10-05T21:01:44 A 4.55e+00 9.23e-02 1998-10-05T21:01:44 A 1.67e+01 8.26e-01 2004-09-07T07:03:48.646 A 1.71e+01 8.62e-01 2004-09-07T07:03:48.646 A 1.71e+01 8.33e-01 2004-09-07T07:03:48.646 A A 1 3.09 1.5 1.5 -45.0 2.34e+01 1.15e+00 2004-09-07T07:03:48.646 A 2.38e+01 1.17e+00 2004-09-07T07:03:48.646 A 2.39e+01 1.15e+00 2004-09-07T07:03:48.646 A A 1 2.97 1.7 1.7 -45.0 3.30e+01 1.58e+00 2004-09-07T07:03:48.646 A 3.29e+01 1.59e+00 2004-09-07T07:03:48.646 A 3.33e+01 1.58e+00 2004-09-07T07:03:48.646 A A 1 3.92 1.8 1.8 -45.0 4.64e+01 2.21e+00 2004-09-07T07:03:48.646 A 4.66e+01 2.20e+00 2004-09-07T07:03:48.646 A 4.73e+01 2.22e+00 2004-09-07T07:03:48.646 A A 1 4.98 1.8 1.8 -45.0 1.26e+02 1.17e+01 2004-09-18T16:14:42.183 A 1.17e+02 1.09e+01 2004-09-18T22:34:28.703 A 1.21e+02 1.12e+01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 2.36e+02 4.02e+01 2004-09-19T08:53:04.473 B Q 0 0.00 0.0 0.0 0.0 - SSTc2d J034157.4+314837 055.4893401 6.15e-07 +31.8102061 6.15e-07 A A 03415745+3148365 -5.00 -1.01e+00 4.97e-02 4.32e+00 6 YSOc_star+dust(IR1) 14.04 2.36 7.11 0.3530 0.97 3 5.81e+01 9.64e-01 1998-10-05T21:01:35 A 1.48e+02 2.32e+00 1998-10-05T21:01:35 A 2.30e+02 2.97e+00 1998-10-05T21:01:35 A 3.19e+02 1.77e+01 2004-09-07T07:03:48.646 A 3.28e+02 1.82e+01 2004-09-07T07:03:48.646 A 3.39e+02 2.17e+01 2004-09-07T07:03:48.646 K A 7 1.77 1.5 1.5 -45.0 3.42e+02 1.83e+01 2004-09-07T07:03:48.646 A 3.37e+02 1.84e+01 2004-09-07T07:03:48.646 A 3.42e+02 1.81e+01 2004-09-07T07:03:48.646 A A 1 2.97 1.7 1.7 -45.0 3.42e+02 2.06e+01 2004-09-07T07:03:48.646 A 3.27e+02 1.65e+01 2004-09-07T07:03:48.646 A 3.39e+02 1.63e+01 2004-09-07T07:03:48.646 A A 1 3.92 1.8 1.8 -45.0 3.22e+02 1.57e+01 2004-09-07T07:03:48.646 A 3.18e+02 1.58e+01 2004-09-07T07:03:48.646 A 3.24e+02 1.60e+01 2004-09-07T07:03:48.646 A A 1 4.98 1.8 1.8 -45.0 2.69e+02 2.49e+01 2004-09-18T16:14:42.183 A 2.61e+02 2.42e+01 2004-09-18T22:34:28.703 A 2.68e+02 2.48e+01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 1.71e+02 2.66e+01 2004-09-19T08:53:04.473 B Q 0 0.00 0.0 0.0 0.0 - SSTc2d J034202.2+314802 055.5090391 7.00e-07 +31.8005703 7.00e-07 A A 03420217+3148019 -2.00 1.47e+00 5.06e-02 2.65e+01 6 YSOc_red -999.00 -999.00 -999.00 -999.0000 -999.00 0 1.56e-01 -9.99e+02 1998-10-05T21:01:44 U 5.60e-01 -9.99e+02 1998-10-05T21:01:44 U 8.06e-01 7.79e-02 1998-10-05T21:01:44 A 8.47e-01 4.61e-02 2004-09-07T07:03:48.646 A 8.59e-01 4.87e-02 2004-09-07T07:03:48.646 A 8.66e-01 4.50e-02 2004-09-07T07:03:48.646 A A 1 3.09 1.5 1.5 -45.0 1.23e+00 6.51e-02 2004-09-07T07:03:48.646 A 1.22e+00 6.60e-02 2004-09-07T07:03:48.646 A 1.24e+00 6.29e-02 2004-09-07T07:03:48.646 A A 1 2.97 1.7 1.7 -45.0 2.29e+00 1.40e-01 2004-09-07T07:03:48.646 A 2.34e+00 1.34e-01 2004-09-07T07:03:48.646 A 2.34e+00 1.26e-01 2004-09-07T07:03:48.646 A A 1 3.92 1.8 1.8 -45.0 6.78e+00 3.39e-01 2004-09-07T07:03:48.646 A 6.79e+00 3.36e-01 2004-09-07T07:03:48.646 A 6.91e+00 3.34e-01 2004-09-07T07:03:48.646 A A 1 4.98 1.8 1.8 -45.0 1.16e+02 1.07e+01 2004-09-18T16:14:42.183 A 1.14e+02 1.05e+01 2004-09-18T22:34:28.703 A 1.16e+02 1.07e+01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 2.28e+02 3.36e+01 2004-09-19T08:53:04.473 S Q 0 0.00 0.0 0.0 0.0 - SSTc2d J034219.3+314327 055.5802985 4.34e-07 +31.7241545 4.34e-07 A A 03421927+3143269 -5.00 -5.70e-01 4.78e-02 5.74e+00 6 YSOc_star+dust(IR1) 15.22 2.36 9.21 0.3540 0.92 3 6.18e+00 1.25e-01 1998-10-05T21:01:44 A 1.79e+01 3.14e-01 1998-10-05T21:01:44 A 2.93e+01 4.86e-01 1998-10-05T21:01:44 A 4.38e+01 2.23e+00 2004-09-07T07:03:48.646 A 4.51e+01 2.33e+00 2004-09-07T07:03:48.646 A 4.55e+01 2.26e+00 2004-09-07T07:03:48.646 A A 1 3.09 1.5 1.5 -45.0 4.48e+01 2.27e+00 2004-09-07T07:03:48.646 A 4.52e+01 2.26e+00 2004-09-07T07:03:48.646 A 4.53e+01 2.22e+00 2004-09-07T07:03:48.646 A A 1 2.97 1.7 1.7 -45.0 4.30e+01 2.07e+00 2004-09-07T07:03:48.646 A 4.23e+01 2.04e+00 2004-09-07T07:03:48.646 A 4.33e+01 2.06e+00 2004-09-07T07:03:48.646 A A 1 3.92 1.8 1.8 -45.0 4.92e+01 2.35e+00 2004-09-07T07:03:48.646 A 4.98e+01 2.36e+00 2004-09-07T07:03:48.646 A 5.04e+01 2.37e+00 2004-09-07T07:03:48.646 A A 1 4.98 1.8 1.8 -45.0 1.12e+02 1.03e+01 2004-09-18T16:14:42.183 A 1.15e+02 1.06e+01 2004-09-18T22:34:28.703 A 1.13e+02 1.05e+01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 1.09e+02 2.35e+01 2004-09-19T08:53:04.473 C Q 0 0.00 0.0 0.0 0.0 - SSTc2d J034255.9+315842 055.7331248 7.70e-07 +31.9783391 7.70e-07 A A 03425596+3158419 -5.00 -9.80e-01 4.95e-02 4.52e+01 6 YSOc_star+dust(IR1) 8.61 2.36 8.10 0.3540 1.35 3 9.57e+01 2.12e+00 1999-11-05T20:22:08 A 1.32e+02 3.52e+00 1999-11-05T20:22:08 A 1.64e+02 2.87e+00 1999-11-05T20:22:08 A 3.83e+02 2.39e+01 2004-09-07T07:03:48.646 A 3.93e+02 2.42e+01 2004-09-07T07:03:48.646 A 3.98e+02 2.30e+01 2004-09-07T07:03:48.646 A A 9 3.09 1.5 1.5 -45.0 3.43e+02 2.33e+01 2004-09-07T07:03:48.646 K 3.46e+02 2.31e+01 2004-09-07T07:03:48.646 K 3.46e+02 2.13e+01 2004-09-07T07:03:48.646 K A 7 2.27 1.7 1.7 -45.0 3.68e+02 1.89e+01 2004-09-07T07:03:48.646 A 3.64e+02 1.87e+01 2004-09-07T07:03:48.646 A 3.75e+02 1.84e+01 2004-09-07T07:03:48.646 A A 7 3.92 1.8 1.8 -45.0 5.18e+02 2.62e+01 2004-09-07T07:03:48.646 A 5.22e+02 2.63e+01 2004-09-07T07:03:48.646 A 5.40e+02 2.66e+01 2004-09-07T07:03:48.646 A A 7 4.98 1.8 1.8 -45.0 1.70e+02 1.59e+01 2004-09-18T16:14:42.183 A 1.69e+02 1.58e+01 2004-09-18T22:34:28.703 A 1.68e+02 1.57e+01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 2.43e+03 2.39e+02 2004-09-19T08:53:04.473 A Q 2 0.00 0.0 0.0 0.0 - SSTc2d J034329.4+315219 055.8726456 3.95e-07 +31.8720702 3.95e-07 A A 03432944+3152194 -5.00 7.90e-01 4.77e-02 1.30e+01 6 YSOc_star+dust(IR1) 11.31 2.39 11.70 0.3580 0.00 3 1.37e+00 5.79e-02 2000-01-09T16:18:20 A 3.53e+00 1.37e-01 2000-01-09T16:18:20 A 4.19e+00 1.27e-01 2000-01-09T16:18:20 A 5.24e+00 2.51e-01 2004-02-11T00:28:52.750 A 4.24e+00 2.47e-01 2004-02-11T00:28:52.750 A 5.42e+00 2.58e-01 2004-02-11T00:28:52.750 A B 1 1.77 1.5 1.5 -45.0 9.39e+00 4.59e-01 2004-09-07T07:03:48.646 A 9.27e+00 4.62e-01 2004-09-07T07:03:48.646 A 9.56e+00 4.50e-01 2005-09-16T09:10:33.865 A A 1 2.27 1.7 1.7 -45.0 1.59e+01 7.53e-01 2004-02-11T00:28:52.750 A 1.34e+01 7.79e-01 2004-02-11T00:28:52.750 A 1.59e+01 7.46e-01 2004-02-11T00:28:52.750 A B 1 2.54 1.8 1.8 -45.0 3.24e+01 1.56e+00 2004-09-07T07:03:48.646 A 3.23e+01 1.60e+00 2004-09-07T07:03:48.646 A 3.29e+01 1.54e+00 2005-09-16T09:10:33.865 A A 1 2.54 1.8 1.8 -45.0 1.71e+02 1.58e+01 2004-09-18T16:14:42.183 A 1.58e+02 1.48e+01 2004-09-18T22:34:28.703 A 1.64e+02 1.52e+01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 2.45e+02 2.97e+01 2004-09-19T08:53:04.473 P Q 2 0.00 0.0 0.0 0.0 - SSTc2d J034344.5+314309 055.9353510 7.19e-07 +31.7192595 7.19e-07 A A 03434449+3143092 -5.00 -2.30e-01 5.04e-02 8.58e+00 6 YSOc_star+dust(IR2) 17.94 1.47 7.57 0.1760 1.91 4 1.88e+01 3.63e-01 1999-12-27T17:16:22 A 4.97e+01 1.28e+00 1999-12-27T17:16:22 A 1.02e+02 2.07e+00 1999-12-27T17:16:22 A 1.26e+02 6.85e+00 2004-09-07T07:03:48.646 A 8.12e+01 6.82e+00 2004-09-07T07:03:48.646 A 8.92e+01 6.30e+00 2004-09-07T07:03:48.646 A C 9 3.09 1.5 1.5 -45.0 1.80e+02 9.10e+00 2004-09-07T07:03:48.646 A 1.30e+02 1.00e+01 2004-09-07T07:03:48.646 A 1.69e+02 9.09e+00 2005-09-16T09:10:33.865 A B 1 2.27 1.7 1.7 -45.0 2.05e+02 1.00e+01 2004-09-07T07:03:48.646 A 1.75e+02 1.00e+01 2004-09-07T07:03:48.646 A 1.90e+02 9.94e+00 2004-09-07T07:03:48.646 A B 2 9.53 2.9 2.9 0.0 2.54e+02 1.23e+01 2004-09-07T07:03:48.646 A 2.30e+02 1.23e+01 2004-09-07T07:03:48.646 A 2.53e+02 1.23e+01 2005-09-16T09:10:33.865 A A 1 2.54 1.8 1.8 -45.0 5.11e+02 4.72e+01 2004-09-18T16:14:42.183 A 4.65e+02 4.36e+01 2004-09-18T22:34:28.703 A 4.84e+02 4.50e+01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 4.58e+02 4.68e+01 2004-09-19T08:53:04.473 A Q 0 0.00 0.0 0.0 0.0 - SSTc2d J034344.6+320818 055.9359387 5.92e-07 +32.1382705 5.92e-07 A A 03434461+3208177 -5.00 -6.90e-01 4.89e-02 1.16e+02 6 YSOc_star+dust(IR4) 7.92 1.13 9.77 0.1060 1.05 6 2.41e+01 5.11e-01 1999-12-27T17:16:48 A 3.61e+01 9.32e-01 1999-12-27T17:16:48 A 3.21e+01 7.11e-01 1999-12-27T17:16:48 A 1.97e+01 9.98e-01 2004-02-11T00:28:52.750 A 2.10e+01 1.11e+00 2004-02-11T00:28:52.750 A 1.90e+01 1.08e+00 2004-02-11T00:28:52.750 A A 1 3.09 1.5 1.5 -45.0 1.46e+01 7.27e-01 2004-02-11T00:28:52.750 A 1.54e+01 7.88e-01 2004-02-11T00:28:52.750 A 1.49e+01 7.52e-01 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 1.12e+01 5.51e-01 2004-02-11T00:28:52.750 A 1.18e+01 5.85e-01 2004-02-11T00:28:52.750 A 1.14e+01 5.52e-01 2004-02-11T00:28:52.750 A A 1 3.92 1.8 1.8 -45.0 1.06e+01 5.17e-01 2004-02-11T00:28:52.750 A 1.10e+01 5.40e-01 2004-02-11T00:28:52.750 A 1.09e+01 5.18e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 1.04e+02 9.62e+00 2004-09-18T16:14:42.183 A 1.05e+02 9.71e+00 2004-09-18T22:34:28.703 A 1.04e+02 9.66e+00 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 1.73e+02 4.33e+01 2004-09-19T08:53:04.473 P Q 0 0.00 0.0 0.0 0.0 - SSTc2d J034345.2+320359 055.9381954 6.70e-07 +32.0662831 6.70e-07 A B 03434517+3203585 -5.00 -2.20e-01 4.98e-02 9.05e+01 6 YSOc_star+dust(IR1) 16.50 2.37 9.22 0.3560 2.04 3 4.91e+00 1.31e-01 1999-12-27T17:16:39 A 1.41e+01 3.76e-01 1999-12-27T17:16:39 A 2.63e+01 6.30e-01 1999-12-27T17:16:39 A 2.79e+01 1.44e+00 2004-02-11T00:28:52.750 A 1.06e+02 8.13e+00 2004-02-11T00:28:52.750 A 2.11e+02 1.04e+01 2004-02-11T00:28:52.750 A E 7 1.77 1.5 1.5 -45.0 4.88e+01 2.57e+00 2004-02-11T00:28:52.750 A 1.38e+02 8.41e+00 2004-02-11T00:28:52.750 A 1.40e+02 8.51e+00 2004-02-11T00:28:52.750 A E 7 2.97 1.7 1.7 -45.0 6.19e+01 3.00e+00 2004-02-11T00:28:52.750 A 1.43e+02 9.24e+00 2004-02-11T00:28:52.750 A 1.99e+02 9.75e+00 2004-02-11T00:28:52.750 A E 1 2.54 1.8 1.8 -45.0 8.97e+01 4.49e+00 2004-02-11T00:28:52.750 A 1.51e+02 9.89e+00 2004-02-11T00:28:52.750 A 1.14e+02 7.29e+00 2004-02-11T00:28:52.750 A C 1 4.98 1.8 1.8 -45.0 7.40e+02 6.89e+01 2004-09-18T16:14:42.183 A 8.06e+02 7.49e+01 2004-09-18T22:34:28.703 A 7.36e+02 6.86e+01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 9.17e+02 9.32e+01 2004-09-19T08:53:04.473 P Q 0 0.00 0.0 0.0 0.0 - SSTc2d J034351.0+320308 055.9625743 1.23e-06 +32.0522495 1.23e-06 A C null -5.00 -2.80e-01 6.27e-02 1.96e+02 5 YSOc_red -999.00 -999.00 -999.00 -999.0000 -999.00 0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 1.48e+01 9.18e-01 2004-02-11T00:28:52.750 A 1.34e+00 1.39e-01 2004-02-11T00:28:52.750 A 6.81e+00 4.75e-01 2005-09-16T09:10:33.865 A E 2 4.99 2.2 2.0 -47.5 3.06e+01 2.05e+00 2004-02-11T00:28:52.750 A 3.30e+00 3.15e-01 2004-02-11T00:28:52.750 A 3.99e+00 5.99e-01 2004-02-11T00:28:52.750 B E 1 2.97 1.7 1.7 -45.0 3.03e+01 1.54e+00 2004-02-11T00:28:52.750 A 3.61e+00 3.16e-01 2004-02-11T00:28:52.750 A 1.28e+01 6.74e-01 2004-02-11T00:28:52.750 A E 1 2.54 1.8 1.8 -45.0 1.84e+01 9.03e-01 2004-02-11T00:28:52.750 A 2.27e+00 1.49e-01 2004-02-11T00:28:52.750 A 2.15e+00 1.53e-01 2004-02-11T00:28:52.750 A E 1 4.98 1.8 1.8 -45.0 3.93e+01 3.66e+00 2004-09-18T16:14:42.183 A 5.11e+01 4.74e+00 2004-09-18T22:34:28.703 A 4.54e+01 4.25e+00 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 1.96e+03 1.95e+02 2004-09-19T08:53:04.473 P Q 0 0.00 0.0 0.0 0.0 - SSTc2d J034356.4+320056 055.9848616 1.17e-08 +32.0154609 1.17e-08 A A null -1.54 -1.14e+00 1.22e-01 1.52e+01 5 YSOc_red -999.00 -999.00 -999.00 -999.0000 -999.00 0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 5.84e-01 9.78e-02 2004-02-11T00:28:52.750 B 1.18e+00 1.02e-01 2004-02-11T00:28:52.750 K 7.62e-01 4.59e-02 2004-02-11T00:28:52.750 K C 7 1.77 1.5 1.5 -45.0 7.13e-01 7.83e-02 2004-02-11T00:28:52.750 A 1.31e+00 1.15e-01 2004-02-11T00:28:52.750 K 1.05e+00 7.74e-02 2004-02-11T00:28:52.750 K C 7 2.27 1.7 1.7 -45.0 8.55e-01 1.09e-01 2004-02-11T00:28:52.750 K 9.39e-01 1.17e-01 2004-02-11T00:28:52.750 K 5.48e-01 5.71e-02 2004-02-11T00:28:52.750 A A -2 2.54 1.8 1.8 -45.0 9.15e-01 2.06e-01 2004-02-11T00:28:52.750 C 5.14e-01 8.17e-02 2004-02-11T00:28:52.750 B 4.36e-01 6.35e-02 2004-02-11T00:28:52.750 B A 7 4.98 1.8 1.8 -45.0 1.98e+00 4.13e-01 2004-09-18T16:14:42.183 C -9.99e+02 -9.99e+02 null U 1.44e+00 4.19e-01 2004-09-18T19:24:35.443 C Q -2 8.04 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 0.00e+00 0.00e+00 undetected Y Q 0 0.00 0.0 0.0 0.0 - SSTc2d J034356.8+320305 055.9868311 8.91e-09 +32.0513149 8.91e-09 A A null -2.42 1.37e+00 8.86e-02 8.95e+00 5 YSOc_red -999.00 -999.00 -999.00 -999.0000 -999.00 0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 5.07e-02 1.15e-02 2004-02-11T00:28:52.750 C Q -2 1.77 1.5 1.5 -45.0 2.93e-01 3.01e-02 2004-02-11T00:28:52.750 A 3.11e-01 3.03e-02 2004-02-11T00:28:52.750 A 1.95e-01 6.41e-02 2004-02-11T00:28:52.750 C A -2 2.97 1.7 1.7 -45.0 5.04e-01 8.31e-02 2004-02-11T00:28:52.750 B 5.85e-01 7.88e-02 2004-02-11T00:28:52.750 A 5.22e-01 5.35e-02 2005-09-16T09:10:33.865 A A 7 3.92 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 6.03e-01 1.17e-01 2004-02-11T00:28:52.750 B Q -2 4.98 1.8 1.8 -45.0 1.12e+01 1.14e+00 2004-09-18T16:14:42.183 A 1.03e+01 1.02e+00 2004-09-18T22:34:28.703 A 1.08e+01 1.04e+00 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 3.28e+03 3.14e+02 2004-09-19T08:53:04.473 S Q 0 0.00 0.0 0.0 0.0 - SSTc2d J034359.4+320036 055.9975507 2.25e-06 +32.0099032 2.25e-06 A A 03435940+3200354 -5.00 1.40e-01 5.23e-02 9.00e+01 6 YSOc_red -999.00 -999.00 -999.00 -999.0000 -999.00 0 3.10e-01 -9.99e+02 1999-12-27T17:16:39 U 4.44e-01 7.56e-02 1999-12-27T17:16:39 C 2.64e+00 1.63e-01 1999-12-27T17:16:39 A 2.20e+00 1.89e-01 2004-02-11T00:28:52.750 A 2.19e+00 1.70e-01 2004-02-11T00:28:52.750 A 2.18e+00 1.21e-01 2004-02-11T00:28:52.750 K A 7 3.46 2.1 2.1 -48.8 1.65e+01 1.21e+00 2004-02-11T00:28:52.750 K 1.28e+01 1.16e+00 2004-02-11T00:28:52.750 A 1.29e+01 1.03e+00 2004-02-11T00:28:52.750 A B 2 4.29 2.6 2.1 -77.6 5.13e+00 3.68e-01 2004-02-11T00:28:52.750 A 5.21e+00 3.64e-01 2004-02-11T00:28:52.750 A 5.31e+00 2.81e-01 2004-02-11T00:28:52.750 A A 2 4.87 1.5 4.2 -31.9 3.90e+00 2.90e-01 2004-02-11T00:28:52.750 A 4.07e+00 3.04e-01 2004-02-11T00:28:52.750 A 4.04e+00 2.71e-01 2004-02-11T00:28:52.750 A A 2 9.18 3.0 2.7 -80.9 4.39e+01 4.27e+00 2004-09-18T16:14:42.183 A 4.22e+01 3.96e+00 2004-09-18T22:34:28.703 A 4.32e+01 4.03e+00 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 3.67e+02 4.59e+01 2004-09-19T08:53:04.473 S Q 0 0.00 0.0 0.0 0.0 - SSTc2d J034359.8+320035 055.9991913 3.73e-08 +32.0097123 3.73e-08 A A 03435972+3200352 -5.00 -2.60e-01 5.80e-02 1.96e+02 6 YSOc_red -999.00 -999.00 -999.00 -999.0000 -999.00 0 2.74e-01 -9.99e+02 1999-12-27T17:16:39 U 6.55e-01 -9.99e+02 1999-12-27T17:16:39 U 1.12e+00 8.47e-02 1999-12-27T17:16:39 A 8.62e-01 9.28e-02 2004-02-11T00:28:52.750 A 8.90e-01 8.20e-02 2004-02-11T00:28:52.750 A 8.96e-01 6.49e-02 2004-02-11T00:28:52.750 A A 2 4.74 2.2 1.9 -86.5 8.25e+00 4.22e-01 2004-02-11T00:28:52.750 A 8.30e+00 4.35e-01 2004-02-11T00:28:52.750 A 8.24e+00 4.45e-01 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 4.88e+00 3.42e-01 2004-02-11T00:28:52.750 K 2.45e+00 1.51e-01 2004-02-11T00:28:52.750 A 3.38e+00 1.79e-01 2004-02-11T00:28:52.750 K D 1 2.54 1.8 1.8 -45.0 1.81e+00 1.67e-01 2004-02-11T00:28:52.750 K 1.50e+00 1.33e-01 2004-02-11T00:28:52.750 A 1.33e+00 1.01e-01 2004-02-11T00:28:52.750 A A 1 2.54 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 1.35e+01 1.42e+00 2004-09-18T19:24:35.443 A Q -2 8.04 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 0.00e+00 0.00e+00 undetected Y Q 0 0.00 0.0 0.0 0.0 - SSTc2d J034359.6+320154 055.9985393 6.38e-07 +32.0316949 6.38e-07 A A 03435964+3201539 -5.00 -3.40e-01 4.92e-02 9.87e+00 6 YSOc -999.00 -999.00 -999.00 -999.0000 -999.00 0 6.63e+00 2.50e-01 1999-12-27T17:16:39 A 5.00e+01 1.70e+00 1999-12-27T17:16:39 A 1.79e+02 4.29e+00 1999-12-27T17:16:39 A 3.60e+02 2.63e+01 2004-02-11T00:28:52.750 K 3.18e+02 2.47e+01 2004-02-11T00:28:52.750 A 3.61e+02 1.78e+01 2004-02-11T00:28:52.750 A A 7 1.77 1.5 1.5 -45.0 4.85e+02 2.53e+01 2004-02-11T00:28:52.750 A 4.31e+02 2.49e+01 2004-02-11T00:28:52.750 A 4.93e+02 3.12e+01 2004-02-11T00:28:52.750 K A 7 2.27 1.7 1.7 -45.0 5.74e+02 2.83e+01 2004-02-11T00:28:52.750 A 5.53e+02 2.92e+01 2004-02-11T00:28:52.750 A 5.82e+02 2.77e+01 2004-02-11T00:28:52.750 A A 9 2.54 1.8 1.8 -45.0 7.17e+02 3.56e+01 2004-02-11T00:28:52.750 A 7.50e+02 4.00e+01 2004-02-11T00:28:52.750 A 7.01e+02 3.99e+01 2004-02-11T00:28:52.750 A A 7 4.98 1.8 1.8 -45.0 9.93e+02 9.23e+01 2004-09-18T16:14:42.183 A 9.38e+02 8.76e+01 2004-09-18T22:34:28.703 A 9.47e+02 8.82e+01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 1.60e+03 1.58e+02 2004-09-19T08:53:04.473 A Q 0 0.00 0.0 0.0 0.0 - SSTc2d J034402.4+320205 056.0100101 1.09e-06 +32.0346969 1.09e-06 A A null -3.05 1.53e+00 5.79e-02 1.28e+01 5 YSOc_red -999.00 -999.00 -999.00 -999.0000 -999.00 0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 6.09e-01 5.68e-02 2004-02-11T00:28:52.750 A 6.32e-01 6.12e-02 2004-02-11T00:28:52.750 A 7.33e-01 4.37e-02 2004-02-11T00:28:52.750 A A 2 8.46 5.4 2.0 -86.4 1.88e+00 1.35e-01 2004-02-11T00:28:52.750 A 2.11e+00 1.54e-01 2004-02-11T00:28:52.750 A 1.93e+00 1.13e-01 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 3.43e+00 1.95e-01 2004-02-11T00:28:52.750 A 3.82e+00 2.19e-01 2004-02-11T00:28:52.750 A 3.82e+00 1.88e-01 2004-02-11T00:28:52.750 A A 1 2.54 1.8 1.8 -45.0 6.48e+00 3.34e-01 2004-02-11T00:28:52.750 A 6.32e+00 3.23e-01 2004-02-11T00:28:52.750 A 6.41e+00 3.16e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 1.03e+02 9.52e+00 2004-09-18T16:14:42.183 A 1.05e+02 1.07e+01 2004-09-18T22:34:28.703 A 1.07e+02 1.04e+01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 8.47e+02 9.06e+01 2004-09-19T08:53:04.473 P Q 0 0.00 0.0 0.0 0.0 - SSTc2d J034402.6+320160 056.0109606 2.83e-08 +32.0332027 2.83e-08 A A null -2.79 1.12e+00 5.90e-02 3.73e+01 5 YSOc_red -999.00 -999.00 -999.00 -999.0000 -999.00 0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 1.11e+00 7.49e-02 2004-02-11T00:28:52.750 A 1.05e+00 6.39e-02 2004-02-11T00:28:52.750 A 8.14e-01 4.28e-02 2004-02-11T00:28:52.750 A A 1 1.77 1.5 1.5 -45.0 2.93e+00 2.00e-01 2004-02-11T00:28:52.750 A 2.58e+00 1.86e-01 2004-02-11T00:28:52.750 A 2.83e+00 1.50e-01 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 5.01e+00 2.73e-01 2004-02-11T00:28:52.750 A 4.39e+00 2.45e-01 2004-02-11T00:28:52.750 A 3.99e+00 1.94e-01 2004-02-11T00:28:52.750 A A 1 2.54 1.8 1.8 -45.0 6.24e+00 3.14e-01 2004-02-11T00:28:52.750 A 5.81e+00 3.02e-01 2004-02-11T00:28:52.750 A 6.06e+00 2.95e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 5.17e+01 4.85e+00 2004-09-18T16:14:42.183 A -9.99e+02 -9.99e+02 null U 5.45e+01 5.94e+00 2004-09-18T19:24:35.443 A Q -2 8.04 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 0.00e+00 0.00e+00 undetected Y Q 0 0.00 0.0 0.0 0.0 - SSTc2d J034409.2+320238 056.0383460 4.75e-07 +32.0438395 4.75e-07 A A 03440920+3202376 -5.00 6.90e-01 4.82e-02 1.10e+01 6 YSOc_star+dust(IR1) 14.99 2.45 11.53 0.3660 0.90 3 7.76e-01 6.72e-02 1998-10-05T21:29:14 A 2.18e+00 1.38e-01 1998-10-05T21:29:14 A 3.57e+00 1.45e-01 1998-10-05T21:29:14 A 1.89e+01 9.31e-01 2004-02-11T00:28:52.750 A 1.64e+01 8.79e-01 2004-02-11T00:28:52.750 A 1.23e+01 5.93e-01 2004-02-11T00:28:52.750 A A 1 1.77 1.5 1.5 -45.0 2.61e+01 1.28e+00 2004-02-11T00:28:52.750 A 2.34e+01 1.17e+00 2004-02-11T00:28:52.750 A 2.46e+01 1.19e+00 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 3.37e+01 1.63e+00 2004-02-11T00:28:52.750 A 3.15e+01 1.55e+00 2004-02-11T00:28:52.750 A 2.84e+01 1.33e+00 2004-02-11T00:28:52.750 A A 1 2.54 1.8 1.8 -45.0 4.75e+01 2.28e+00 2004-02-11T00:28:52.750 A 4.81e+01 2.39e+00 2004-02-11T00:28:52.750 A 4.84e+01 2.31e+00 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 2.74e+02 2.57e+01 2004-09-18T16:14:42.183 A 2.92e+02 2.70e+01 2004-09-18T22:34:28.703 A 2.83e+02 2.63e+01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 2.65e+02 5.65e+01 2004-09-19T08:53:04.473 P Q 0 0.00 0.0 0.0 0.0 - SSTc2d J034413.0+320135 056.0540738 6.94e-07 +32.0265247 6.94e-07 A A 03441297+3201354 -5.00 3.70e-01 4.96e-02 3.66e+01 6 YSOc -999.00 -999.00 -999.00 -999.0000 -999.00 0 1.47e+00 7.97e-02 1998-10-05T21:29:14 A 1.09e+01 3.01e-01 1998-10-05T21:29:14 A 6.47e+01 1.25e+00 1998-10-05T21:29:14 A 1.90e+02 9.93e+00 2004-02-11T00:28:52.750 A 1.88e+02 1.44e+01 2004-02-11T00:28:52.750 A 1.48e+02 7.42e+00 2004-02-11T00:28:52.750 K A 7 1.77 1.5 1.5 -45.0 4.15e+02 2.72e+01 2004-02-11T00:28:52.750 K 4.05e+02 2.11e+01 2004-02-11T00:28:52.750 A 4.36e+02 2.92e+01 2004-02-11T00:28:52.750 K A 7 2.27 1.7 1.7 -45.0 6.03e+02 2.95e+01 2004-02-11T00:28:52.750 A 5.91e+02 3.53e+01 2004-02-11T00:28:52.750 A 4.89e+02 2.34e+01 2004-02-11T00:28:52.750 A A 7 2.54 1.8 1.8 -45.0 6.52e+02 3.29e+01 2004-02-11T00:28:52.750 A 6.44e+02 3.57e+01 2004-02-11T00:28:52.750 A 6.04e+02 3.53e+01 2004-02-11T00:28:52.750 A A 7 4.98 1.8 1.8 -45.0 1.73e+03 1.61e+02 2004-09-18T16:14:42.183 A 1.67e+03 1.56e+02 2004-09-18T22:34:28.703 A 1.61e+03 1.51e+02 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 4.59e+03 4.40e+02 2004-09-19T08:53:04.473 P Q 0 0.00 0.0 0.0 0.0 - SSTc2d J034421.4+315933 056.0889634 3.71e-07 +31.9923811 3.71e-07 A A 03442135+3159327 -5.00 2.10e-01 4.76e-02 3.01e+01 6 YSOc_red -999.00 -999.00 -999.00 -999.0000 -999.00 0 3.29e-01 -9.99e+02 1998-10-05T21:29:14 U 1.17e+00 9.13e-02 1998-10-05T21:29:14 A 6.63e+00 1.83e-01 1998-10-05T21:29:14 A 3.97e+01 1.99e+00 2004-02-11T00:28:52.750 A 3.96e+01 2.09e+00 2004-02-11T00:28:52.750 A 3.10e+01 1.47e+00 2004-02-11T00:28:52.750 A A 1 1.77 1.5 1.5 -45.0 5.92e+01 2.88e+00 2004-02-11T00:28:52.750 A 6.09e+01 3.16e+00 2004-02-11T00:28:52.750 A 5.04e+01 2.36e+00 2004-02-11T00:28:52.750 A A 1 2.27 1.7 1.7 -45.0 7.29e+01 3.49e+00 2004-02-11T00:28:52.750 A 7.53e+01 3.59e+00 2004-02-11T00:28:52.750 A 6.17e+01 2.87e+00 2004-02-11T00:28:52.750 A A 1 2.54 1.8 1.8 -45.0 8.78e+01 4.40e+00 2004-02-11T00:28:52.750 A 7.86e+01 3.93e+00 2004-02-11T00:28:52.750 A 7.09e+01 3.32e+00 2004-02-11T00:28:52.750 A A 1 2.54 1.8 1.8 -45.0 2.47e+02 2.28e+01 2004-09-18T16:14:42.183 A 2.17e+02 2.03e+01 2004-09-18T22:34:28.703 A 2.28e+02 2.12e+01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 6.00e+02 6.14e+01 2004-09-19T08:53:04.473 A Q 0 0.00 0.0 0.0 0.0 - SSTc2d J034513.8+321210 056.3075775 6.55e-07 +32.2027786 6.55e-07 A A 03451380+3212098 -2.78 7.60e-01 4.94e-02 1.48e+01 6 YSOc_red -999.00 -999.00 -999.00 -999.0000 -999.00 0 3.40e-01 5.11e-02 1998-11-21T20:13:38 C 8.60e-01 8.00e-02 1998-11-21T20:13:38 A 1.77e+00 9.14e-02 1998-11-21T20:13:38 A 2.02e+00 1.06e-01 2004-02-11T00:28:52.750 A 1.96e+00 1.01e-01 2004-02-11T00:28:52.750 A 1.95e+00 1.04e-01 2004-02-11T00:28:52.750 A A 1 3.09 1.5 1.5 -45.0 3.62e+00 1.81e-01 2004-02-11T00:28:52.750 A 3.34e+00 1.70e-01 2004-02-11T00:28:52.750 A 3.46e+00 1.72e-01 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 6.57e+00 3.34e-01 2004-02-11T00:28:52.750 A 6.13e+00 3.10e-01 2004-02-11T00:28:52.750 A 6.37e+00 3.13e-01 2004-02-11T00:28:52.750 A A 1 3.92 1.8 1.8 -45.0 1.25e+01 6.08e-01 2004-02-11T00:28:52.750 A 1.11e+01 5.58e-01 2004-02-11T00:28:52.750 A 1.17e+01 5.68e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 5.92e+01 5.53e+00 2004-09-18T16:14:42.183 A 6.10e+01 5.65e+00 2004-09-18T22:34:28.703 A 6.04e+01 5.60e+00 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 2.82e+02 3.43e+01 2004-09-19T08:53:04.473 P Q 0 0.00 0.0 0.0 0.0 - SSTc2d J034516.3+320620 056.3180895 7.00e-07 +32.1055863 7.00e-07 A A 03451634+3206199 -5.00 -4.30e-01 4.87e-02 2.78e+00 6 YSOc_star+dust(IR1) 14.00 2.36 8.05 0.3540 1.12 3 2.50e+01 4.83e-01 1998-01-25T13:52:19 A 6.20e+01 1.26e+00 1998-01-25T13:52:19 A 9.73e+01 1.52e+00 1998-01-25T13:52:19 A 1.64e+02 9.32e+00 2004-02-11T00:28:52.750 A 1.53e+02 8.25e+00 2004-02-11T00:28:52.750 A 1.40e+02 6.82e+00 2004-02-11T00:28:52.750 A A 7 1.77 1.5 1.5 -45.0 1.95e+02 1.06e+01 2004-02-11T00:28:52.750 A 1.65e+02 9.84e+00 2004-02-11T00:28:52.750 A 1.83e+02 9.72e+00 2004-02-11T00:28:52.750 A B 1 2.97 1.7 1.7 -45.0 2.02e+02 1.07e+01 2004-02-11T00:28:52.750 A 1.83e+02 1.01e+01 2004-02-11T00:28:52.750 A 1.80e+02 8.53e+00 2004-02-11T00:28:52.750 A A 2 4.59 2.1 2.7 -52.6 2.55e+02 1.30e+01 2004-02-11T00:28:52.750 A 2.41e+02 1.53e+01 2004-02-11T00:28:52.750 A 2.52e+02 1.44e+01 2004-02-11T00:28:52.750 A A 7 4.98 1.8 1.8 -45.0 3.68e+02 3.40e+01 2004-09-18T16:14:42.183 A 3.70e+02 3.42e+01 2004-09-18T22:34:28.703 A 3.73e+02 3.45e+01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 4.45e+02 5.29e+01 2004-09-19T08:53:04.473 A Q 0 0.00 0.0 0.0 0.0 - SSTc2d J034520.5+320634 056.3352517 5.01e-07 +32.1095779 5.01e-07 A A 03452046+3206344 -5.00 -8.30e-01 4.82e-02 2.00e+01 6 YSOc_star+dust(IR2) 11.18 1.40 8.00 0.1600 0.55 4 5.70e+01 1.10e+00 1998-01-25T13:52:19 A 1.04e+02 2.11e+00 1998-01-25T13:52:19 A 1.22e+02 1.90e+00 1998-01-25T13:52:19 A 1.09e+02 5.65e+00 2004-02-11T00:28:52.750 A 9.12e+01 5.27e+00 2004-02-11T00:28:52.750 A 9.21e+01 4.63e+00 2004-02-11T00:28:52.750 A B 1 1.77 1.5 1.5 -45.0 9.21e+01 4.61e+00 2004-02-11T00:28:52.750 A 8.49e+01 4.77e+00 2004-02-11T00:28:52.750 A 8.85e+01 4.54e+00 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 8.21e+01 3.96e+00 2004-02-11T00:28:52.750 A 7.89e+01 3.76e+00 2004-02-11T00:28:52.750 A 7.42e+01 3.47e+00 2004-02-11T00:28:52.750 A A 1 2.54 1.8 1.8 -45.0 8.44e+01 4.23e+00 2004-02-11T00:28:52.750 A 8.36e+01 4.14e+00 2004-02-11T00:28:52.750 A 8.31e+01 4.11e+00 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 2.00e+02 1.86e+01 2004-09-18T16:14:42.183 A 2.07e+02 1.91e+01 2004-09-18T22:34:28.703 A 2.04e+02 1.89e+01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 3.57e+02 3.69e+01 2004-09-19T08:53:04.473 A Q 0 0.00 0.0 0.0 0.0 - SSTc2d J034525.1+320930 056.3547902 4.85e-07 +32.1584235 4.85e-07 A A 03452514+3209301 -5.00 -5.60e-01 4.76e-02 2.29e+01 6 YSOc_star+dust(IR2) 10.88 1.39 9.17 0.1570 1.74 4 2.42e+01 4.68e-01 1998-01-25T13:52:11 A 3.68e+01 7.45e-01 1998-01-25T13:52:11 A 3.88e+01 6.07e-01 1998-01-25T13:52:11 A 2.96e+01 1.58e+00 2004-02-11T00:28:52.750 A 3.22e+01 1.68e+00 2004-02-11T00:28:52.750 A 3.26e+01 1.54e+00 2004-02-11T00:28:52.750 A A 1 1.77 1.5 1.5 -45.0 2.75e+01 1.36e+00 2004-02-11T00:28:52.750 A 3.06e+01 1.51e+00 2004-02-11T00:28:52.750 A 2.89e+01 1.44e+00 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 2.53e+01 1.21e+00 2004-02-11T00:28:52.750 A 2.79e+01 1.37e+00 2004-02-11T00:28:52.750 A 2.69e+01 1.26e+00 2004-02-11T00:28:52.750 A A 1 2.54 1.8 1.8 -45.0 3.83e+01 1.87e+00 2004-02-11T00:28:52.750 A 4.21e+01 2.07e+00 2004-02-11T00:28:52.750 A 4.03e+01 1.94e+00 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 1.13e+02 1.05e+01 2004-09-18T16:14:42.183 A 1.05e+02 9.79e+00 2004-09-18T22:34:28.703 A 1.09e+02 1.01e+01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 8.95e+01 1.56e+01 2004-09-19T08:53:04.473 P Q 0 0.00 0.0 0.0 0.0 - SSTc2d J034548.3+322412 056.4511053 9.13e-07 +32.4033447 9.13e-07 A A 03454828+3224118 -5.00 -7.20e-01 5.03e-02 2.47e+01 6 YSOc_red -999.00 -999.00 -999.00 -999.0000 -999.00 0 4.70e+02 1.39e+01 1998-01-25T13:52:02 A 6.97e+02 1.99e+01 1998-01-25T13:52:02 A 1.03e+03 1.99e+01 1998-01-25T13:52:02 A 9.78e+02 5.63e+01 2004-02-11T00:28:52.750 A 9.13e+02 6.31e+01 2004-02-11T00:28:52.750 A 9.52e+02 6.03e+01 2004-02-11T00:28:52.750 A A 7 3.09 1.5 1.5 -45.0 9.02e+02 5.40e+01 2004-02-11T00:28:52.750 A 8.83e+02 5.24e+01 2004-02-11T00:28:52.750 A 8.85e+02 5.20e+01 2004-02-11T00:28:52.750 A A 7 2.97 1.7 1.7 -45.0 8.04e+02 4.03e+01 2004-02-11T00:28:52.750 A 7.66e+02 4.90e+01 2004-02-11T00:28:52.750 A 7.52e+02 4.73e+01 2004-02-11T00:28:52.750 A A 7 3.92 1.8 1.8 -45.0 7.35e+02 3.86e+01 2004-02-11T00:28:52.750 A 7.56e+02 3.86e+01 2004-02-11T00:28:52.750 A 7.67e+02 3.79e+01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 2.45e+03 2.37e+02 2004-09-18T16:14:42.183 A 3.00e+03 2.81e+02 2004-09-18T22:34:28.703 A 2.63e+03 2.48e+02 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 5.39e+03 5.05e+02 2004-09-19T08:53:04.473 P Q 0 0.00 0.0 0.0 0.0 - SSTc2d J034705.4+324309 056.7726369 6.18e-07 +32.7190344 6.18e-07 A A 03470544+3243084 -5.00 3.60e-01 4.84e-02 3.04e+01 6 YSOc_red -999.00 -999.00 -999.00 -999.0000 -999.00 0 4.34e-01 6.08e-02 1998-01-25T14:13:12 B 2.02e+00 1.10e-01 1998-01-25T14:13:12 A 1.01e+01 2.23e-01 1998-01-25T14:13:12 A 6.02e+01 3.04e+00 2004-02-11T00:28:52.750 A 6.10e+01 3.13e+00 2004-02-11T00:28:52.750 A 6.24e+01 3.08e+00 2004-02-11T00:28:52.750 A A 7 3.09 1.5 1.5 -45.0 8.25e+01 4.35e+00 2004-02-11T00:28:52.750 A 8.28e+01 4.39e+00 2004-02-11T00:28:52.750 A 8.41e+01 4.24e+00 2004-02-11T00:28:52.750 A A 7 2.97 1.7 1.7 -45.0 1.02e+02 5.02e+00 2004-02-11T00:28:52.750 A 1.04e+02 5.02e+00 2004-02-11T00:28:52.750 A 1.03e+02 4.92e+00 2004-02-11T00:28:52.750 A A 1 3.92 1.8 1.8 -45.0 1.28e+02 6.49e+00 2004-02-11T00:28:52.750 A 1.28e+02 6.32e+00 2004-02-11T00:28:52.750 A 1.31e+02 6.70e+00 2004-02-11T00:28:52.750 A A 2 8.54 2.9 2.6 -53.4 6.12e+02 5.67e+01 2004-09-18T16:14:42.183 A 6.00e+02 5.57e+01 2004-09-18T22:34:28.703 A 5.95e+02 5.54e+01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 9.14e+02 9.16e+01 2004-09-19T08:53:04.473 P Q 0 0.00 0.0 0.0 0.0 - SSTc2d J034741.6+325144 056.9232658 2.88e-09 +32.8622382 2.88e-09 A A 03474160+3251437 -5.00 7.80e-01 5.13e-02 1.57e+02 6 YSOc -999.00 -999.00 -999.00 -999.0000 -999.00 0 1.84e-01 4.36e-02 1998-01-25T14:20:24 D 2.46e+00 9.07e-02 1998-01-25T14:20:24 A 2.18e+01 4.01e-01 1998-01-25T14:20:24 A 4.02e+02 2.14e+01 2004-02-11T00:28:52.750 A 4.06e+02 2.19e+01 2004-02-11T00:28:52.750 A 3.87e+02 2.76e+01 2004-02-11T00:28:52.750 A A -2 3.09 1.5 1.5 -45.0 7.89e+02 4.50e+01 2004-02-11T00:28:52.750 A 8.08e+02 4.45e+01 2004-02-11T00:28:52.750 A 8.09e+02 4.44e+01 2004-02-11T00:28:52.750 A A 7 2.97 1.7 1.7 -45.0 1.34e+03 6.83e+01 2004-02-11T00:28:52.750 A 1.35e+03 7.00e+01 2004-02-11T00:28:52.750 A 1.38e+03 6.90e+01 2004-02-11T00:28:52.750 A A 7 3.92 1.8 1.8 -45.0 1.63e+03 8.55e+01 2004-02-11T00:28:52.750 A 1.70e+03 1.01e+02 2004-02-11T00:28:52.750 A 1.74e+03 1.02e+02 2004-02-11T00:28:52.750 A A 2 14.25 3.7 3.4 -67.0 3.37e+03 3.18e+02 2004-09-18T16:14:42.183 A 3.39e+03 3.19e+02 2004-09-18T22:34:28.703 A 3.40e+03 3.17e+02 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 1.08e+04 1.07e+03 2004-09-19T08:53:04.473 A Q 0 0.00 0.0 0.0 0.0 - SSTc2d J032519.5+303424 051.3313375 5.88e-07 +30.5733784 5.88e-07 A A 03251953+3034242 -2.19 -8.00e-02 5.01e-02 9.90e+00 6 YSOc -999.00 -999.00 -999.00 -999.0000 -999.00 0 2.90e-01 4.24e-02 1998-01-20T15:45:22 C 3.80e-01 6.82e-02 1998-01-20T15:45:22 C 7.81e-01 6.69e-02 1998-01-20T15:45:22 A 2.48e+00 1.30e-01 2004-09-07T10:34:29.464 A 2.49e+00 1.25e-01 2004-09-07T10:34:29.464 A 2.49e+00 1.23e-01 2004-09-07T10:34:29.464 A A 1 3.09 1.5 1.5 -45.0 3.27e+00 1.62e-01 2004-09-07T10:34:29.464 A 3.26e+00 1.62e-01 2004-09-07T10:34:29.464 A 3.27e+00 1.58e-01 2004-09-07T10:34:29.464 A A 1 2.97 1.7 1.7 -45.0 4.16e+00 2.25e-01 2004-09-07T10:34:29.464 A 4.21e+00 2.17e-01 2004-09-07T10:34:29.464 A 4.17e+00 2.11e-01 2004-09-07T10:34:29.464 A A 1 3.92 1.8 1.8 -45.0 5.33e+00 2.67e-01 2004-09-07T10:34:29.464 A 5.31e+00 2.70e-01 2004-09-07T10:34:29.464 A 5.33e+00 2.58e-01 2004-09-07T10:34:29.464 A A 1 4.98 1.8 1.8 -45.0 1.09e+01 1.03e+00 2004-09-19T22:12:44.497 A 1.08e+01 1.03e+00 2004-09-20T03:00:42.863 A 1.09e+01 1.02e+00 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032537.9+304424 051.4077607 9.84e-09 +30.7399255 9.84e-09 A A null -1.57 1.41e+00 1.48e-01 3.43e+00 5 YSOc_red -999.00 -999.00 -999.00 -999.0000 -999.00 0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 1.22e-01 2.79e-02 2004-09-07T10:34:29.464 C Q -2 3.09 1.5 1.5 -45.0 1.91e-01 3.12e-02 2004-09-07T10:34:29.464 B 1.78e-01 2.97e-02 2004-09-07T10:34:29.464 B 1.90e-01 2.50e-02 2004-09-07T10:34:29.464 A A 7 2.97 1.7 1.7 -45.0 3.27e-01 4.69e-02 2004-09-07T10:34:29.464 B 2.71e-01 4.50e-02 2004-09-07T10:34:29.464 B 2.64e-01 3.10e-02 2004-09-07T10:34:29.464 A A 7 3.92 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 3.29e-01 9.26e-02 2004-09-07T10:34:29.464 C Q -2 4.98 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 1.23e+01 2.86e+00 2004-09-20T12:36:43.680 C Q -2 8.04 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032738.6+301336 051.9109468 1.44e-08 +30.2267923 1.44e-08 A A 03273864+3013364 -1.54 -8.60e-01 7.46e-02 2.17e+00 6 YSOc_star+dust(IR3) 24.23 1.86 11.79 0.1590 1.45 4 9.35e-02 -9.99e+02 1999-11-26T20:10:19 U 5.31e-01 6.55e-02 1999-11-26T20:10:19 B 9.28e-01 7.26e-02 1999-11-26T20:10:19 A 1.09e+00 5.79e-02 2004-09-07T10:34:29.464 A 1.15e+00 6.07e-02 2004-09-07T10:34:29.464 A 1.12e+00 5.68e-02 2004-09-07T10:34:29.464 A A 1 3.09 1.5 1.5 -45.0 1.09e+00 5.98e-02 2004-09-07T10:34:29.464 A 1.07e+00 5.97e-02 2004-09-07T10:34:29.464 A 1.08e+00 5.61e-02 2004-09-07T10:34:29.464 A A 1 2.97 1.7 1.7 -45.0 1.06e+00 7.90e-02 2004-09-07T10:34:29.464 A 1.11e+00 7.76e-02 2004-09-07T10:34:29.464 A 1.07e+00 6.75e-02 2004-09-07T10:34:29.464 A A 1 3.92 1.8 1.8 -45.0 1.15e+00 7.98e-02 2004-09-07T10:34:29.464 A 1.08e+00 7.56e-02 2004-09-07T10:34:29.464 A 1.10e+00 6.72e-02 2004-09-07T10:34:29.464 A A 1 4.98 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 2.54e+00 6.66e-01 2004-09-20T12:36:43.680 C Q -2 8.04 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032759.9+300856 051.9995979 1.11e-08 +30.1487744 1.11e-08 A A 03275992+3008556 -5.00 -1.05e+00 1.16e-01 2.36e+01 6 YSOc_star+dust(MP1) 13.99 2.05 13.38 0.1600 1.17 6 3.44e-01 -9.99e+02 1999-11-26T20:11:28 U 4.70e-01 8.66e-02 1999-11-26T20:11:28 C 7.76e-01 8.51e-02 1999-11-26T20:11:28 B 4.53e-01 4.55e-02 2004-09-07T10:34:29.464 A 4.68e-01 5.10e-02 2004-09-07T10:34:29.464 A 4.56e-01 3.88e-02 2004-09-07T10:34:29.464 A A 1 3.09 1.5 1.5 -45.0 4.17e-01 2.90e-02 2004-09-07T10:34:29.464 A 4.35e-01 2.92e-02 2004-09-07T10:34:29.464 A 4.29e-01 2.51e-02 2004-09-07T10:34:29.464 A A 1 2.97 1.7 1.7 -45.0 2.73e-01 5.17e-02 2004-09-07T10:34:29.464 B 2.43e-01 5.50e-02 2004-09-07T10:34:29.464 C 2.57e-01 3.89e-02 2004-09-07T10:34:29.464 B A 1 3.92 1.8 1.8 -45.0 1.79e-01 4.65e-02 2004-09-07T10:34:29.464 C -9.99e+02 -9.99e+02 null U 1.49e-01 3.07e-02 2004-09-07T10:34:29.464 C Q 9 4.98 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 2.78e+00 8.05e-01 2004-09-20T12:36:43.680 C Q -2 8.04 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032834.9+305455 052.1455678 5.55e-07 +30.9151510 5.55e-07 A A 03283494+3054545 -1.96 6.00e-02 5.12e-02 6.28e+00 6 YSOc -999.00 -999.00 -999.00 -999.0000 -999.00 0 1.04e-01 -9.99e+02 2000-10-01T20:40:08 U 2.41e-01 -9.99e+02 2000-10-01T20:40:08 U 6.56e-01 8.52e-02 2000-10-01T20:40:08 C 1.85e+00 9.01e-02 2004-09-07T10:34:29.464 A 1.87e+00 9.31e-02 2004-09-07T10:34:29.464 A 1.87e+00 8.99e-02 2004-09-07T10:34:29.464 A A 1 3.09 1.5 1.5 -45.0 2.61e+00 1.35e-01 2004-09-07T10:34:29.464 A 2.63e+00 1.34e-01 2004-09-07T10:34:29.464 A 2.62e+00 1.30e-01 2004-09-07T10:34:29.464 A A 1 2.97 1.7 1.7 -45.0 3.67e+00 1.84e-01 2004-09-07T10:34:29.464 A 3.62e+00 1.81e-01 2004-09-07T10:34:29.464 A 3.65e+00 1.77e-01 2004-09-07T10:34:29.464 A A 1 3.92 1.8 1.8 -45.0 4.68e+00 2.34e-01 2004-09-07T10:34:29.464 A 4.95e+00 2.49e-01 2004-09-07T10:34:29.464 A 4.78e+00 2.32e-01 2004-09-07T10:34:29.464 A A 1 4.98 1.8 1.8 -45.0 1.13e+01 1.07e+00 2004-09-19T22:12:44.497 A 1.15e+01 1.11e+00 2004-09-20T03:00:42.863 A 1.16e+01 1.09e+00 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032838.8+311807 052.1615814 5.54e-07 +31.3018271 5.54e-07 A A 03283875+3118068 -4.10 -1.80e-01 4.97e-02 1.55e+01 6 YSOc_star+dust(IR2) 43.32 2.70 9.09 0.2410 0.11 3 7.82e-02 -9.99e+02 2000-10-01T20:39:42 U 3.48e-01 8.29e-02 2000-10-01T20:39:42 D 1.66e+00 1.07e-01 2000-10-01T20:39:42 A 5.22e+00 2.58e-01 2004-02-10T08:34:30.264 A 4.24e+00 2.11e-01 2004-02-10T08:34:30.264 A 4.50e+00 2.28e-01 2004-02-10T08:34:30.264 A B 1 3.09 1.5 1.5 -45.0 8.77e+00 4.33e-01 2004-02-10T08:34:30.264 A 6.26e+00 3.17e-01 2004-02-10T08:34:30.264 A 7.03e+00 3.67e-01 2004-02-10T08:34:30.264 A C 1 2.97 1.7 1.7 -45.0 1.03e+01 5.01e-01 2004-02-10T08:34:30.264 A 7.30e+00 3.51e-01 2004-02-10T08:34:30.264 A 8.10e+00 3.99e-01 2004-02-10T08:34:30.264 A C 1 3.92 1.8 1.8 -45.0 1.10e+01 5.39e-01 2004-02-10T08:34:30.264 A 1.06e+01 6.91e-01 2004-02-10T08:34:30.264 A 9.75e+00 4.76e-01 2004-02-10T08:34:30.264 A A 1 4.98 1.8 1.8 -45.0 1.58e+01 1.48e+00 2004-09-19T22:12:44.497 A 1.71e+01 1.61e+00 2004-09-20T03:00:42.863 A 1.64e+01 1.53e+00 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032842.4+302953 052.1767252 4.92e-07 +30.4981030 4.92e-07 A A 03284242+3029530 -5.00 -8.90e-01 4.79e-02 4.80e+00 6 YSOc_star+dust(IR2) 8.72 1.39 9.41 0.1590 2.17 4 3.29e+01 6.37e-01 2000-10-01T20:40:34 A 4.21e+01 8.93e-01 2000-10-01T20:40:34 A 3.73e+01 7.89e-01 2000-10-01T20:40:34 A 3.02e+01 1.52e+00 2004-09-07T10:34:29.464 A 3.01e+01 1.57e+00 2004-09-07T10:34:29.464 A 3.03e+01 1.50e+00 2004-09-07T10:34:29.464 A A 1 3.09 1.5 1.5 -45.0 2.79e+01 1.40e+00 2004-09-07T10:34:29.464 A 2.83e+01 1.56e+00 2004-09-07T10:34:29.464 A 2.81e+01 1.42e+00 2004-09-07T10:34:29.464 A A 1 2.97 1.7 1.7 -45.0 2.78e+01 1.36e+00 2004-09-07T10:34:29.464 A 2.70e+01 1.32e+00 2004-09-07T10:34:29.464 A 2.74e+01 1.31e+00 2004-09-07T10:34:29.464 A A 1 3.92 1.8 1.8 -45.0 3.43e+01 1.66e+00 2004-09-07T10:34:29.464 A 3.37e+01 1.65e+00 2004-09-07T10:34:29.464 A 3.40e+01 1.62e+00 2004-09-07T10:34:29.464 A A 1 4.98 1.8 1.8 -45.0 4.01e+01 3.71e+00 2004-09-19T22:12:44.497 A 4.14e+01 3.84e+00 2004-09-20T03:00:42.863 A 4.10e+01 3.79e+00 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032843.2+311043 052.1801622 5.13e-07 +31.1785147 5.13e-07 A A 03284323+3110425 -5.00 -1.47e+00 4.98e-02 2.08e+00 6 YSOc_star+dust(IR4) 21.06 1.14 9.62 0.1050 0.63 6 1.13e+00 6.99e-02 2000-10-01T20:39:50 A 5.31e+00 1.76e-01 2000-10-01T20:39:50 A 1.03e+01 2.45e-01 2000-10-01T20:39:50 A 9.58e+00 4.75e-01 2004-02-10T08:34:30.264 A 1.02e+01 5.20e-01 2004-02-10T08:34:30.264 A 1.03e+01 4.93e-01 2005-09-16T09:56:12.848 A A 1 1.77 1.5 1.5 -45.0 7.96e+00 4.00e-01 2004-02-10T08:34:30.264 A 8.79e+00 4.37e-01 2004-02-10T08:34:30.264 A 8.30e+00 4.17e-01 2004-02-10T08:34:30.264 A A 1 2.97 1.7 1.7 -45.0 7.02e+00 3.51e-01 2004-02-10T08:34:30.264 A 7.58e+00 3.80e-01 2004-02-10T08:34:30.264 A 7.60e+00 3.60e-01 2005-09-16T09:56:12.848 A A 1 2.54 1.8 1.8 -45.0 5.86e+00 2.90e-01 2004-02-10T08:34:30.264 A 6.10e+00 3.03e-01 2004-02-10T08:34:30.264 A 6.00e+00 2.88e-01 2004-02-10T08:34:30.264 A A 1 4.98 1.8 1.8 -45.0 4.15e+00 5.15e-01 2004-09-19T22:12:44.497 A 4.13e+00 4.42e-01 2004-09-20T03:00:42.863 A 4.21e+00 4.31e-01 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032844.1+312053 052.1837167 6.39e-07 +31.3479850 6.39e-07 A A 03284407+3120528 -5.00 -1.12e+00 5.04e-02 3.22e+00 6 YSOc_star+dust(IR2) 11.49 1.41 11.18 0.1620 1.37 4 3.20e+00 1.00e-01 2000-10-01T20:39:42 A 5.20e+00 1.73e-01 2000-10-01T20:39:42 A 5.93e+00 1.64e-01 2000-10-01T20:39:42 A 5.13e+00 2.51e-01 2004-02-10T08:34:30.264 A 5.11e+00 2.59e-01 2004-02-10T08:34:30.264 A 4.91e+00 2.61e-01 2004-02-10T08:34:30.264 A A 1 3.09 1.5 1.5 -45.0 4.21e+00 2.13e-01 2004-02-10T08:34:30.264 A 4.28e+00 2.16e-01 2004-02-10T08:34:30.264 A 4.21e+00 2.14e-01 2004-02-10T08:34:30.264 A A 1 2.97 1.7 1.7 -45.0 3.95e+00 2.07e-01 2004-02-10T08:34:30.264 A 3.83e+00 1.95e-01 2004-02-10T08:34:30.264 A 3.90e+00 1.94e-01 2004-02-10T08:34:30.264 A A 1 3.92 1.8 1.8 -45.0 4.25e+00 2.15e-01 2004-02-10T08:34:30.264 A 4.20e+00 2.12e-01 2004-02-10T08:34:30.264 A 4.22e+00 2.06e-01 2004-02-10T08:34:30.264 A A 1 4.98 1.8 1.8 -45.0 4.55e+00 5.10e-01 2004-09-19T22:12:44.497 A 4.16e+00 4.38e-01 2004-09-20T03:00:42.863 A 4.32e+00 4.34e-01 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032846.2+311638 052.1925226 5.48e-07 +31.2773430 5.48e-07 A A 03284618+3116385 -5.00 -1.65e+00 5.13e-02 3.97e+00 6 YSOc_star+dust(IR3) 6.07 1.24 8.94 0.1280 1.80 5 7.12e+01 1.38e+00 2000-10-01T20:39:50 A 1.02e+02 1.97e+00 2000-10-01T20:39:50 A 8.87e+01 1.63e+00 2000-10-01T20:39:50 A 5.32e+01 2.60e+00 2004-02-10T08:34:30.264 A 5.04e+01 2.57e+00 2004-02-10T08:34:30.264 A 4.45e+01 2.72e+00 2004-02-10T08:34:30.264 A A 1 3.09 1.5 1.5 -45.0 3.99e+01 1.98e+00 2004-02-10T08:34:30.264 A 3.93e+01 2.04e+00 2004-02-10T08:34:30.264 A 3.91e+01 1.98e+00 2004-02-10T08:34:30.264 A A 1 2.97 1.7 1.7 -45.0 3.52e+01 1.71e+00 2004-02-10T08:34:30.264 A 3.54e+01 1.78e+00 2004-02-10T08:34:30.264 A 3.52e+01 1.70e+00 2004-02-10T08:34:30.264 A A 1 3.92 1.8 1.8 -45.0 3.36e+01 1.61e+00 2004-02-10T08:34:30.264 A 3.20e+01 1.67e+00 2004-02-10T08:34:30.264 A 3.25e+01 1.60e+00 2004-02-10T08:34:30.264 A A 1 4.98 1.8 1.8 -45.0 1.35e+01 1.45e+00 2004-09-19T22:12:44.497 A -9.99e+02 -9.99e+02 null U 1.36e+01 1.38e+00 2004-09-20T12:36:43.680 A Q 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032847.6+312406 052.1985349 6.18e-07 +31.4016777 6.18e-07 A A 03284764+3124061 -5.00 -1.10e+00 4.92e-02 5.84e+00 6 YSOc_star+dust(IR1) 14.52 2.37 10.05 0.3560 0.70 3 3.33e+00 1.17e-01 2000-10-01T20:39:42 A 9.30e+00 2.83e-01 2000-10-01T20:39:42 A 1.45e+01 3.33e-01 2000-10-01T20:39:42 A 1.99e+01 1.01e+00 2004-02-10T08:34:30.264 A 2.28e+01 1.17e+00 2004-02-10T08:34:30.264 A 1.96e+01 1.11e+00 2004-02-10T08:34:30.264 A A 1 3.09 1.5 1.5 -45.0 1.69e+01 8.55e-01 2004-02-10T08:34:30.264 A 2.07e+01 1.04e+00 2004-02-10T08:34:30.264 A 1.75e+01 9.05e-01 2004-02-10T08:34:30.264 A B 1 2.97 1.7 1.7 -45.0 1.52e+01 7.59e-01 2004-02-10T08:34:30.264 A 1.84e+01 9.06e-01 2004-02-10T08:34:30.264 A 1.67e+01 8.31e-01 2004-02-10T08:34:30.264 A B 1 3.92 1.8 1.8 -45.0 1.28e+01 6.32e-01 2004-02-10T08:34:30.264 A 1.48e+01 7.25e-01 2004-02-10T08:34:30.264 A 1.34e+01 6.57e-01 2004-02-10T08:34:30.264 A B 1 4.98 1.8 1.8 -45.0 1.59e+01 1.50e+00 2004-09-19T22:12:44.497 A 1.71e+01 1.61e+00 2004-09-20T03:00:42.863 A 1.65e+01 1.54e+00 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032847.8+311655 052.1993332 5.72e-07 +31.2819627 5.72e-07 A A 03284782+3116552 -5.00 -8.10e-01 4.94e-02 2.16e+00 6 YSOc_star+dust(IR2) 14.50 1.43 9.17 0.1670 1.75 4 1.06e+01 2.45e-01 2000-10-01T20:39:50 A 2.03e+01 4.48e-01 2000-10-01T20:39:50 A 2.89e+01 5.58e-01 2000-10-01T20:39:50 A 3.06e+01 1.55e+00 2004-02-10T08:34:30.264 A 3.08e+01 1.55e+00 2004-02-10T08:34:30.264 A 2.62e+01 1.57e+00 2004-02-10T08:34:30.264 A A 1 3.09 1.5 1.5 -45.0 3.39e+01 1.69e+00 2004-02-10T08:34:30.264 A 3.21e+01 1.79e+00 2004-02-10T08:34:30.264 A 3.25e+01 1.72e+00 2004-02-10T08:34:30.264 A A 1 2.97 1.7 1.7 -45.0 2.87e+01 1.41e+00 2004-02-10T08:34:30.264 A 3.08e+01 1.50e+00 2004-02-10T08:34:30.264 A 2.93e+01 1.44e+00 2004-02-10T08:34:30.264 A A 1 3.92 1.8 1.8 -45.0 3.18e+01 1.52e+00 2004-02-10T08:34:30.264 A 3.12e+01 1.64e+00 2004-02-10T08:34:30.264 A 3.17e+01 1.56e+00 2004-02-10T08:34:30.264 A A 1 4.98 1.8 1.8 -45.0 4.48e+01 4.16e+00 2004-09-19T22:12:44.497 A 4.56e+01 4.26e+00 2004-09-20T03:00:42.863 A 4.54e+01 4.21e+00 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032848.8+311609 052.2032042 6.39e-07 +31.2690977 6.39e-07 A A 03284872+3116086 -2.09 3.30e-01 5.18e-02 1.60e+00 6 YSOc_red -999.00 -999.00 -999.00 -999.0000 -999.00 0 7.48e-02 -9.99e+02 2000-10-01T20:39:50 U 1.23e-01 -9.99e+02 2000-10-01T20:39:50 U 8.93e-01 1.03e-01 2000-10-01T20:39:50 B 1.70e+00 8.80e-02 2004-02-10T08:34:30.264 A 1.64e+00 8.96e-02 2004-02-10T08:34:30.264 A 1.65e+00 8.96e-02 2004-02-10T08:34:30.264 A A 1 3.09 1.5 1.5 -45.0 2.37e+00 1.17e-01 2004-02-10T08:34:30.264 A 2.31e+00 1.17e-01 2004-02-10T08:34:30.264 A 2.35e+00 1.15e-01 2004-02-10T08:34:30.264 A A 1 2.97 1.7 1.7 -45.0 3.37e+00 1.81e-01 2004-02-10T08:34:30.264 A 3.40e+00 1.80e-01 2004-02-10T08:34:30.264 A 3.38e+00 1.72e-01 2004-02-10T08:34:30.264 A A 1 3.92 1.8 1.8 -45.0 5.25e+00 2.62e-01 2004-02-10T08:34:30.264 A 5.62e+00 2.84e-01 2004-02-10T08:34:30.264 A 5.48e+00 2.65e-01 2004-02-10T08:34:30.264 A A 1 4.98 1.8 1.8 -45.0 1.88e+01 1.77e+00 2004-09-19T22:12:44.497 A 1.89e+01 1.80e+00 2004-09-20T03:00:42.863 A 1.90e+01 1.78e+00 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032851.0+311818 052.2126176 9.73e-07 +31.3051313 9.73e-07 A A 03285101+3118184 -5.00 -6.20e-01 5.21e-02 4.98e+00 6 YSOc_star+dust(IR2) 15.91 1.46 7.25 0.1740 2.26 4 4.54e+01 8.79e-01 2000-10-01T20:39:42 A 9.60e+01 1.68e+00 2000-10-01T20:39:42 A 1.41e+02 2.60e+00 2000-10-01T20:39:42 A 1.22e+02 6.28e+00 2004-02-10T08:34:30.264 A 1.55e+02 1.11e+01 2004-02-10T08:34:30.264 A 1.43e+02 9.82e+00 2004-02-10T08:34:30.264 A B 7 3.09 1.5 1.5 -45.0 1.20e+02 7.16e+00 2004-02-10T08:34:30.264 A 1.43e+02 1.10e+01 2004-02-10T08:34:30.264 A 1.37e+02 8.93e+00 2004-02-10T08:34:30.264 A A 2 5.98 2.4 2.2 -83.4 1.15e+02 5.61e+00 2004-02-10T08:34:30.264 A 1.71e+02 1.06e+01 2004-02-10T08:34:30.264 A 1.34e+02 7.82e+00 2004-02-10T08:34:30.264 A C 1 3.92 1.8 1.8 -45.0 1.60e+02 8.64e+00 2004-02-10T08:34:30.264 A 2.06e+02 1.17e+01 2004-02-10T08:34:30.264 A 1.94e+02 1.46e+01 2004-02-10T08:34:30.264 K B 7 2.54 1.8 1.8 -45.0 3.27e+02 3.03e+01 2004-09-19T22:12:44.497 A 3.22e+02 2.99e+01 2004-09-20T03:00:42.863 A 3.26e+02 3.02e+01 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032851.1+311632 052.2128139 6.30e-07 +31.2756626 6.30e-07 A A 03285105+3116324 -5.00 -1.26e+00 5.31e-02 5.51e+00 6 YSOc_star+dust(IR2) 6.37 1.43 11.34 0.1660 0.15 4 7.69e+00 1.77e-01 2000-10-01T20:39:50 A 1.01e+01 2.51e-01 2000-10-01T20:39:50 A 9.45e+00 2.35e-01 2000-10-01T20:39:50 A 5.94e+00 3.04e-01 2004-02-10T08:34:30.264 A 6.30e+00 3.17e-01 2004-02-10T08:34:30.264 A 5.63e+00 3.31e-01 2004-02-10T08:34:30.264 A A 1 3.09 1.5 1.5 -45.0 5.40e+00 2.66e-01 2004-02-10T08:34:30.264 A 5.23e+00 2.57e-01 2004-02-10T08:34:30.264 A 5.12e+00 2.60e-01 2004-02-10T08:34:30.264 A A 1 2.97 1.7 1.7 -45.0 4.85e+00 2.47e-01 2004-02-10T08:34:30.264 A 4.56e+00 2.33e-01 2004-02-10T08:34:30.264 A 4.70e+00 2.34e-01 2004-02-10T08:34:30.264 A A 1 3.92 1.8 1.8 -45.0 5.48e+00 2.72e-01 2004-02-10T08:34:30.264 A 5.18e+00 2.61e-01 2004-02-10T08:34:30.264 A 5.31e+00 2.60e-01 2004-02-10T08:34:30.264 A A 1 4.98 1.8 1.8 -45.0 4.06e+00 4.96e-01 2004-09-19T22:12:44.497 A 3.55e+00 4.56e-01 2004-09-20T03:00:42.863 A 3.83e+00 4.25e-01 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032851.2+311955 052.2133468 5.93e-07 +31.3318958 5.93e-07 A A 03285119+3119548 -5.00 -6.30e-01 4.90e-02 1.14e+01 6 YSOc_star+dust(IR2) 11.12 1.42 8.54 0.1650 0.42 4 3.28e+01 6.35e-01 2000-10-01T20:39:42 A 6.50e+01 1.38e+00 2000-10-01T20:39:42 A 7.32e+01 1.35e+00 2000-10-01T20:39:42 A 5.49e+01 2.83e+00 2004-02-10T08:34:30.264 A 6.21e+01 3.46e+00 2004-02-10T08:34:30.264 A 5.59e+01 3.19e+00 2004-02-10T08:34:30.264 A A 1 3.09 1.5 1.5 -45.0 5.43e+01 2.73e+00 2004-02-10T08:34:30.264 A 5.76e+01 3.00e+00 2004-02-10T08:34:30.264 A 5.45e+01 2.90e+00 2004-02-10T08:34:30.264 A A 1 2.97 1.7 1.7 -45.0 5.50e+01 2.67e+00 2004-02-10T08:34:30.264 A 5.46e+01 2.73e+00 2004-02-10T08:34:30.264 A 5.48e+01 2.65e+00 2004-02-10T08:34:30.264 A A 1 3.92 1.8 1.8 -45.0 8.60e+01 4.23e+00 2004-02-10T08:34:30.264 A 8.12e+01 4.23e+00 2004-02-10T08:34:30.264 A 8.40e+01 4.13e+00 2004-02-10T08:34:30.264 A A 1 4.98 1.8 1.8 -45.0 1.30e+02 1.20e+01 2004-09-19T22:12:44.497 A 1.28e+02 1.19e+01 2004-09-20T03:00:42.863 A 1.30e+02 1.20e+01 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032851.3+311739 052.2135838 1.07e-06 +31.2942550 1.07e-06 A A 03285129+3117397 -1.67 7.80e-01 5.53e-02 2.47e+01 6 YSOc_star+dust(IR2) 9.73 1.70 13.39 0.1850 0.16 4 4.59e-01 7.23e-02 2000-10-01T20:39:42 C 8.71e-01 1.35e-01 2000-10-01T20:39:42 C 1.11e+00 1.35e-01 2000-10-01T20:39:42 B 7.53e-01 4.64e-02 2004-02-10T08:34:30.264 A 6.67e-01 5.08e-02 2004-02-10T08:34:30.264 A 6.72e-01 4.62e-02 2004-02-10T08:34:30.264 A A 1 3.09 1.5 1.5 -45.0 1.20e+00 6.94e-02 2004-02-10T08:34:30.264 A 1.22e+00 6.80e-02 2004-02-10T08:34:30.264 A 1.21e+00 6.42e-02 2004-02-10T08:34:30.264 A A 1 2.97 1.7 1.7 -45.0 1.68e+00 9.79e-02 2004-02-10T08:34:30.264 A 1.81e+00 1.07e-01 2004-02-10T08:34:30.264 A 1.73e+00 9.41e-02 2004-02-10T08:34:30.264 A A 1 3.92 1.8 1.8 -45.0 4.55e+00 2.30e-01 2004-02-10T08:34:30.264 A 5.43e+00 4.09e-01 2004-02-10T08:34:30.264 A 4.93e+00 3.20e-01 2004-02-10T08:34:30.264 A A 1 4.98 1.8 1.8 -45.0 2.45e+01 2.29e+00 2004-09-19T22:12:44.497 A 2.41e+01 2.25e+00 2004-09-20T03:00:42.863 A 2.45e+01 2.28e+00 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032852.2+311547 052.2172963 5.47e-07 +31.2630740 5.47e-07 A A 03285213+3115471 -5.00 -1.21e+00 5.12e-02 5.04e+00 6 YSOc_star+dust(IR2) 8.83 1.41 10.86 0.1630 1.66 4 8.67e+00 2.00e-01 2000-10-01T20:39:50 A 1.05e+01 2.62e-01 2000-10-01T20:39:50 A 1.03e+01 2.27e-01 2000-10-01T20:39:50 A 8.61e+00 4.16e-01 2004-02-10T08:34:30.264 A 7.94e+00 4.12e-01 2004-02-10T08:34:30.264 A 7.87e+00 4.31e-01 2004-02-10T08:34:30.264 A A 1 3.09 1.5 1.5 -45.0 6.63e+00 3.27e-01 2004-02-10T08:34:30.264 A 6.80e+00 3.39e-01 2004-02-10T08:34:30.264 A 6.70e+00 3.27e-01 2004-02-10T08:34:30.264 A A 1 2.97 1.7 1.7 -45.0 6.16e+00 3.15e-01 2004-02-10T08:34:30.264 A 5.96e+00 3.04e-01 2004-02-10T08:34:30.264 A 6.06e+00 3.00e-01 2004-02-10T08:34:30.264 A A 1 3.92 1.8 1.8 -45.0 7.48e+00 3.64e-01 2004-02-10T08:34:30.264 A 7.29e+00 3.60e-01 2004-02-10T08:34:30.264 A 7.40e+00 3.54e-01 2004-02-10T08:34:30.264 A A 1 4.98 1.8 1.8 -45.0 5.23e+00 6.08e-01 2004-09-19T22:12:44.497 A 5.05e+00 5.71e-01 2004-09-20T03:00:42.863 A 5.08e+00 5.33e-01 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032852.2+304506 052.2173587 6.99e-07 +30.7515379 6.99e-07 A A 03285217+3045055 -5.00 -6.90e-01 4.90e-02 1.26e+01 6 YSOc_star+dust(IR1) 7.58 2.36 7.98 0.3550 0.27 3 1.21e+02 2.46e+00 2000-10-01T20:40:16 A 1.82e+02 4.36e+00 2000-10-01T20:40:16 A 1.96e+02 3.96e+00 2000-10-01T20:40:16 A 2.38e+02 1.31e+01 2004-09-07T10:34:29.464 A 2.42e+02 1.35e+01 2004-09-07T10:34:29.464 A 2.41e+02 1.27e+01 2004-09-07T10:34:29.464 A A 9 3.09 1.5 1.5 -45.0 2.90e+02 2.56e+01 2004-09-07T10:34:29.464 K 2.93e+02 2.46e+01 2004-09-07T10:34:29.464 K 2.90e+02 2.14e+01 2004-09-07T10:34:29.464 K A 7 2.27 1.7 1.7 -45.0 3.51e+02 1.83e+01 2004-09-07T10:34:29.464 A 3.44e+02 1.78e+01 2004-09-07T10:34:29.464 A 3.47e+02 1.64e+01 2004-09-07T10:34:29.464 A A 1 3.92 1.8 1.8 -45.0 4.38e+02 2.28e+01 2004-09-07T10:34:29.464 A 4.39e+02 2.29e+01 2004-09-07T10:34:29.464 A 4.37e+02 2.19e+01 2004-09-07T10:34:29.464 A A 1 4.98 1.8 1.8 -45.0 3.07e+02 2.85e+01 2004-09-19T22:12:44.497 A 3.12e+02 2.90e+01 2004-09-20T03:00:42.863 A 3.12e+02 2.89e+01 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032852.2+312245 052.2173951 5.93e-07 +31.3792395 5.93e-07 A A 03285216+3122453 -5.00 -1.16e+00 4.94e-02 1.36e+01 6 YSOc_star+dust(IR3) 7.94 1.24 9.61 0.1280 1.20 5 2.57e+01 4.97e-01 2000-10-01T20:39:42 A 4.03e+01 8.90e-01 2000-10-01T20:39:42 A 3.97e+01 7.68e-01 2000-10-01T20:39:42 A 2.60e+01 1.32e+00 2004-02-10T08:34:30.264 A 2.34e+01 1.20e+00 2004-02-10T08:34:30.264 A 2.21e+01 1.31e+00 2004-02-10T08:34:30.264 A A 1 3.09 1.5 1.5 -45.0 2.09e+01 1.05e+00 2004-02-10T08:34:30.264 A 1.89e+01 9.51e-01 2004-02-10T08:34:30.264 A 1.87e+01 1.01e+00 2004-02-10T08:34:30.264 A A 1 2.97 1.7 1.7 -45.0 1.78e+01 8.79e-01 2004-02-10T08:34:30.264 A 1.61e+01 9.91e-01 2004-02-10T08:34:30.264 A 1.69e+01 8.30e-01 2004-02-10T08:34:30.264 A A 1 3.92 1.8 1.8 -45.0 2.04e+01 9.77e-01 2004-02-10T08:34:30.264 A 1.72e+01 8.42e-01 2004-02-10T08:34:30.264 A 1.85e+01 8.90e-01 2004-02-10T08:34:30.264 A B 1 4.98 1.8 1.8 -45.0 2.18e+01 2.05e+00 2004-09-19T22:12:44.497 A 2.38e+01 2.23e+00 2004-09-20T03:00:42.863 A 2.28e+01 2.13e+00 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032852.9+311626 052.2205155 7.07e-07 +31.2740039 7.07e-07 A A 03285290+3116264 -1.54 -1.28e+00 5.66e-02 4.30e+00 6 YSOc_star+dust(IR2) 6.97 1.42 11.60 0.1650 0.41 4 5.70e+00 1.37e-01 2000-10-01T20:39:50 A 7.15e+00 2.04e-01 2000-10-01T20:39:50 A 6.82e+00 1.70e-01 2000-10-01T20:39:50 A 4.54e+00 2.25e-01 2004-02-10T08:34:30.264 A 4.63e+00 2.41e-01 2004-02-10T08:34:30.264 A 4.32e+00 2.49e-01 2004-02-10T08:34:30.264 A A 1 3.09 1.5 1.5 -45.0 3.73e+00 1.87e-01 2004-02-10T08:34:30.264 A 3.88e+00 1.98e-01 2004-02-10T08:34:30.264 A 3.78e+00 1.89e-01 2004-02-10T08:34:30.264 A A 1 2.97 1.7 1.7 -45.0 3.35e+00 1.83e-01 2004-02-10T08:34:30.264 A 3.57e+00 1.86e-01 2004-02-10T08:34:30.264 A 3.45e+00 1.77e-01 2004-02-10T08:34:30.264 A A 1 3.92 1.8 1.8 -45.0 3.59e+00 1.87e-01 2004-02-10T08:34:30.264 A 3.60e+00 1.89e-01 2004-02-10T08:34:30.264 A 3.62e+00 1.78e-01 2004-02-10T08:34:30.264 A A 1 4.98 1.8 1.8 -45.0 3.50e+00 4.77e-01 2004-09-19T22:12:44.497 A 2.91e+00 4.97e-01 2004-09-20T03:00:42.863 B 3.13e+00 4.14e-01 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032853.8+311456 052.2241763 4.77e-08 +31.2489510 4.77e-08 A A null -5.00 1.46e+00 1.52e-01 2.45e+01 5 YSOc_red -999.00 -999.00 -999.00 -999.0000 -999.00 0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 1.72e-02 4.64e-03 2004-02-10T08:34:30.264 C Q 7 3.09 1.5 1.5 -45.0 1.68e-01 1.29e-02 2004-02-10T08:34:30.264 A 1.71e-01 1.36e-02 2004-02-10T08:34:30.264 A 1.69e-01 1.11e-02 2004-02-10T08:34:30.264 A A 1 2.97 1.7 1.7 -45.0 2.93e-01 4.77e-02 2004-02-10T08:34:30.264 B 2.40e-01 5.22e-02 2004-02-10T08:34:30.264 C 2.69e-01 3.84e-02 2004-02-10T08:34:30.264 A A 1 3.92 1.8 1.8 -45.0 1.24e-01 4.34e-02 2004-02-10T08:34:30.264 D 1.74e-01 4.78e-02 2004-02-10T08:34:30.264 C 1.39e-01 3.37e-02 2004-02-10T08:34:30.264 C A 7 4.98 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 1.21e+01 3.17e+00 2004-09-20T12:36:43.680 C Q -2 8.04 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032854.1+311654 052.2253724 4.34e-08 +31.2817246 4.34e-08 A A 03285407+3116543 -5.00 -8.00e-01 5.10e-02 1.26e+01 6 YSOc_star+dust(IR3) 10.91 1.23 10.15 0.1250 2.00 5 9.81e+00 2.26e-01 2000-10-01T20:39:50 A 1.56e+01 3.88e-01 2000-10-01T20:39:50 A 1.53e+01 3.10e-01 2000-10-01T20:39:50 A 1.16e+01 5.57e-01 2004-02-10T08:34:30.264 A 1.15e+01 5.99e-01 2004-02-10T08:34:30.264 A 1.18e+01 6.05e-01 2004-02-10T08:34:30.264 A A 1 3.09 1.5 1.5 -45.0 1.02e+01 5.04e-01 2004-02-10T08:34:30.264 A 9.88e+00 5.28e-01 2004-02-10T08:34:30.264 A 9.86e+00 4.99e-01 2004-02-10T08:34:30.264 A A 1 2.97 1.7 1.7 -45.0 1.06e+01 5.18e-01 2004-02-10T08:34:30.264 A 1.02e+01 5.15e-01 2004-02-10T08:34:30.264 A 9.86e+00 5.03e-01 2004-02-10T08:34:30.264 A A 1 3.92 1.8 1.8 -45.0 1.34e+01 6.45e-01 2004-02-10T08:34:30.264 A 1.36e+01 6.98e-01 2004-02-10T08:34:30.264 A 1.37e+01 6.51e-01 2004-02-10T08:34:30.264 A A 1 4.98 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 2.13e+01 2.27e+00 2004-09-20T12:36:43.680 A Q -2 8.04 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032854.6+311651 052.2276266 5.80e-07 +31.2808622 5.80e-07 A A 03285461+3116512 -5.00 -6.80e-01 4.90e-02 2.70e+00 6 YSOc_star+dust(IR2) 16.98 1.41 8.24 0.1630 0.85 4 1.14e+01 2.42e-01 2000-10-01T20:39:50 A 3.41e+01 7.23e-01 2000-10-01T20:39:50 A 5.38e+01 1.04e+00 2000-10-01T20:39:50 A 5.34e+01 2.62e+00 2004-02-10T08:34:30.264 A 5.72e+01 2.96e+00 2004-02-10T08:34:30.264 A 5.18e+01 2.85e+00 2004-02-10T08:34:30.264 A A 1 3.09 1.5 1.5 -45.0 4.89e+01 2.41e+00 2004-02-10T08:34:30.264 A 5.91e+01 3.12e+00 2004-02-10T08:34:30.264 A 5.21e+01 2.74e+00 2004-02-10T08:34:30.264 A B 1 2.97 1.7 1.7 -45.0 4.76e+01 2.30e+00 2004-02-10T08:34:30.264 A 6.21e+01 3.15e+00 2004-02-10T08:34:30.264 A 5.35e+01 2.69e+00 2004-02-10T08:34:30.264 A B 1 3.92 1.8 1.8 -45.0 6.03e+01 2.92e+00 2004-02-10T08:34:30.264 A 7.35e+01 3.85e+00 2004-02-10T08:34:30.264 A 6.75e+01 3.33e+00 2004-02-10T08:34:30.264 A B 1 4.98 1.8 1.8 -45.0 1.06e+02 9.98e+00 2004-09-19T22:12:44.497 A 1.04e+02 9.80e+00 2004-09-20T03:00:42.863 A 1.05e+02 9.80e+00 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032855.1+311629 052.2294810 6.54e-07 +31.2746495 6.54e-07 A A 03285505+3116287 -5.00 -5.00e-01 4.90e-02 6.82e+00 6 YSOc_star+dust(IR1) 16.58 2.40 8.83 0.3620 0.48 3 5.89e+00 1.46e-01 2000-10-01T20:39:50 A 2.13e+01 5.30e-01 2000-10-01T20:39:50 A 3.58e+01 1.52e+00 2000-10-01T20:39:50 A 6.42e+01 3.19e+00 2004-02-10T08:34:30.264 A 6.26e+01 3.83e+00 2004-02-10T08:34:30.264 A 7.03e+01 3.53e+00 2004-02-10T08:34:30.264 A A 7 3.09 1.5 1.5 -45.0 6.48e+01 3.17e+00 2004-02-10T08:34:30.264 A 6.98e+01 3.69e+00 2004-02-10T08:34:30.264 A 6.91e+01 3.55e+00 2004-02-10T08:34:30.264 A A 1 2.97 1.7 1.7 -45.0 6.50e+01 3.15e+00 2004-02-10T08:34:30.264 A 7.53e+01 3.70e+00 2004-02-10T08:34:30.264 A 7.00e+01 3.37e+00 2004-02-10T08:34:30.264 A B 1 3.92 1.8 1.8 -45.0 7.62e+01 4.05e+00 2004-02-10T08:34:30.264 A 7.23e+01 4.12e+00 2004-02-10T08:34:30.264 A 7.86e+01 3.91e+00 2004-02-10T08:34:30.264 A A 1 4.98 1.8 1.8 -45.0 1.86e+02 1.76e+01 2004-09-19T22:12:44.497 A 1.84e+02 1.75e+01 2004-09-20T03:00:42.863 A 1.87e+02 1.75e+01 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032855.2+311625 052.2298313 4.71e-09 +31.2735316 4.71e-09 A A 03285514+3116247 -5.00 -6.00e-01 5.48e-02 1.75e+01 6 YSOc_star+dust(IR2) 11.39 1.49 9.64 0.1780 0.11 4 9.97e+00 3.67e-01 2000-10-01T20:39:50 A 2.23e+01 1.11e+00 2000-10-01T20:39:50 A 2.84e+01 6.28e-01 2000-10-01T20:39:50 A 2.85e+01 1.38e+00 2004-02-10T08:34:30.264 A 2.53e+01 1.64e+00 2004-02-10T08:34:30.264 A 1.94e+01 1.40e+00 2004-02-10T08:34:30.264 A A 7 3.09 1.5 1.5 -45.0 2.73e+01 1.34e+00 2004-02-10T08:34:30.264 A 2.58e+01 1.71e+00 2004-02-10T08:34:30.264 A 2.73e+01 1.33e+00 2004-02-10T08:34:30.264 A A 1 2.97 1.7 1.7 -45.0 2.45e+01 1.17e+00 2004-02-10T08:34:30.264 A 2.37e+01 1.31e+00 2004-02-10T08:34:30.264 A 2.03e+01 1.12e+00 2004-02-10T08:34:30.264 A A 1 3.92 1.8 1.8 -45.0 3.08e+01 1.55e+00 2004-02-10T08:34:30.264 A 3.17e+01 1.98e+00 2004-02-10T08:34:30.264 A 2.83e+01 1.38e+00 2004-02-10T08:34:30.264 A A 1 4.98 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 7.83e+01 8.93e+00 2004-09-20T12:36:43.680 A Q -2 8.04 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032856.1+311908 052.2338139 6.08e-07 +31.3190041 6.08e-07 A A null -5.00 -3.60e-01 5.52e-02 1.04e+02 5 YSOc -999.00 -999.00 -999.00 -999.0000 -999.00 0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 8.08e+00 4.23e-01 2004-02-10T08:34:30.264 A 9.23e+00 4.74e-01 2004-02-10T08:34:30.264 A 8.49e+00 4.51e-01 2004-02-10T08:34:30.264 A A 1 3.09 1.5 1.5 -45.0 2.40e+01 1.89e+00 2004-02-10T08:34:30.264 A 2.44e+01 1.29e+00 2004-02-10T08:34:30.264 A 2.45e+01 1.53e+00 2004-02-10T08:34:30.264 A A 1 2.97 1.7 1.7 -45.0 3.33e+01 1.59e+00 2004-02-10T08:34:30.264 A 3.50e+01 1.75e+00 2004-02-10T08:34:30.264 A 3.40e+01 1.67e+00 2004-02-10T08:34:30.264 A A 1 3.92 1.8 1.8 -45.0 3.32e+01 1.60e+00 2004-02-10T08:34:30.264 A 3.39e+01 1.66e+00 2004-02-10T08:34:30.264 A 3.38e+01 1.61e+00 2004-02-10T08:34:30.264 A A 1 4.98 1.8 1.8 -45.0 2.35e+01 2.21e+00 2004-09-19T22:12:44.497 A 2.50e+01 2.34e+00 2004-09-20T03:00:42.863 A 2.43e+01 2.26e+00 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032857.0+311622 052.2373607 5.02e-07 +31.2728488 5.02e-07 A A 03285694+3116222 -5.00 -2.20e-01 4.89e-02 7.99e+01 6 YSOc_star+dust(IR4) 15.40 1.13 9.33 0.1060 1.30 6 4.99e+00 1.38e-01 2000-10-01T20:39:50 A 1.74e+01 4.17e-01 2000-10-01T20:39:50 A 2.46e+01 4.99e-01 2000-10-01T20:39:50 A 1.87e+01 9.05e-01 2004-02-10T08:34:30.264 A 1.91e+01 9.61e-01 2004-02-10T08:34:30.264 A 1.73e+01 9.79e-01 2004-02-10T08:34:30.264 A A 1 3.09 1.5 1.5 -45.0 1.51e+01 7.42e-01 2004-02-10T08:34:30.264 A 1.56e+01 7.83e-01 2004-02-10T08:34:30.264 A 1.49e+01 7.63e-01 2004-02-10T08:34:30.264 A A 1 2.97 1.7 1.7 -45.0 1.31e+01 6.52e-01 2004-02-10T08:34:30.264 A 1.31e+01 6.50e-01 2004-02-10T08:34:30.264 A 1.31e+01 6.33e-01 2004-02-10T08:34:30.264 A A 1 3.92 1.8 1.8 -45.0 2.00e+01 9.60e-01 2004-02-10T08:34:30.264 A 1.90e+01 9.63e-01 2004-02-10T08:34:30.264 A 1.95e+01 9.33e-01 2004-02-10T08:34:30.264 A A 1 4.98 1.8 1.8 -45.0 1.77e+02 1.64e+01 2004-09-19T22:12:44.497 A 1.77e+02 1.64e+01 2004-09-20T03:00:42.863 A 1.78e+02 1.65e+01 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032857.2+311535 052.2382312 7.79e-07 +31.2596212 7.79e-07 A A 03285715+3115345 -1.92 -3.50e-01 5.13e-02 7.06e+00 6 YSOc_star+dust(IR2) 14.46 1.45 11.45 0.1680 0.84 4 1.10e+00 6.77e-02 2000-10-01T20:39:50 A 2.62e+00 1.25e-01 2000-10-01T20:39:50 A 3.54e+00 1.40e-01 2000-10-01T20:39:50 A 3.06e+00 1.82e-01 2004-02-10T08:34:30.264 A 3.35e+00 2.07e-01 2004-02-10T08:34:30.264 A 3.17e+00 1.88e-01 2004-02-10T08:34:30.264 A A 1 3.09 1.5 1.5 -45.0 3.22e+00 1.69e-01 2004-02-10T08:34:30.264 A 3.35e+00 1.84e-01 2004-02-10T08:34:30.264 A 3.29e+00 1.82e-01 2004-02-10T08:34:30.264 A A 1 2.97 1.7 1.7 -45.0 3.90e+00 2.08e-01 2004-02-10T08:34:30.264 A 3.83e+00 2.26e-01 2004-02-10T08:34:30.264 A 3.90e+00 2.13e-01 2004-02-10T08:34:30.264 A A 1 3.92 1.8 1.8 -45.0 5.77e+00 2.82e-01 2004-02-10T08:34:30.264 A 5.73e+00 2.86e-01 2004-02-10T08:34:30.264 A 5.77e+00 2.76e-01 2004-02-10T08:34:30.264 A A 1 4.98 1.8 1.8 -45.0 1.20e+01 1.20e+00 2004-09-19T22:12:44.497 A 1.33e+01 1.45e+00 2004-09-20T03:00:42.863 A 1.28e+01 1.27e+00 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032857.7+311948 052.2404352 6.27e-07 +31.3300200 6.27e-07 A A 03285769+3119481 -5.00 -6.10e-01 4.94e-02 7.92e+00 6 YSOc_star+dust(IR2) 10.80 1.42 10.05 0.1640 0.71 4 9.65e+00 2.40e-01 2000-10-01T20:39:42 A 1.66e+01 4.59e-01 2000-10-01T20:39:42 A 1.87e+01 4.31e-01 2000-10-01T20:39:42 A 1.78e+01 9.05e-01 2004-02-10T08:34:30.264 A 1.56e+01 8.22e-01 2004-02-10T08:34:30.264 A 1.43e+01 8.00e-01 2004-02-10T08:34:30.264 A A 1 3.09 1.5 1.5 -45.0 1.69e+01 8.15e-01 2004-02-10T08:34:30.264 A 1.44e+01 7.38e-01 2004-02-10T08:34:30.264 A 1.49e+01 7.49e-01 2004-02-10T08:34:30.264 A B 1 2.97 1.7 1.7 -45.0 1.64e+01 8.06e-01 2004-02-10T08:34:30.264 A 1.46e+01 7.53e-01 2004-02-10T08:34:30.264 A 1.53e+01 7.58e-01 2004-02-10T08:34:30.264 A A 1 3.92 1.8 1.8 -45.0 2.43e+01 1.20e+00 2004-02-10T08:34:30.264 A 2.29e+01 1.13e+00 2004-02-10T08:34:30.264 A 2.10e+01 1.05e+00 2004-02-10T08:34:30.264 A A 1 4.98 1.8 1.8 -45.0 3.54e+01 3.38e+00 2004-09-19T22:12:44.497 A 3.95e+01 3.76e+00 2004-09-20T03:00:42.863 A 3.66e+01 3.45e+00 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032858.1+311804 052.2421319 6.87e-07 +31.3010176 6.87e-07 A A 03285809+3118038 -1.82 -2.16e+00 6.45e-02 7.20e-01 6 YSOc_star+dust(MP1) 7.11 1.10 10.50 0.0991 1.53 7 1.20e+01 2.54e-01 2000-10-01T20:39:42 A 2.13e+01 4.70e-01 2000-10-01T20:39:42 A 1.93e+01 3.91e-01 2000-10-01T20:39:42 A 1.04e+01 5.28e-01 2004-02-10T08:34:30.264 A 1.08e+01 5.54e-01 2004-02-10T08:34:30.264 A 9.93e+00 5.51e-01 2004-02-10T08:34:30.264 A A 1 3.09 1.5 1.5 -45.0 6.93e+00 3.46e-01 2004-02-10T08:34:30.264 A 7.82e+00 3.91e-01 2004-02-10T08:34:30.264 A 7.31e+00 3.68e-01 2004-02-10T08:34:30.264 A A 1 2.97 1.7 1.7 -45.0 4.88e+00 2.48e-01 2004-02-10T08:34:30.264 A 6.36e+00 3.22e-01 2004-02-10T08:34:30.264 A 5.37e+00 2.72e-01 2004-02-10T08:34:30.264 A B 1 3.92 1.8 1.8 -45.0 3.23e+00 1.70e-01 2004-02-10T08:34:30.264 A 4.94e+00 2.52e-01 2004-02-10T08:34:30.264 A 4.04e+00 2.09e-01 2004-02-10T08:34:30.264 A C 1 4.98 1.8 1.8 -45.0 1.26e+00 2.74e-01 2004-09-19T22:12:44.497 C 1.22e+00 4.36e-01 2004-09-20T03:00:42.863 D 1.24e+00 2.57e-01 2004-09-20T12:36:43.680 C A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032858.3+312209 052.2427431 6.16e-08 +31.3692281 6.16e-08 A A 03285824+3122093 -5.00 -4.90e-01 9.24e-02 1.22e+01 6 YSOc_star+dust(IR2) 17.63 1.47 11.32 0.1650 1.30 4 6.32e-01 6.75e-02 2000-10-01T20:39:42 B 1.72e+00 1.12e-01 2000-10-01T20:39:42 A 3.00e+00 2.04e-01 2000-10-01T20:39:42 A 2.88e+00 1.44e-01 2004-02-10T08:34:30.264 A 3.36e+00 1.85e-01 2004-02-10T08:34:30.264 A 2.92e+00 1.56e-01 2004-02-10T08:34:30.264 A B 1 3.09 1.5 1.5 -45.0 3.20e+00 1.98e-01 2004-02-10T08:34:30.264 A 2.47e+00 2.59e-01 2004-02-10T08:34:30.264 A 3.03e+00 1.76e-01 2004-02-10T08:34:30.264 A B 1 2.97 1.7 1.7 -45.0 2.90e+00 1.66e-01 2004-02-10T08:34:30.264 A 3.35e+00 3.06e-01 2004-02-10T08:34:30.264 A 3.01e+00 1.73e-01 2004-02-10T08:34:30.264 A A 1 3.92 1.8 1.8 -45.0 4.43e+00 2.85e-01 2004-02-10T08:34:30.264 A -9.99e+02 -9.99e+02 null U 3.58e+00 6.83e-01 2004-02-10T08:34:30.264 B Q 1 4.98 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 3.45e+01 9.34e+00 2004-09-20T12:36:43.680 C Q -2 8.04 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032858.3+312202 052.2427862 1.45e-08 +31.3672322 1.45e-08 A A 03285824+3122021 -5.00 -6.60e-01 6.55e-02 3.80e-01 6 YSOc_star+dust(IR1) 14.97 2.38 10.79 0.3570 2.35 3 1.73e+00 7.32e-02 2000-10-01T20:39:42 A 4.10e+00 1.62e-01 2000-10-01T20:39:42 A 7.27e+00 1.94e-01 2000-10-01T20:39:42 A 8.94e+00 4.54e-01 2004-02-10T08:34:30.264 A 8.99e+00 4.48e-01 2004-02-10T08:34:30.264 A 7.55e+00 4.41e-01 2004-02-10T08:34:30.264 A A 1 3.09 1.5 1.5 -45.0 9.51e+00 4.76e-01 2004-02-10T08:34:30.264 A 8.18e+00 4.21e-01 2004-02-10T08:34:30.264 A 8.62e+00 4.31e-01 2004-02-10T08:34:30.264 A B 1 2.97 1.7 1.7 -45.0 9.77e+00 5.03e-01 2004-02-10T08:34:30.264 A 8.51e+00 4.52e-01 2004-02-10T08:34:30.264 A 9.19e+00 4.70e-01 2004-02-10T08:34:30.264 A A 1 3.92 1.8 1.8 -45.0 1.18e+01 5.82e-01 2004-02-10T08:34:30.264 A 9.74e+00 5.90e-01 2004-02-10T08:34:30.264 A 1.06e+01 5.30e-01 2004-02-10T08:34:30.264 A B 1 4.98 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 1.47e+01 3.22e+00 2004-09-20T12:36:43.680 C Q -2 8.04 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032859.2+312032 052.2468124 2.79e-09 +31.3423589 2.79e-09 A A 03285920+3120327 -3.16 1.30e-01 9.49e-02 1.07e+01 6 YSOc_star+dust(IR1) 8.09 2.94 14.23 0.4750 1.96 3 3.74e-01 5.78e-02 2000-10-01T20:39:42 C 4.25e-01 8.72e-02 2000-10-01T20:39:42 D 6.49e-01 1.05e-01 2000-10-01T20:39:42 C -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 6.30e-01 6.40e-02 2004-02-10T08:34:30.264 A Q 7 3.09 1.5 1.5 -45.0 1.79e+00 1.60e-01 2004-02-10T08:34:30.264 A 1.75e+00 1.64e-01 2004-02-10T08:34:30.264 A 1.55e+00 1.32e-01 2004-02-10T08:34:30.264 A A 1 2.97 1.7 1.7 -45.0 1.47e+00 2.07e-01 2004-02-10T08:34:30.264 A 1.24e+00 1.18e-01 2004-02-10T08:34:30.264 A 1.28e+00 1.21e-01 2004-02-10T08:34:30.264 A A 7 3.92 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U 1.56e+00 1.47e-01 2004-02-10T08:34:30.264 A 1.29e+00 1.81e-01 2004-02-10T08:34:30.264 A Q -2 4.98 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 1.05e+01 1.97e+00 2004-09-20T12:36:43.680 B Q -2 8.04 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032859.3+311549 052.2471562 9.55e-07 +31.2635188 9.55e-07 A A 03285930+3115485 -5.00 6.00e-02 4.92e-02 8.99e+00 6 YSOc_star+dust(IR4) 40.80 1.16 5.72 0.1080 1.65 6 4.04e-01 5.99e-02 2000-10-01T20:39:50 C 9.98e+00 2.67e-01 2000-10-01T20:39:50 A 4.46e+01 8.62e-01 2000-10-01T20:39:50 A 1.03e+02 5.99e+00 2004-02-10T08:34:30.264 A 1.03e+02 6.19e+00 2004-02-10T08:34:30.264 A 1.08e+02 5.90e+00 2004-02-10T08:34:30.264 A A 7 3.09 1.5 1.5 -45.0 1.16e+02 6.50e+00 2004-02-10T08:34:30.264 A 1.15e+02 8.05e+00 2004-02-10T08:34:30.264 A 1.23e+02 6.68e+00 2004-02-10T08:34:30.264 A A 2 6.52 2.5 2.3 -82.6 1.22e+02 6.53e+00 2004-02-10T08:34:30.264 A 1.22e+02 6.71e+00 2004-02-10T08:34:30.264 A 1.22e+02 6.32e+00 2004-02-10T08:34:30.264 A A 1 3.92 1.8 1.8 -45.0 2.04e+02 1.49e+01 2004-02-10T08:34:30.264 K 1.68e+02 9.34e+00 2004-02-10T08:34:30.264 A 1.89e+02 1.01e+01 2004-02-10T08:34:30.264 A B 1 2.54 1.8 1.8 -45.0 8.34e+02 7.72e+01 2004-09-19T22:12:44.497 A 8.44e+02 7.82e+01 2004-09-20T03:00:42.863 A 8.38e+02 7.76e+01 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032859.6+312147 052.2481709 5.94e-07 +31.3629630 5.94e-07 A A 03285954+3121467 -5.00 -8.30e-01 4.93e-02 3.92e+00 6 YSOc_star+dust(IR1) 13.48 2.36 8.84 0.3540 1.52 3 1.44e+01 3.04e-01 2000-10-01T20:39:42 A 3.21e+01 7.09e-01 2000-10-01T20:39:42 A 5.05e+01 9.77e-01 2000-10-01T20:39:42 A 7.84e+01 4.10e+00 2004-02-10T08:34:30.264 A 7.44e+01 4.33e+00 2004-02-10T08:34:30.264 A 7.17e+01 4.21e+00 2004-02-10T08:34:30.264 A A 1 3.09 1.5 1.5 -45.0 8.19e+01 4.16e+00 2004-02-10T08:34:30.264 A 7.78e+01 4.14e+00 2004-02-10T08:34:30.264 A 8.08e+01 4.01e+00 2004-02-10T08:34:30.264 A A 1 2.97 1.7 1.7 -45.0 7.15e+01 3.48e+00 2004-02-10T08:34:30.264 A 6.71e+01 3.30e+00 2004-02-10T08:34:30.264 A 6.88e+01 3.38e+00 2004-02-10T08:34:30.264 A A 1 3.92 1.8 1.8 -45.0 7.51e+01 3.78e+00 2004-02-10T08:34:30.264 A 7.20e+01 3.56e+00 2004-02-10T08:34:30.264 A 7.25e+01 3.61e+00 2004-02-10T08:34:30.264 A A 1 4.98 1.8 1.8 -45.0 1.07e+02 9.94e+00 2004-09-19T22:12:44.497 A 9.22e+01 8.54e+00 2004-09-20T03:00:42.863 A 9.95e+01 9.28e+00 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032901.9+311653 052.2578166 5.10e-08 +31.2814344 5.10e-08 A A 03290188+3116533 -5.00 1.20e-01 8.59e-02 6.97e+01 6 YSOc_star+dust(IR2) 10.44 1.55 13.63 0.1710 0.15 4 3.37e-01 4.38e-02 1999-11-26T20:23:08 B 6.50e-01 6.40e-02 1999-11-26T20:23:08 B 7.52e-01 7.41e-02 1999-11-26T20:23:08 A 5.11e-01 2.98e-02 2004-02-10T08:34:30.264 A 5.40e-01 3.43e-02 2004-02-10T08:34:30.264 A 5.27e-01 3.00e-02 2004-02-10T08:34:30.264 A A 1 3.09 1.5 1.5 -45.0 5.00e-01 6.02e-02 2004-02-10T08:34:30.264 A 7.57e-01 6.41e-02 2004-02-10T08:34:30.264 K 7.04e-01 4.42e-02 2004-02-10T08:34:30.264 K B 7 2.27 1.7 1.7 -45.0 4.27e-01 5.54e-02 2004-02-10T08:34:30.264 A 4.80e-01 5.23e-02 2004-02-10T08:34:30.264 A 4.29e-01 4.50e-02 2004-02-10T08:34:30.264 A A 1 3.92 1.8 1.8 -45.0 2.82e-01 9.06e-02 2004-02-10T08:34:30.264 C 3.22e-01 6.81e-02 2004-02-10T08:34:30.264 C 3.03e-01 4.61e-02 2004-02-10T08:34:30.264 B A 7 4.98 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 3.11e+01 5.93e+00 2004-09-20T12:36:43.680 B Q -2 8.04 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032902.8+312217 052.2617023 2.59e-08 +31.3714478 2.59e-08 A A 03290279+3122172 -5.00 -7.10e-01 6.80e-02 5.30e-01 6 YSOc_star+dust(IR2) 24.08 1.51 10.59 0.1690 1.82 4 2.70e-01 4.37e-02 1999-11-26T20:22:60 C 1.23e+00 7.72e-02 1999-11-26T20:22:60 A 3.12e+00 9.21e-02 1999-11-26T20:22:60 A 3.81e+00 2.10e-01 2004-02-10T08:34:30.264 A 3.99e+00 2.14e-01 2004-02-10T08:34:30.264 A 3.80e+00 2.15e-01 2004-02-10T08:34:30.264 A A 1 3.09 1.5 1.5 -45.0 4.40e+00 2.28e-01 2004-02-10T08:34:30.264 A 4.27e+00 2.24e-01 2004-02-10T08:34:30.264 A 4.29e+00 2.18e-01 2004-02-10T08:34:30.264 A A 1 2.97 1.7 1.7 -45.0 4.60e+00 2.58e-01 2004-02-10T08:34:30.264 A 4.61e+00 2.53e-01 2004-02-10T08:34:30.264 A 4.61e+00 2.42e-01 2004-02-10T08:34:30.264 A A 1 3.92 1.8 1.8 -45.0 4.99e+00 2.63e-01 2004-02-10T08:34:30.264 A 4.82e+00 2.57e-01 2004-02-10T08:34:30.264 A 4.77e+00 2.40e-01 2004-02-10T08:34:30.264 A A 1 4.98 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 5.52e+00 1.51e+00 2004-09-20T12:36:43.680 C Q -2 8.04 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032903.1+312238 052.2631077 5.41e-07 +31.3772277 5.41e-07 A A 03290313+3122381 -5.00 -3.60e-01 4.88e-02 4.53e+00 6 YSOc_star+dust(IR1) 14.28 2.37 9.78 0.3550 2.19 3 5.16e+00 1.33e-01 1999-11-26T20:22:60 A 1.16e+01 3.73e-01 1999-11-26T20:22:60 A 1.97e+01 3.99e-01 1999-11-26T20:22:60 A 2.22e+01 1.09e+00 2004-02-10T08:34:30.264 A 2.35e+01 1.19e+00 2004-02-10T08:34:30.264 A 2.05e+01 1.13e+00 2004-02-10T08:34:30.264 A A 1 3.09 1.5 1.5 -45.0 2.40e+01 1.19e+00 2004-02-10T08:34:30.264 A 2.67e+01 1.34e+00 2004-02-10T08:34:30.264 A 2.52e+01 1.26e+00 2004-02-10T08:34:30.264 A A 1 2.97 1.7 1.7 -45.0 2.31e+01 1.12e+00 2004-02-10T08:34:30.264 A 2.78e+01 1.36e+00 2004-02-10T08:34:30.264 A 2.45e+01 1.19e+00 2004-02-10T08:34:30.264 A B 1 3.92 1.8 1.8 -45.0 3.39e+01 1.64e+00 2004-02-10T08:34:30.264 A 3.60e+01 1.78e+00 2004-02-10T08:34:30.264 A 3.53e+01 1.71e+00 2004-02-10T08:34:30.264 A A 1 4.98 1.8 1.8 -45.0 8.33e+01 7.79e+00 2004-09-19T22:12:44.497 A 8.17e+01 7.66e+00 2004-09-20T03:00:42.863 A 8.35e+01 7.77e+00 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032903.2+312545 052.2634082 9.13e-07 +31.4292067 9.13e-07 A A 03290320+3125451 -1.98 -5.10e-01 5.16e-02 2.46e+01 6 YSOc_star+dust(IR2) 14.36 1.45 12.06 0.1640 1.90 4 7.64e-01 5.14e-02 1999-11-26T20:22:60 A 1.45e+00 8.39e-02 1999-11-26T20:22:60 A 1.95e+00 9.18e-02 1999-11-26T20:22:60 A 1.80e+00 1.06e-01 2004-02-10T08:34:30.264 A 1.87e+00 9.58e-02 2004-02-10T08:34:30.264 A 1.84e+00 9.83e-02 2004-02-10T08:34:30.264 A A 1 3.09 1.5 1.5 -45.0 1.69e+00 8.70e-02 2004-02-10T08:34:30.264 A 1.74e+00 9.04e-02 2004-02-10T08:34:30.264 A 1.72e+00 8.67e-02 2004-02-10T08:34:30.264 A A 1 2.97 1.7 1.7 -45.0 1.61e+00 1.08e-01 2004-02-10T08:34:30.264 A 1.66e+00 1.02e-01 2004-02-10T08:34:30.264 A 1.62e+00 9.21e-02 2004-02-10T08:34:30.264 A A 1 3.92 1.8 1.8 -45.0 1.71e+00 1.10e-01 2004-02-10T08:34:30.264 A 1.82e+00 1.16e-01 2004-02-10T08:34:30.264 A 1.78e+00 1.00e-01 2004-02-10T08:34:30.264 A A 1 4.98 1.8 1.8 -45.0 7.74e+00 8.02e-01 2004-09-19T22:12:44.497 A 7.89e+00 8.50e-01 2004-09-20T03:00:42.863 A 7.87e+00 7.82e-01 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032903.3+312315 052.2638923 5.19e-07 +31.3873914 5.19e-07 A A 03290332+3123148 -4.47 1.13e+00 4.94e-02 4.55e+01 6 YSOc_red -999.00 -999.00 -999.00 -999.0000 -999.00 0 2.00e-01 4.31e-02 1999-11-26T20:22:60 D 4.75e-01 6.21e-02 1999-11-26T20:22:60 B 1.57e+00 7.66e-02 1999-11-26T20:22:60 A 4.56e+00 2.29e-01 2004-02-10T08:34:30.264 A 4.78e+00 2.41e-01 2004-02-10T08:34:30.264 A 4.53e+00 2.45e-01 2004-02-10T08:34:30.264 A A 1 3.09 1.5 1.5 -45.0 7.86e+00 3.84e-01 2004-02-10T08:34:30.264 A 7.64e+00 3.73e-01 2004-02-10T08:34:30.264 A 7.57e+00 3.79e-01 2004-02-10T08:34:30.264 A A 1 2.97 1.7 1.7 -45.0 1.88e+01 9.25e-01 2004-02-10T08:34:30.264 A 1.86e+01 9.23e-01 2004-02-10T08:34:30.264 A 1.86e+01 9.01e-01 2004-02-10T08:34:30.264 A A 1 3.92 1.8 1.8 -45.0 4.75e+01 2.26e+00 2004-02-10T08:34:30.264 A 4.65e+01 2.23e+00 2004-02-10T08:34:30.264 A 4.62e+01 2.23e+00 2004-02-10T08:34:30.264 A A 1 4.98 1.8 1.8 -45.0 1.43e+02 1.32e+01 2004-09-19T22:12:44.497 A 1.37e+02 1.28e+01 2004-09-20T03:00:42.863 A 1.41e+02 1.31e+01 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032903.9+305630 052.2661258 6.95e-07 +30.9416020 6.95e-07 A A 03290388+3056297 -2.73 -1.70e-01 4.98e-02 6.66e+01 6 YSOc_star+dust(IR2) 8.74 1.44 13.13 0.1600 1.50 4 8.83e-01 5.29e-02 1999-11-26T20:23:34 A 1.43e+00 8.70e-02 1999-11-26T20:23:34 A 1.19e+00 7.68e-02 1999-11-26T20:23:34 A 8.34e-01 4.33e-02 2004-09-07T10:34:29.464 A 8.22e-01 4.38e-02 2004-09-07T10:34:29.464 A 9.65e-01 4.65e-02 2005-09-16T09:56:12.848 A A 1 1.77 1.5 1.5 -45.0 8.59e-01 4.60e-02 2004-09-07T10:34:29.464 A 8.39e-01 4.68e-02 2004-09-07T10:34:29.464 A 1.02e+00 4.89e-02 2005-09-16T09:56:12.848 A A 1 2.27 1.7 1.7 -45.0 7.94e-01 5.66e-02 2004-09-07T10:34:29.464 A 7.67e-01 5.11e-02 2004-09-07T10:34:29.464 A 8.69e-01 4.68e-02 2005-09-16T09:56:12.848 A A 1 2.54 1.8 1.8 -45.0 8.64e-01 6.38e-02 2004-09-07T10:34:29.464 A 9.73e-01 6.82e-02 2004-09-07T10:34:29.464 A 9.79e-01 5.22e-02 2005-09-16T09:56:12.848 A A 1 2.54 1.8 1.8 -45.0 1.14e+01 1.10e+00 2004-09-19T22:12:44.497 A 1.10e+01 1.04e+00 2004-09-20T03:00:42.863 A 1.11e+01 1.04e+00 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032903.9+312149 052.2661357 7.50e-07 +31.3635042 7.50e-07 A A 03290386+3121487 -5.00 -7.40e-01 4.99e-02 1.90e+00 6 YSOc_star+dust(IR1) 13.33 2.37 7.76 0.3550 1.07 3 3.87e+01 1.07e+00 1999-11-26T20:22:60 A 9.02e+01 2.66e+00 1999-11-26T20:22:60 A 1.37e+02 2.78e+00 1999-11-26T20:22:60 A 1.72e+02 9.20e+00 2004-02-10T08:34:30.264 A 1.84e+02 1.87e+01 2004-02-10T08:34:30.264 K 1.61e+02 9.32e+00 2004-02-10T08:34:30.264 A A 7 1.77 1.5 1.5 -45.0 1.74e+02 8.96e+00 2004-02-10T08:34:30.264 A 1.50e+02 8.28e+00 2004-02-10T08:34:30.264 A 1.49e+02 8.63e+00 2004-02-10T08:34:30.264 A A 1 2.97 1.7 1.7 -45.0 1.66e+02 8.42e+00 2004-02-10T08:34:30.264 A 1.38e+02 7.60e+00 2004-02-10T08:34:30.264 A 1.51e+02 7.67e+00 2004-02-10T08:34:30.264 A B 2 7.95 2.7 2.6 -58.9 2.02e+02 1.00e+01 2004-02-10T08:34:30.264 A 1.66e+02 9.63e+00 2004-02-10T08:34:30.264 A 1.86e+02 1.00e+01 2004-02-10T08:34:30.264 A B 7 4.98 1.8 1.8 -45.0 2.73e+02 2.54e+01 2004-09-19T22:12:44.497 A 2.54e+02 2.38e+01 2004-09-20T03:00:42.863 A 2.62e+02 2.44e+01 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032904.1+311447 052.2669270 1.80e-06 +31.2462623 1.80e-06 A A null -5.00 1.43e+00 6.00e-02 5.66e+01 5 YSOc_red -999.00 -999.00 -999.00 -999.0000 -999.00 0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 3.84e-01 3.43e-02 2004-02-10T08:34:30.264 A 4.59e-01 5.02e-02 2004-02-10T08:34:30.264 A 4.43e-01 2.94e-02 2005-09-16T09:56:12.848 A A 7 1.77 1.5 1.5 -45.0 1.16e+00 6.93e-02 2004-02-10T08:34:30.264 A 1.46e+00 9.37e-02 2004-02-10T08:34:30.264 A 1.08e+00 8.67e-02 2004-02-10T08:34:30.264 A B 2 7.38 3.1 2.1 -89.3 1.08e+00 1.69e-01 2004-02-10T08:34:30.264 B 1.56e+00 1.04e-01 2004-02-10T08:34:30.264 A 1.27e+00 7.73e-02 2005-09-16T09:56:12.848 A B 2 6.26 4.9 1.6 -66.2 1.33e+00 8.44e-02 2004-02-10T08:34:30.264 A 1.62e+00 1.12e-01 2004-02-10T08:34:30.264 A 1.43e+00 8.62e-02 2004-02-10T08:34:30.264 A B 1 4.98 1.8 1.8 -45.0 6.97e+01 6.53e+00 2004-09-19T22:12:44.497 A 7.40e+01 6.92e+00 2004-09-20T03:00:42.863 A 7.18e+01 6.68e+00 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032904.1+305613 052.2671572 5.17e-07 +30.9368989 5.17e-07 A A 03290413+3056127 -1.88 -7.90e-01 4.84e-02 1.97e+01 6 YSOc_star+dust(IR2) 8.57 1.40 11.67 0.1580 1.85 4 4.42e+00 1.22e-01 1999-11-26T20:23:34 A 5.24e+00 1.54e-01 1999-11-26T20:23:34 A 4.93e+00 1.05e-01 1999-11-26T20:23:34 A 3.91e+00 1.95e-01 2004-09-07T10:34:29.464 A 3.78e+00 1.99e-01 2004-09-07T10:34:29.464 A 3.80e+00 1.82e-01 2005-09-16T09:56:12.848 A A 1 1.77 1.5 1.5 -45.0 3.33e+00 1.70e-01 2004-09-07T10:34:29.464 A 3.41e+00 1.72e-01 2004-09-07T10:34:29.464 A 3.31e+00 1.56e-01 2005-09-16T09:56:12.848 A A 1 2.27 1.7 1.7 -45.0 3.19e+00 1.80e-01 2004-09-07T10:34:29.464 A 3.00e+00 1.60e-01 2004-09-07T10:34:29.464 A 3.03e+00 1.49e-01 2005-09-16T09:56:12.848 A A 1 2.54 1.8 1.8 -45.0 3.67e+00 1.84e-01 2004-09-07T10:34:29.464 A 3.70e+00 1.86e-01 2004-09-07T10:34:29.464 A 3.64e+00 1.72e-01 2005-09-16T09:56:12.848 A A 1 2.54 1.8 1.8 -45.0 9.18e+00 9.00e-01 2004-09-19T22:12:44.497 A 8.70e+00 8.75e-01 2004-09-20T03:00:42.863 A 8.87e+00 8.58e-01 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032904.7+311659 052.2694985 6.28e-07 +31.2830525 6.28e-07 A A 03290466+3116591 -4.98 -2.30e-01 5.01e-02 2.68e+00 6 YSOc -999.00 -999.00 -999.00 -999.0000 -999.00 0 9.60e-01 6.37e-02 1999-11-26T20:23:08 A 2.79e+00 1.03e-01 1999-11-26T20:23:08 A 5.73e+00 1.16e-01 1999-11-26T20:23:08 A 8.90e+00 4.45e-01 2004-02-10T08:34:30.264 A 7.64e+00 4.08e-01 2004-02-10T08:34:30.264 A 7.77e+00 4.37e-01 2004-02-10T08:34:30.264 A B 1 3.09 1.5 1.5 -45.0 8.61e+00 4.22e-01 2004-02-10T08:34:30.264 A 9.00e+00 4.49e-01 2004-02-10T08:34:30.264 A 8.84e+00 4.33e-01 2004-02-10T08:34:30.264 A A 1 2.97 1.7 1.7 -45.0 8.84e+00 4.43e-01 2004-02-10T08:34:30.264 A 1.02e+01 5.26e-01 2004-02-10T08:34:30.264 A 9.36e+00 4.77e-01 2004-02-10T08:34:30.264 A A 1 3.92 1.8 1.8 -45.0 1.36e+01 6.71e-01 2004-02-10T08:34:30.264 A 1.51e+01 7.41e-01 2004-02-10T08:34:30.264 A 1.45e+01 7.25e-01 2004-02-10T08:34:30.264 A A 1 4.98 1.8 1.8 -45.0 3.48e+01 3.53e+00 2004-09-19T22:12:44.497 A 3.51e+01 3.73e+00 2004-09-20T03:00:42.863 A 3.56e+01 3.48e+00 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032904.7+311135 052.2697156 5.68e-07 +31.1930288 5.68e-07 A A 03290472+3111348 -5.00 -3.20e-01 4.98e-02 3.29e+01 6 YSOc -999.00 -999.00 -999.00 -999.0000 -999.00 0 6.35e-02 -9.99e+02 1999-11-26T20:23:17 U 5.67e-01 -9.99e+02 1999-11-26T20:23:17 U 1.11e+00 7.77e-02 1999-11-26T20:23:17 A 5.09e+00 2.58e-01 2004-02-10T08:34:30.264 A 4.79e+00 2.51e-01 2004-02-10T08:34:30.264 A 4.80e+00 2.32e-01 2005-09-16T09:56:12.848 A A 1 1.77 1.5 1.5 -45.0 7.85e+00 3.92e-01 2004-02-10T08:34:30.264 A 6.92e+00 3.46e-01 2004-02-10T08:34:30.264 A 7.10e+00 3.76e-01 2004-02-10T08:34:30.264 A A 1 2.97 1.7 1.7 -45.0 9.15e+00 4.56e-01 2004-02-10T08:34:30.264 A 7.47e+00 3.86e-01 2004-02-10T08:34:30.264 A 7.78e+00 3.72e-01 2005-09-16T09:56:12.848 A B 1 2.54 1.8 1.8 -45.0 9.45e+00 4.62e-01 2004-02-10T08:34:30.264 A 8.03e+00 3.98e-01 2004-02-10T08:34:30.264 A 8.55e+00 4.18e-01 2004-02-10T08:34:30.264 A B 1 4.98 1.8 1.8 -45.0 1.12e+01 1.08e+00 2004-09-19T22:12:44.497 A 1.10e+01 1.08e+00 2004-09-20T03:00:42.863 A 1.12e+01 1.06e+00 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032904.9+312038 052.2706051 5.74e-07 +31.3439879 5.74e-07 A A 03290493+3120385 -5.00 2.20e-01 4.97e-02 1.00e+00 6 YSOc_star+dust(IR2) 34.93 2.18 8.91 0.2110 0.91 3 9.39e-01 -9.99e+02 1999-11-26T20:23:08 U 1.48e+00 1.91e-01 1999-11-26T20:23:08 B 4.30e+00 2.62e-01 1999-11-26T20:23:08 A 8.84e+00 4.65e-01 2004-02-10T08:34:30.264 A 8.44e+00 5.03e-01 2004-02-10T08:34:30.264 A 9.03e+00 4.79e-01 2004-02-10T08:34:30.264 A A 1 3.09 1.5 1.5 -45.0 1.21e+01 6.11e-01 2004-02-10T08:34:30.264 A 1.13e+01 5.67e-01 2004-02-10T08:34:30.264 A 1.16e+01 6.19e-01 2004-02-10T08:34:30.264 A A 1 2.97 1.7 1.7 -45.0 1.51e+01 7.54e-01 2004-02-10T08:34:30.264 A 1.46e+01 7.39e-01 2004-02-10T08:34:30.264 A 1.50e+01 7.28e-01 2004-02-10T08:34:30.264 A A 1 3.92 1.8 1.8 -45.0 2.16e+01 1.03e+00 2004-02-10T08:34:30.264 A 2.03e+01 1.01e+00 2004-02-10T08:34:30.264 A 2.13e+01 1.00e+00 2004-02-10T08:34:30.264 A A 1 4.98 1.8 1.8 -45.0 9.84e+01 9.21e+00 2004-09-19T22:12:44.497 A 9.39e+01 8.81e+00 2004-09-20T03:00:42.863 A 9.66e+01 8.99e+00 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032905.2+312037 052.2716040 4.74e-08 +31.3435747 4.74e-08 A A 03290506+3120377 -2.61 3.40e-01 7.59e-02 1.57e+00 5 YSOc_red -999.00 -999.00 -999.00 -999.0000 -999.00 0 4.29e-01 7.43e-02 1999-11-26T20:23:08 C 3.00e+00 -9.99e+02 1999-11-26T20:23:08 U 6.12e+00 -9.99e+02 1999-11-26T20:23:08 U 2.63e+00 1.64e-01 2004-02-10T08:34:30.264 A 2.81e+00 1.81e-01 2004-02-10T08:34:30.264 A 2.05e+00 1.60e-01 2004-02-10T08:34:30.264 A A 7 3.09 1.5 1.5 -45.0 3.48e+00 1.88e-01 2004-02-10T08:34:30.264 A 3.75e+00 1.96e-01 2004-02-10T08:34:30.264 A 3.20e+00 1.73e-01 2004-02-10T08:34:30.264 A A 1 2.97 1.7 1.7 -45.0 4.31e+00 2.54e-01 2004-02-10T08:34:30.264 A 4.85e+00 2.90e-01 2004-02-10T08:34:30.264 A 4.23e+00 2.46e-01 2004-02-10T08:34:30.264 A A 1 3.92 1.8 1.8 -45.0 5.41e+00 3.24e-01 2004-02-10T08:34:30.264 A 6.42e+00 3.90e-01 2004-02-10T08:34:30.264 A 5.91e+00 3.08e-01 2004-02-10T08:34:30.264 A A 1 4.98 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 3.26e+01 4.91e+00 2004-09-20T12:36:43.680 B Q -2 8.04 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032905.8+311640 052.2740634 1.40e-06 +31.2776649 1.40e-06 A A 03290575+3116396 -5.00 -5.00e-01 5.05e-02 5.39e+00 6 YSOc_star+dust(IR1) 27.43 2.39 6.91 0.3580 1.75 3 2.55e+00 1.32e-01 1999-11-26T20:23:08 A 2.32e+01 9.39e-01 1999-11-26T20:23:08 A 7.10e+01 1.83e+00 1999-11-26T20:23:08 A 1.25e+02 9.02e+00 2004-02-10T08:34:30.264 A 1.32e+02 9.58e+00 2004-02-10T08:34:30.264 A 1.40e+02 9.09e+00 2004-02-10T08:34:30.264 A A 7 3.09 1.5 1.5 -45.0 1.62e+02 1.05e+01 2004-02-10T08:34:30.264 A 9.89e+01 7.61e+00 2004-02-10T08:34:30.264 K 1.66e+02 9.97e+00 2004-02-10T08:34:30.264 A C 2 4.90 2.6 2.4 -67.1 1.71e+02 9.22e+00 2004-02-10T08:34:30.264 A 1.81e+02 1.06e+01 2004-02-10T08:34:30.264 A 1.77e+02 9.30e+00 2004-02-10T08:34:30.264 A A 2 7.08 2.5 2.5 0.0 2.03e+02 1.01e+01 2004-02-10T08:34:30.264 A 2.07e+02 1.05e+01 2004-02-10T08:34:30.264 A 2.04e+02 1.01e+01 2004-02-10T08:34:30.264 A A 2 10.84 3.3 2.9 -63.0 2.83e+02 2.66e+01 2004-09-19T22:12:44.497 A 2.94e+02 2.74e+01 2004-09-20T03:00:42.863 A 2.84e+02 2.66e+01 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032906.3+311346 052.2763677 4.59e-07 +31.2295507 4.59e-07 A A 03290631+3113464 -5.00 -3.90e-01 4.81e-02 4.71e+00 6 YSOc_star+dust(IR3) 39.33 1.59 8.24 0.1460 1.99 4 9.90e-02 -9.99e+02 1999-11-26T20:23:08 U 1.35e+00 8.09e-02 1999-11-26T20:23:08 A 5.75e+00 1.22e-01 1999-11-26T20:23:08 A 1.10e+01 5.58e-01 2004-02-10T08:34:30.264 A 1.08e+01 5.45e-01 2004-02-10T08:34:30.264 A 1.12e+01 5.32e-01 2005-09-16T09:56:12.848 A A 1 1.77 1.5 1.5 -45.0 1.49e+01 7.41e-01 2004-02-10T08:34:30.264 A 1.40e+01 6.85e-01 2004-02-10T08:34:30.264 A 1.38e+01 7.14e-01 2004-02-10T08:34:30.264 A A 1 2.97 1.7 1.7 -45.0 1.61e+01 7.86e-01 2004-02-10T08:34:30.264 A 1.49e+01 7.29e-01 2004-02-10T08:34:30.264 A 1.59e+01 7.44e-01 2005-09-16T09:56:12.848 A A 1 2.54 1.8 1.8 -45.0 1.78e+01 8.72e-01 2004-02-10T08:34:30.264 A 1.70e+01 8.56e-01 2004-02-10T08:34:30.264 A 1.71e+01 8.51e-01 2004-02-10T08:34:30.264 A A 1 4.98 1.8 1.8 -45.0 3.01e+01 2.82e+00 2004-09-19T22:12:44.497 A 3.15e+01 3.05e+00 2004-09-20T03:00:42.863 A 3.08e+01 2.88e+00 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032908.0+312251 052.2831522 3.02e-08 +31.3809490 3.02e-08 A A 03290794+3122515 -5.00 -6.60e-01 7.52e-02 7.10e-01 6 YSOc_star+dust(IR2) 19.51 1.44 7.82 0.1680 1.97 4 1.00e+01 2.59e-01 1999-11-26T20:22:60 A 3.46e+01 9.88e-01 1999-11-26T20:22:60 A 5.58e+01 1.13e+00 1999-11-26T20:22:60 A 7.23e+01 4.43e+00 2004-02-10T08:34:30.264 A 5.97e+01 4.08e+00 2004-02-10T08:34:30.264 A 6.75e+01 4.11e+00 2004-02-10T08:34:30.264 A B 1 3.09 1.5 1.5 -45.0 8.00e+01 4.00e+00 2004-02-10T08:34:30.264 A 6.91e+01 3.75e+00 2004-02-10T08:34:30.264 A 7.24e+01 3.70e+00 2004-02-10T08:34:30.264 A A 1 2.97 1.7 1.7 -45.0 8.38e+01 4.24e+00 2004-02-10T08:34:30.264 A 7.71e+01 3.96e+00 2004-02-10T08:34:30.264 A 8.00e+01 4.01e+00 2004-02-10T08:34:30.264 A A 1 3.92 1.8 1.8 -45.0 9.45e+01 7.54e+00 2004-02-10T08:34:30.264 A 9.63e+01 8.71e+00 2004-02-10T08:34:30.264 A 9.48e+01 7.09e+00 2004-02-10T08:34:30.264 A A 1 4.98 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 8.94e+01 2.33e+01 2004-09-20T12:36:43.680 C Q -2 8.04 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032909.0+312256 052.2873662 7.20e-09 +31.3822638 7.20e-09 A B 03290895+3122562 -5.00 -5.00e-02 8.48e-02 3.41e+00 6 YSOc -999.00 -999.00 -999.00 -999.0000 -999.00 0 6.03e-01 7.56e-02 1999-11-26T20:22:60 B 4.40e+00 2.35e-01 1999-11-26T20:22:60 A 1.37e+01 4.42e-01 1999-11-26T20:22:60 A 2.71e+01 1.38e+00 2004-02-10T08:34:30.264 A 3.82e+01 2.05e+00 2004-02-10T08:34:30.264 A 3.01e+01 1.67e+00 2004-02-10T08:34:30.264 A C 1 3.09 1.5 1.5 -45.0 3.40e+01 1.73e+00 2004-02-10T08:34:30.264 A 4.74e+01 2.46e+00 2004-02-10T08:34:30.264 A 3.98e+01 2.15e+00 2004-02-10T08:34:30.264 A C 1 2.97 1.7 1.7 -45.0 4.05e+01 3.29e+00 2004-02-10T08:34:30.264 A 5.14e+01 4.09e+00 2004-02-10T08:34:30.264 A 4.40e+01 3.24e+00 2004-02-10T08:34:30.264 A B 1 3.92 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 5.66e+01 1.16e+01 2004-02-10T08:34:30.264 C Q -2 4.98 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 1.21e+02 2.62e+01 2004-09-20T12:36:43.680 C Q -2 8.04 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032909.1+312306 052.2879172 6.42e-09 +31.3848682 6.42e-09 A A 03290908+3123056 -5.00 1.20e-01 8.05e-02 9.23e+00 6 YSOc_PAH-em -999.00 -999.00 -999.00 -999.0000 -999.00 0 2.20e+00 8.32e-02 1999-11-26T20:22:60 A 6.80e+00 2.38e-01 1999-11-26T20:22:60 A 1.17e+01 3.12e-01 1999-11-26T20:22:60 A 1.60e+01 8.20e-01 2004-02-10T08:34:30.264 A 1.55e+01 8.07e-01 2004-02-10T08:34:30.264 A 1.50e+01 8.44e-01 2004-02-10T08:34:30.264 A A 1 3.09 1.5 1.5 -45.0 1.67e+01 9.03e-01 2004-02-10T08:34:30.264 A 1.64e+01 8.22e-01 2004-02-10T08:34:30.264 A 1.66e+01 8.24e-01 2004-02-10T08:34:30.264 A A 1 2.97 1.7 1.7 -45.0 2.23e+01 2.52e+00 2004-02-10T08:34:30.264 A 2.12e+01 2.91e+00 2004-02-10T08:34:30.264 A 1.76e+01 2.15e+00 2004-02-10T08:34:30.264 A A 2 6.25 2.4 2.3 -64.6 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 5.29e+01 1.16e+01 2004-02-10T08:34:30.264 C Q -2 4.98 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 2.11e+02 3.82e+01 2004-09-20T12:36:43.680 B Q -2 8.04 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032909.1+312129 052.2879256 1.88e-06 +31.3579858 1.88e-06 A B 03290907+3121291 -5.00 8.80e-01 6.42e-02 7.27e+01 6 YSOc -999.00 -999.00 -999.00 -999.0000 -999.00 0 9.44e-01 -9.99e+02 1999-11-26T20:22:60 U 1.58e+00 -9.99e+02 1999-11-26T20:22:60 U 4.30e+00 1.86e-01 1999-11-26T20:22:60 A 4.05e+01 2.31e+00 2004-02-10T08:34:30.264 A 2.48e+01 1.61e+00 2004-02-10T08:34:30.264 A 2.65e+01 1.90e+00 2004-02-10T08:34:30.264 A C 1 3.09 1.5 1.5 -45.0 1.04e+02 5.64e+00 2004-02-10T08:34:30.264 A 6.74e+01 4.36e+00 2004-02-10T08:34:30.264 A 8.09e+01 4.91e+00 2004-02-10T08:34:30.264 A C 2 6.52 2.5 2.3 -63.1 1.67e+02 8.69e+00 2004-02-10T08:34:30.264 A 1.10e+02 6.49e+00 2004-02-10T08:34:30.264 A 1.30e+02 7.63e+00 2004-02-10T08:34:30.264 A C 2 8.26 2.7 2.7 0.0 1.88e+02 1.29e+01 2004-02-10T08:34:30.264 A 1.14e+02 1.05e+01 2004-02-10T08:34:30.264 A 1.33e+02 9.98e+00 2004-02-10T08:34:30.264 A C 1 4.98 1.8 1.8 -45.0 3.64e+02 6.94e+01 2004-09-19T22:12:44.497 B 3.59e+02 6.53e+01 2004-09-20T03:00:42.863 B 3.74e+02 5.16e+01 2004-09-20T12:36:43.680 A A 7 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032909.3+312104 052.2888992 1.09e-06 +31.3511275 1.09e-06 A A 03290933+3121042 -2.92 -1.20e-01 6.07e-02 1.98e+01 6 YSOc_star+dust(IR3) 21.46 1.27 10.62 0.1280 0.82 5 4.33e-01 4.26e-02 1999-11-26T20:23:08 B 1.98e+00 8.40e-02 1999-11-26T20:23:08 A 3.66e+00 1.08e-01 1999-11-26T20:23:08 A 3.47e+00 2.05e-01 2004-02-10T08:34:30.264 A 4.06e+00 2.30e-01 2004-02-10T08:34:30.264 A 4.13e+00 2.33e-01 2004-02-10T08:34:30.264 A A 1 3.09 1.5 1.5 -45.0 3.30e+00 1.75e-01 2004-02-10T08:34:30.264 A 3.73e+00 1.94e-01 2004-02-10T08:34:30.264 A 3.50e+00 1.82e-01 2004-02-10T08:34:30.264 A A 1 2.97 1.7 1.7 -45.0 4.17e+00 3.27e-01 2004-02-10T08:34:30.264 A 4.02e+00 3.28e-01 2004-02-10T08:34:30.264 A 4.03e+00 3.36e-01 2004-02-10T08:34:30.264 A A 1 3.92 1.8 1.8 -45.0 7.10e+00 6.04e-01 2004-02-10T08:34:30.264 A 6.44e+00 4.81e-01 2004-02-10T08:34:30.264 A 6.51e+00 5.39e-01 2004-02-10T08:34:30.264 A A 1 4.98 1.8 1.8 -45.0 3.82e+01 5.85e+00 2004-09-19T22:12:44.497 B 3.74e+01 5.37e+00 2004-09-20T03:00:42.863 B 3.58e+01 4.56e+00 2004-09-20T12:36:43.680 A A 2 12.29 3.5 3.1 -74.5 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032909.4+311414 052.2891675 6.51e-07 +31.2371632 6.51e-07 A A null -5.00 -6.80e-01 6.38e-02 8.47e+01 5 YSOc -999.00 -999.00 -999.00 -999.0000 -999.00 0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 3.01e+00 1.57e-01 2004-02-10T08:34:30.264 A 2.93e+00 1.62e-01 2004-02-10T08:34:30.264 A 2.93e+00 1.42e-01 2005-09-16T09:56:12.848 A A 1 1.77 1.5 1.5 -45.0 8.33e+00 4.52e-01 2004-02-10T08:34:30.264 A 8.42e+00 4.19e-01 2004-02-10T08:34:30.264 A 7.28e+00 4.86e-01 2004-02-10T08:34:30.264 A A 1 2.97 1.7 1.7 -45.0 9.09e+00 4.64e-01 2004-02-10T08:34:30.264 A 8.99e+00 4.48e-01 2004-02-10T08:34:30.264 A 8.95e+00 4.24e-01 2005-09-16T09:56:12.848 A A 1 2.54 1.8 1.8 -45.0 6.02e+00 3.34e-01 2004-02-10T08:34:30.264 A 5.84e+00 3.28e-01 2004-02-10T08:34:30.264 A 5.99e+00 3.15e-01 2004-02-10T08:34:30.264 A A 1 4.98 1.8 1.8 -45.0 3.58e+00 5.17e-01 2004-09-19T22:12:44.497 B 2.83e+00 7.27e-01 2004-09-20T03:00:42.863 C 3.58e+00 4.62e-01 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032909.5+312721 052.2895381 6.50e-07 +31.4558068 6.50e-07 A A 03290948+3127209 -1.81 -8.30e-01 5.10e-02 9.91e+00 6 YSOc_star+dust(IR2) 9.61 1.42 11.47 0.1630 0.78 4 3.47e+00 1.12e-01 1999-11-26T20:22:60 A 5.45e+00 1.71e-01 1999-11-26T20:22:60 A 5.59e+00 1.49e-01 1999-11-26T20:22:60 A 4.21e+00 2.12e-01 2004-02-10T08:34:30.264 A 4.64e+00 2.34e-01 2004-02-10T08:34:30.264 A 4.18e+00 2.28e-01 2004-02-10T08:34:30.264 A A 1 3.09 1.5 1.5 -45.0 3.72e+00 1.83e-01 2004-02-10T08:34:30.264 A 4.19e+00 2.06e-01 2004-02-10T08:34:30.264 A 3.89e+00 1.95e-01 2004-02-10T08:34:30.264 A A 1 2.97 1.7 1.7 -45.0 3.52e+00 1.82e-01 2004-02-10T08:34:30.264 A 3.72e+00 1.96e-01 2004-02-10T08:34:30.264 A 3.60e+00 1.80e-01 2004-02-10T08:34:30.264 A A 1 3.92 1.8 1.8 -45.0 4.98e+00 2.53e-01 2004-02-10T08:34:30.264 A 5.24e+00 2.68e-01 2004-02-10T08:34:30.264 A 5.12e+00 2.52e-01 2004-02-10T08:34:30.264 A A 1 4.98 1.8 1.8 -45.0 6.58e+00 7.28e-01 2004-09-19T22:12:44.497 A 7.04e+00 7.76e-01 2004-09-20T03:00:42.863 A 6.84e+00 7.02e-01 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032909.7+312256 052.2902168 1.24e-08 +31.3823127 1.24e-08 A A 03290964+3122564 -5.00 -1.26e+00 8.36e-02 1.36e+01 6 YSOc_star+dust(IR4) 11.51 1.15 8.15 0.1090 0.90 6 4.94e+01 1.00e+00 1999-11-26T20:22:60 A 8.80e+01 2.43e+00 1999-11-26T20:22:60 A 1.02e+02 2.08e+00 1999-11-26T20:22:60 A 7.08e+01 3.64e+00 2004-02-10T08:34:30.264 A 5.86e+01 3.87e+00 2004-02-10T08:34:30.264 A 7.07e+01 3.69e+00 2004-02-10T08:34:30.264 A B 1 3.09 1.5 1.5 -45.0 5.29e+01 2.70e+00 2004-02-10T08:34:30.264 A 5.14e+01 2.61e+00 2004-02-10T08:34:30.264 A 5.27e+01 2.62e+00 2004-02-10T08:34:30.264 A A 1 2.97 1.7 1.7 -45.0 4.66e+01 3.85e+00 2004-02-10T08:34:30.264 A 3.91e+01 3.81e+00 2004-02-10T08:34:30.264 A 4.56e+01 3.38e+00 2004-02-10T08:34:30.264 A A 2 8.50 3.0 2.5 -46.6 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 6.87e+01 1.30e+01 2004-02-10T08:34:30.264 B Q -2 4.98 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 1.14e+02 2.49e+01 2004-09-20T12:36:43.680 C Q -2 8.04 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032910.5+312335 052.2936222 3.21e-08 +31.3929767 3.21e-08 A A 03291046+3123348 -5.00 -8.50e-01 7.44e-02 1.09e+00 6 YSOc_star+dust(IR2) 18.16 1.42 10.66 0.1630 0.73 4 8.90e-01 5.00e-02 1999-11-26T20:22:60 A 3.03e+00 1.00e-01 1999-11-26T20:22:60 A 5.24e+00 1.25e-01 1999-11-26T20:22:60 A 5.06e+00 2.67e-01 2004-02-10T08:34:30.264 A 5.44e+00 2.75e-01 2004-02-10T08:34:30.264 A 5.11e+00 2.74e-01 2004-02-10T08:34:30.264 A A 1 3.09 1.5 1.5 -45.0 5.12e+00 2.51e-01 2004-02-10T08:34:30.264 A 5.11e+00 2.54e-01 2004-02-10T08:34:30.264 A 5.09e+00 2.49e-01 2004-02-10T08:34:30.264 A A 1 2.97 1.7 1.7 -45.0 5.15e+00 2.83e-01 2004-02-10T08:34:30.264 A 5.36e+00 3.00e-01 2004-02-10T08:34:30.264 A 5.29e+00 2.88e-01 2004-02-10T08:34:30.264 A A 1 3.92 1.8 1.8 -45.0 5.34e+00 3.87e-01 2004-02-10T08:34:30.264 A 5.86e+00 4.62e-01 2004-02-10T08:34:30.264 A 5.69e+00 4.52e-01 2004-02-10T08:34:30.264 A A 1 4.98 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 1.00e+01 2.41e+00 2004-09-20T12:36:43.680 C Q -2 8.04 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032910.7+311340 052.2943868 4.46e-09 +31.2277870 4.46e-09 A A null -5.00 7.40e-01 9.21e-02 2.21e+02 5 YSOc_red -999.00 -999.00 -999.00 -999.0000 -999.00 0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 4.83e-02 9.13e-03 2004-02-10T08:34:30.264 B 5.30e-02 7.58e-03 2004-02-10T08:34:30.264 B 5.04e-02 4.33e-03 2005-09-16T09:56:12.848 A A 1 1.77 1.5 1.5 -45.0 9.74e-01 6.36e-02 2004-02-10T08:34:30.264 A 9.65e-01 5.89e-02 2004-02-10T08:34:30.264 A 9.79e-01 5.65e-02 2004-02-10T08:34:30.264 A A 1 2.97 1.7 1.7 -45.0 5.65e-01 4.75e-02 2004-02-10T08:34:30.264 A 6.03e-01 5.29e-02 2004-02-10T08:34:30.264 A 5.82e-01 3.76e-02 2005-09-16T09:56:12.848 A A 1 4.85 2.5 2.5 -49.8 3.01e-01 6.88e-02 2004-02-10T08:34:30.264 C 2.79e-01 6.14e-02 2004-02-10T08:34:30.264 C 2.76e-01 4.46e-02 2004-02-10T08:34:30.264 B A 1 4.98 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 4.75e+00 7.69e-01 2004-09-20T12:36:43.680 B Q -2 8.04 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032910.8+311643 052.2951543 6.41e-07 +31.2784900 6.41e-07 A A 03291082+3116427 -4.41 -1.30e-01 5.04e-02 9.75e+00 6 YSOc_star+dust(IR1) 15.41 2.40 11.36 0.3600 1.64 3 8.74e-01 5.48e-02 1999-11-26T20:23:08 A 2.33e+00 9.45e-02 1999-11-26T20:23:08 A 4.06e+00 1.23e-01 1999-11-26T20:23:08 A 7.38e+00 3.81e-01 2004-02-10T08:34:30.264 A 5.63e+00 2.91e-01 2004-02-10T08:34:30.264 A 5.62e+00 3.49e-01 2004-02-10T08:34:30.264 A B 1 3.09 1.5 1.5 -45.0 9.58e+00 4.72e-01 2004-02-10T08:34:30.264 A 7.05e+00 3.62e-01 2004-02-10T08:34:30.264 A 7.74e+00 4.29e-01 2004-02-10T08:34:30.264 A C 1 2.97 1.7 1.7 -45.0 1.23e+01 6.09e-01 2004-02-10T08:34:30.264 A 9.08e+00 4.59e-01 2004-02-10T08:34:30.264 A 1.04e+01 5.32e-01 2004-02-10T08:34:30.264 A C 1 3.92 1.8 1.8 -45.0 2.05e+01 9.90e-01 2004-02-10T08:34:30.264 A 1.47e+01 7.36e-01 2004-02-10T08:34:30.264 A 1.65e+01 8.42e-01 2004-02-10T08:34:30.264 A C 1 4.98 1.8 1.8 -45.0 2.38e+01 2.25e+00 2004-09-19T22:12:44.497 A 2.36e+01 2.24e+00 2004-09-20T03:00:42.863 A 2.39e+01 2.24e+00 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032910.9+311838 052.2956068 3.56e-09 +31.3105091 3.56e-09 A C null -5.00 1.05e+00 1.13e-01 2.41e+01 5 YSOc_red -999.00 -999.00 -999.00 -999.0000 -999.00 0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 2.95e-01 8.37e-02 2004-02-10T08:34:30.264 C Q -2 3.09 1.5 1.5 -45.0 -9.99e+02 -9.99e+02 null U 1.58e+00 1.88e-01 2004-02-10T08:34:30.264 K 1.23e+00 1.28e-01 2004-02-10T08:34:30.264 K Q 7 2.27 1.7 1.7 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 7.40e-01 1.11e-01 2004-02-10T08:34:30.264 B Q 7 3.92 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 5.98e-01 1.44e-01 2004-02-10T08:34:30.264 C Q -2 4.98 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 3.22e+01 5.24e+00 2004-09-20T12:36:43.680 B Q -2 8.04 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032911.3+311831 052.2969172 2.04e-06 +31.3087350 2.04e-06 A A null -5.00 1.94e+00 6.39e-02 1.18e+02 5 YSOc_red -999.00 -999.00 -999.00 -999.0000 -999.00 0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 6.73e-01 9.66e-02 2004-02-10T08:34:30.264 B 7.78e-01 7.25e-02 2004-02-10T08:34:30.264 A 7.18e-01 6.91e-02 2004-02-10T08:34:30.264 A A 2 6.42 2.7 2.1 -66.2 3.77e+00 2.94e-01 2004-02-10T08:34:30.264 A 3.79e+00 2.55e-01 2004-02-10T08:34:30.264 A 3.94e+00 2.60e-01 2004-02-10T08:34:30.264 A A 1 2.97 1.7 1.7 -45.0 4.71e+00 3.31e-01 2004-02-10T08:34:30.264 A 5.57e+00 3.80e-01 2004-02-10T08:34:30.264 A 5.00e+00 3.27e-01 2004-02-10T08:34:30.264 A A 2 6.35 2.8 2.0 -78.8 3.92e+00 2.84e-01 2004-02-10T08:34:30.264 A 4.35e+00 3.07e-01 2004-02-10T08:34:30.264 A 4.13e+00 2.66e-01 2004-02-10T08:34:30.264 A A 1 4.98 1.8 1.8 -45.0 5.34e+02 4.99e+01 2004-09-19T22:12:44.497 A 5.17e+02 4.91e+01 2004-09-20T03:00:42.863 A 5.22e+02 4.88e+01 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032911.9+312127 052.2995341 3.39e-08 +31.3574938 3.39e-08 A A 03291188+3121271 -5.00 3.40e-01 7.85e-02 1.31e+01 6 YSOc -999.00 -999.00 -999.00 -999.0000 -999.00 0 2.07e-01 -9.99e+02 1999-11-26T20:22:60 U 5.38e-01 5.75e-02 1999-11-26T20:22:60 B 4.95e+00 1.28e-01 1999-11-26T20:22:60 A 1.68e+01 8.94e-01 2004-02-10T08:34:30.264 A 1.74e+01 9.08e-01 2004-02-10T08:34:30.264 A 1.64e+01 9.24e-01 2004-02-10T08:34:30.264 A A 1 3.09 1.5 1.5 -45.0 2.50e+01 1.27e+00 2004-02-10T08:34:30.264 A 2.57e+01 1.29e+00 2004-02-10T08:34:30.264 A 2.49e+01 1.29e+00 2004-02-10T08:34:30.264 A A 1 2.97 1.7 1.7 -45.0 3.22e+01 1.89e+00 2004-02-10T08:34:30.264 A 3.13e+01 2.05e+00 2004-02-10T08:34:30.264 A 3.22e+01 1.81e+00 2004-02-10T08:34:30.264 A A 1 3.92 1.8 1.8 -45.0 3.96e+01 2.99e+00 2004-02-10T08:34:30.264 A 3.63e+01 3.11e+00 2004-02-10T08:34:30.264 A 3.93e+01 2.92e+00 2004-02-10T08:34:30.264 A A 1 4.98 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 5.20e+01 1.72e+01 2004-09-20T12:36:43.680 C Q -2 8.04 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032913.1+312253 052.3047388 6.39e-07 +31.3813362 6.39e-07 A A 03291312+3122529 -5.00 -1.05e+00 5.08e-02 3.49e+00 6 YSOc_star+dust(IR2) 19.52 1.44 7.74 0.1690 2.30 4 1.14e+01 2.30e-01 1999-11-26T20:22:60 A 3.66e+01 9.44e-01 1999-11-26T20:22:60 A 6.03e+01 1.22e+00 1999-11-26T20:22:60 A 9.01e+01 5.01e+00 2004-02-10T08:34:30.264 A 7.72e+01 4.18e+00 2004-02-10T08:34:30.264 A 7.32e+01 4.60e+00 2004-02-10T08:34:30.264 A A 7 3.09 1.5 1.5 -45.0 8.71e+01 4.28e+00 2004-02-10T08:34:30.264 A 7.92e+01 4.09e+00 2004-02-10T08:34:30.264 A 8.32e+01 4.06e+00 2004-02-10T08:34:30.264 A A 2 5.73 2.3 2.2 -47.2 8.40e+01 4.03e+00 2004-02-10T08:34:30.264 A 7.39e+01 3.68e+00 2004-02-10T08:34:30.264 A 7.90e+01 3.80e+00 2004-02-10T08:34:30.264 A A 1 3.92 1.8 1.8 -45.0 7.68e+01 3.88e+00 2004-02-10T08:34:30.264 A 7.19e+01 4.05e+00 2004-02-10T08:34:30.264 A 7.20e+01 3.76e+00 2004-02-10T08:34:30.264 A A 1 4.98 1.8 1.8 -45.0 5.83e+01 5.77e+00 2004-09-19T22:12:44.497 A 6.05e+01 6.51e+00 2004-09-20T03:00:42.863 A 6.00e+01 5.82e+00 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032914.4+311444 052.3100057 2.92e-06 +31.2455703 2.92e-06 A A 03291433+3114441 -5.00 -3.20e-01 5.65e-02 4.66e+01 6 YSOc -999.00 -999.00 -999.00 -999.0000 -999.00 0 1.25e-01 -9.99e+02 1999-11-26T20:23:08 U 3.99e-01 -9.99e+02 1999-11-26T20:23:08 U 1.22e+00 1.26e-01 1999-11-26T20:23:08 B 2.37e+00 1.97e-01 2004-02-10T08:34:30.264 K -9.99e+02 -9.99e+02 null U 1.89e+00 1.13e-01 2005-09-16T09:56:12.848 A Q 7 1.77 1.5 1.5 -45.0 9.95e+00 1.08e+00 2004-02-10T08:34:30.264 A 8.39e+00 6.24e-01 2004-02-10T08:34:30.264 A 8.20e+00 7.09e-01 2004-02-10T08:34:30.264 A A 7 2.97 1.7 1.7 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 4.63e+00 3.56e-01 2005-09-16T09:56:12.848 A Q 7 3.92 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U 3.35e+00 3.14e-01 2004-02-10T08:34:30.264 A 3.45e+00 2.45e-01 2004-02-10T08:34:30.264 A Q 7 4.98 1.8 1.8 -45.0 8.63e+00 9.14e-01 2004-09-19T22:12:44.497 A 8.25e+00 8.59e-01 2004-09-20T03:00:42.863 A 8.49e+00 8.40e-01 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032916.6+312349 052.3191936 5.45e-07 +31.3970563 5.45e-07 A A 03291659+3123495 -5.00 -8.50e-01 5.63e-02 7.77e+00 6 YSOc_star+dust(IR2) 13.65 1.41 9.48 0.1620 1.02 4 7.96e+00 1.76e-01 1999-11-26T20:22:60 A 1.88e+01 5.71e-01 1999-11-26T20:22:60 A 2.25e+01 4.76e-01 1999-11-26T20:22:60 A 2.22e+01 1.12e+00 2004-02-10T08:34:30.264 A 2.11e+01 1.06e+00 2004-02-10T08:34:30.264 A 2.05e+01 1.09e+00 2004-02-10T08:34:30.264 A A 1 3.09 1.5 1.5 -45.0 2.31e+01 1.11e+00 2004-02-10T08:34:30.264 A 2.17e+01 1.08e+00 2004-02-10T08:34:30.264 A 2.21e+01 1.08e+00 2004-02-10T08:34:30.264 A A 1 2.97 1.7 1.7 -45.0 2.45e+01 1.20e+00 2004-02-10T08:34:30.264 A 2.41e+01 1.18e+00 2004-02-10T08:34:30.264 A 2.44e+01 1.22e+00 2004-02-10T08:34:30.264 A A 1 3.92 1.8 1.8 -45.0 3.26e+01 1.71e+00 2004-02-10T08:34:30.264 A 2.90e+01 1.67e+00 2004-02-10T08:34:30.264 A 3.08e+01 1.54e+00 2004-02-10T08:34:30.264 A A 1 4.98 1.8 1.8 -45.0 1.75e+01 4.10e+00 2004-09-19T22:12:44.497 C 1.79e+01 3.00e+00 2004-09-20T03:00:42.863 B 1.82e+01 2.44e+00 2004-09-20T12:36:43.680 A A 7 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032916.7+311618 052.3195548 5.59e-07 +31.2717147 5.59e-07 A A 03291667+3116182 -5.00 -2.40e+00 5.84e-02 6.80e-01 6 YSOc_star+dust(MP1) 4.92 1.10 9.84 0.0987 0.38 7 4.22e+01 9.33e-01 1999-11-26T20:23:08 A 5.14e+01 1.42e+00 1999-11-26T20:23:08 A 4.48e+01 9.08e-01 1999-11-26T20:23:08 A 2.32e+01 1.16e+00 2004-02-10T08:34:30.264 A 2.34e+01 1.21e+00 2004-02-10T08:34:30.264 A 2.22e+01 1.15e+00 2005-09-16T09:56:12.848 A A 1 1.77 1.5 1.5 -45.0 1.64e+01 8.08e-01 2004-02-10T08:34:30.264 A 1.58e+01 7.95e-01 2004-02-10T08:34:30.264 A 1.56e+01 8.08e-01 2004-02-10T08:34:30.264 A A 1 2.97 1.7 1.7 -45.0 1.08e+01 5.32e-01 2004-02-10T08:34:30.264 A 1.10e+01 5.43e-01 2004-02-10T08:34:30.264 A 1.08e+01 5.19e-01 2005-09-16T09:56:12.848 A A 1 2.54 1.8 1.8 -45.0 6.82e+00 3.30e-01 2004-02-10T08:34:30.264 A 6.76e+00 3.35e-01 2004-02-10T08:34:30.264 A 6.83e+00 3.26e-01 2004-02-10T08:34:30.264 A A 1 4.98 1.8 1.8 -45.0 2.11e+00 4.10e-01 2004-09-19T22:12:44.497 B 1.47e+00 3.18e-01 2004-09-20T03:00:42.863 C 1.83e+00 2.87e-01 2004-09-20T12:36:43.680 B A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032916.8+312325 052.3201296 7.94e-07 +31.3903127 7.94e-07 A A 03291681+3123252 -2.11 -1.40e-01 5.04e-02 3.12e+01 6 YSOc_star+dust(IR2) 13.01 1.42 11.99 0.1610 1.46 4 1.09e+00 5.40e-02 1999-11-26T20:22:60 A 1.92e+00 7.60e-02 1999-11-26T20:22:60 A 2.47e+00 7.96e-02 1999-11-26T20:22:60 A 2.13e+00 1.09e-01 2004-02-10T08:34:30.264 A 2.09e+00 1.11e-01 2004-02-10T08:34:30.264 A 2.12e+00 1.09e-01 2004-02-10T08:34:30.264 A A 1 3.09 1.5 1.5 -45.0 2.12e+00 1.05e-01 2004-02-10T08:34:30.264 A 2.02e+00 1.03e-01 2004-02-10T08:34:30.264 A 2.05e+00 1.02e-01 2004-02-10T08:34:30.264 A A 1 2.97 1.7 1.7 -45.0 2.33e+00 1.29e-01 2004-02-10T08:34:30.264 A 2.15e+00 4.75e-01 2004-02-10T08:34:30.264 C 2.28e+00 1.27e-01 2004-02-10T08:34:30.264 A A 1 3.92 1.8 1.8 -45.0 3.53e+00 2.06e-01 2004-02-10T08:34:30.264 A -9.99e+02 -9.99e+02 null U 3.16e+00 1.61e-01 2004-02-10T08:34:30.264 A Q 1 4.98 1.8 1.8 -45.0 2.04e+01 2.19e+00 2004-09-19T22:12:44.497 A 2.03e+01 2.11e+00 2004-09-20T03:00:42.863 A 2.02e+01 2.02e+00 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032917.7+312245 052.3236643 7.14e-07 +31.3791726 7.14e-07 A A 03291766+3122451 -5.00 -7.70e-01 4.97e-02 6.30e-01 6 YSOc_star+dust(IR1) 8.64 2.37 7.35 0.3550 0.22 3 1.63e+02 3.31e+00 1999-11-26T20:22:60 A 2.78e+02 7.69e+00 1999-11-26T20:22:60 A 3.12e+02 6.32e+00 1999-11-26T20:22:60 A 3.47e+02 1.90e+01 2004-02-10T08:34:30.264 A 3.46e+02 2.06e+01 2004-02-10T08:34:30.264 A 3.43e+02 1.91e+01 2004-02-10T08:34:30.264 A A 7 3.09 1.5 1.5 -45.0 3.49e+02 2.21e+01 2004-02-10T08:34:30.264 K 3.56e+02 1.83e+01 2004-02-10T08:34:30.264 A 3.41e+02 1.77e+01 2004-02-10T08:34:30.264 A A 1 2.27 1.7 1.7 -45.0 3.84e+02 2.17e+01 2004-02-10T08:34:30.264 A 3.70e+02 2.17e+01 2004-02-10T08:34:30.264 A 4.05e+02 2.05e+01 2004-02-10T08:34:30.264 A A 1 3.92 1.8 1.8 -45.0 4.09e+02 2.09e+01 2004-02-10T08:34:30.264 A 4.09e+02 2.19e+01 2004-02-10T08:34:30.264 A 4.17e+02 2.37e+01 2004-02-10T08:34:30.264 A A 7 4.98 1.8 1.8 -45.0 5.26e+02 4.89e+01 2004-09-19T22:12:44.497 A 5.20e+02 4.82e+01 2004-09-20T03:00:42.863 A 5.18e+02 4.81e+01 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032917.8+311948 052.3240635 7.18e-07 +31.3300052 7.18e-07 A A 03291776+3119481 -1.70 -9.70e-01 5.12e-02 4.23e+00 6 YSOc_star+dust(IR2) 12.65 1.41 11.41 0.1620 1.32 4 1.91e+00 7.22e-02 1999-11-26T20:23:08 A 3.55e+00 1.18e-01 1999-11-26T20:23:08 A 4.25e+00 1.14e-01 1999-11-26T20:23:08 A 3.75e+00 1.87e-01 2004-02-10T08:34:30.264 A 3.78e+00 1.90e-01 2004-02-10T08:34:30.264 A 3.69e+00 1.93e-01 2004-02-10T08:34:30.264 A A 1 3.09 1.5 1.5 -45.0 3.31e+00 1.63e-01 2004-02-10T08:34:30.264 A 3.45e+00 1.71e-01 2004-02-10T08:34:30.264 A 3.39e+00 1.67e-01 2004-02-10T08:34:30.264 A A 1 2.97 1.7 1.7 -45.0 3.09e+00 1.72e-01 2004-02-10T08:34:30.264 A 3.10e+00 1.68e-01 2004-02-10T08:34:30.264 A 3.12e+00 1.66e-01 2004-02-10T08:34:30.264 A A 1 3.92 1.8 1.8 -45.0 3.16e+00 1.65e-01 2004-02-10T08:34:30.264 A 3.37e+00 1.82e-01 2004-02-10T08:34:30.264 A 3.42e+00 1.83e-01 2004-02-10T08:34:30.264 A A 1 4.98 1.8 1.8 -45.0 4.69e+00 5.25e-01 2004-09-19T22:12:44.497 A 4.87e+00 5.55e-01 2004-09-20T03:00:42.863 A 4.77e+00 4.89e-01 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032918.3+312320 052.3260865 7.11e-07 +31.3888523 7.11e-07 A A null -2.19 1.26e+00 5.84e-02 5.49e+01 5 YSOc_red -999.00 -999.00 -999.00 -999.0000 -999.00 0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 9.51e-01 5.99e-02 2004-02-10T08:34:30.264 A 8.86e-01 5.68e-02 2004-02-10T08:34:30.264 A 9.16e-01 5.21e-02 2004-02-10T08:34:30.264 A A 1 3.09 1.5 1.5 -45.0 2.92e+00 1.56e-01 2004-02-10T08:34:30.264 A 2.75e+00 1.51e-01 2004-02-10T08:34:30.264 A 2.85e+00 1.45e-01 2004-02-10T08:34:30.264 A A 1 2.97 1.7 1.7 -45.0 6.24e+00 2.98e-01 2004-02-10T08:34:30.264 A 6.41e+00 3.23e-01 2004-02-10T08:34:30.264 A 6.25e+00 2.99e-01 2004-02-10T08:34:30.264 A A 1 3.92 1.8 1.8 -45.0 1.01e+01 5.01e-01 2004-02-10T08:34:30.264 A 1.08e+01 5.96e-01 2004-02-10T08:34:30.264 A 1.07e+01 5.44e-01 2004-02-10T08:34:30.264 A A 1 4.98 1.8 1.8 -45.0 6.03e+01 6.90e+00 2004-09-19T22:12:44.497 A 5.73e+01 6.22e+00 2004-09-20T03:00:42.863 A 5.93e+01 6.06e+00 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032918.7+312018 052.3277790 8.83e-07 +31.3382578 8.83e-07 A A 03291865+3120178 -2.01 3.90e-01 5.17e-02 1.93e+01 6 YSOc_star+dust(IR2) 20.95 1.78 12.17 0.1820 2.16 4 1.58e-01 4.41e-02 1999-11-26T20:23:08 D 4.20e-01 6.73e-02 1999-11-26T20:23:08 C 9.57e-01 8.02e-02 1999-11-26T20:23:08 A 1.11e+00 5.84e-02 2004-02-10T08:34:30.264 A 1.12e+00 5.81e-02 2004-02-10T08:34:30.264 A 1.09e+00 5.96e-02 2004-02-10T08:34:30.264 A A 1 3.09 1.5 1.5 -45.0 1.20e+00 6.20e-02 2004-02-10T08:34:30.264 A 1.36e+00 6.98e-02 2004-02-10T08:34:30.264 A 1.30e+00 6.53e-02 2004-02-10T08:34:30.264 A A 1 2.97 1.7 1.7 -45.0 1.41e+00 8.96e-02 2004-02-10T08:34:30.264 A 1.40e+00 9.01e-02 2004-02-10T08:34:30.264 A 1.41e+00 8.30e-02 2004-02-10T08:34:30.264 A A 1 3.92 1.8 1.8 -45.0 2.52e+00 1.41e-01 2004-02-10T08:34:30.264 A 2.75e+00 1.52e-01 2004-02-10T08:34:30.264 A 2.66e+00 1.49e-01 2004-02-10T08:34:30.264 A A 1 4.98 1.8 1.8 -45.0 2.08e+01 1.98e+00 2004-09-19T22:12:44.497 A 2.22e+01 2.09e+00 2004-09-20T03:00:42.863 A 2.17e+01 2.03e+00 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032918.7+312325 052.3280805 5.56e-07 +31.3903765 5.56e-07 A A 03291872+3123254 -5.00 -2.30e-01 5.00e-02 1.55e+01 6 YSOc_star+dust(IR2) 7.82 1.43 9.27 0.1670 1.16 4 4.21e+01 9.30e-01 1999-11-26T20:22:60 A 5.49e+01 1.52e+00 1999-11-26T20:22:60 A 4.92e+01 9.97e-01 1999-11-26T20:22:60 A 4.57e+01 2.24e+00 2004-02-10T08:34:30.264 A 4.28e+01 2.14e+00 2004-02-10T08:34:30.264 A 3.60e+01 2.16e+00 2004-02-10T08:34:30.264 A A 1 3.09 1.5 1.5 -45.0 4.60e+01 2.34e+00 2004-02-10T08:34:30.264 A 4.20e+01 2.12e+00 2004-02-10T08:34:30.264 A 4.19e+01 2.23e+00 2004-02-10T08:34:30.264 A A 1 2.97 1.7 1.7 -45.0 5.56e+01 2.62e+00 2004-02-10T08:34:30.264 A 5.13e+01 2.46e+00 2004-02-10T08:34:30.264 A 5.33e+01 2.53e+00 2004-02-10T08:34:30.264 A A 1 3.92 1.8 1.8 -45.0 1.02e+02 6.15e+00 2004-02-10T08:34:30.264 A 9.04e+01 4.97e+00 2004-02-10T08:34:30.264 A 9.29e+01 4.94e+00 2004-02-10T08:34:30.264 A A 1 4.98 1.8 1.8 -45.0 1.85e+02 1.76e+01 2004-09-19T22:12:44.497 A 1.82e+02 1.72e+01 2004-09-20T03:00:42.863 A 1.84e+02 1.72e+01 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032919.8+312457 052.3324315 5.83e-08 +31.4158270 5.83e-08 A A 03291977+3124572 -5.00 -2.33e+00 7.38e-02 8.26e+00 6 YSOc_star+dust(MP1) 2.31 1.11 8.05 0.1010 0.81 7 4.67e+02 8.60e+00 1999-11-26T20:22:60 A 3.94e+02 1.38e+01 1999-11-26T20:22:60 A 3.04e+02 6.17e+00 1999-11-26T20:22:60 A 1.45e+02 7.55e+00 2004-02-10T08:34:30.264 A 1.47e+02 7.94e+00 2004-02-10T08:34:30.264 A 1.26e+02 8.55e+00 2004-02-10T08:34:30.264 A A 1 3.09 1.5 1.5 -45.0 9.99e+01 4.90e+00 2004-02-10T08:34:30.264 A 9.23e+01 5.05e+00 2004-02-10T08:34:30.264 A 9.67e+01 4.82e+00 2004-02-10T08:34:30.264 A A 2 5.73 2.3 2.2 -56.2 6.63e+01 3.36e+00 2004-02-10T08:34:30.264 A 6.35e+01 3.43e+00 2004-02-10T08:34:30.264 A 6.56e+01 3.29e+00 2004-02-10T08:34:30.264 A A 1 3.92 1.8 1.8 -45.0 3.94e+01 2.88e+00 2004-02-10T08:34:30.264 A 3.64e+01 2.98e+00 2004-02-10T08:34:30.264 A 3.88e+01 2.60e+00 2004-02-10T08:34:30.264 A A 1 4.98 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 3.59e+01 9.11e+00 2004-09-20T12:36:43.680 C Q -2 8.04 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032920.1+312408 052.3335841 9.18e-07 +31.4020922 9.18e-07 A A 03292003+3124076 -5.00 4.20e-01 5.21e-02 4.32e+00 6 YSOc_star+dust(IR2) 48.47 1.95 6.67 0.2050 0.56 3 2.18e-01 -9.99e+02 1999-11-26T20:22:60 U 1.35e+00 9.32e-02 1999-11-26T20:22:60 A 1.02e+01 2.72e-01 1999-11-26T20:22:60 A 3.14e+01 1.79e+00 2004-02-10T08:34:30.264 A 3.16e+01 1.84e+00 2004-02-10T08:34:30.264 A 2.97e+01 1.84e+00 2004-02-10T08:34:30.264 A A 1 3.09 1.5 1.5 -45.0 4.60e+01 2.30e+00 2004-02-10T08:34:30.264 A 4.32e+01 2.28e+00 2004-02-10T08:34:30.264 A 4.40e+01 2.26e+00 2004-02-10T08:34:30.264 A A 1 2.97 1.7 1.7 -45.0 6.02e+01 3.91e+00 2004-02-10T08:34:30.264 A 5.93e+01 3.95e+00 2004-02-10T08:34:30.264 A 6.01e+01 3.60e+00 2004-02-10T08:34:30.264 A A 1 3.92 1.8 1.8 -45.0 9.94e+01 8.41e+00 2004-02-10T08:34:30.264 A 8.26e+01 7.80e+00 2004-02-10T08:34:30.264 A 9.40e+01 7.32e+00 2004-02-10T08:34:30.264 A A 1 4.98 1.8 1.8 -45.0 3.74e+02 3.66e+01 2004-09-19T22:12:44.497 A 3.62e+02 3.44e+01 2004-09-20T03:00:42.863 A 3.65e+02 3.44e+01 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032921.6+312110 052.3398823 5.47e-07 +31.3528721 5.47e-07 A A 03292155+3121104 -5.00 -1.03e+00 4.89e-02 1.60e+01 6 YSOc_star+dust(IR2) 5.98 1.40 10.61 0.1610 0.33 4 1.76e+01 3.56e-01 1999-11-26T20:23:08 A 2.09e+01 6.17e-01 1999-11-26T20:23:08 A 1.88e+01 3.82e-01 1999-11-26T20:23:08 A 1.19e+01 5.69e-01 2004-02-10T08:34:30.264 A 1.18e+01 5.99e-01 2004-02-10T08:34:30.264 A 1.14e+01 5.85e-01 2004-02-10T08:34:30.264 A A 1 3.09 1.5 1.5 -45.0 1.04e+01 5.08e-01 2004-02-10T08:34:30.264 A 9.91e+00 4.96e-01 2004-02-10T08:34:30.264 A 9.97e+00 4.86e-01 2004-02-10T08:34:30.264 A A 1 2.97 1.7 1.7 -45.0 9.01e+00 4.42e-01 2004-02-10T08:34:30.264 A 8.71e+00 4.24e-01 2004-02-10T08:34:30.264 A 8.79e+00 4.24e-01 2004-02-10T08:34:30.264 A A 1 3.92 1.8 1.8 -45.0 1.08e+01 2.60e+00 2004-02-10T08:34:30.264 C 1.02e+01 4.95e-01 2004-02-10T08:34:30.264 A 1.01e+01 5.03e-01 2004-02-10T08:34:30.264 A A 1 4.98 1.8 1.8 -45.0 1.57e+01 1.52e+00 2004-09-19T22:12:44.497 A 1.58e+01 1.52e+00 2004-09-20T03:00:42.863 A 1.58e+01 1.50e+00 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032921.9+311536 052.3411454 5.50e-07 +31.2600529 5.50e-07 A A 03292187+3115363 -5.00 -1.13e+00 4.82e-02 4.15e+00 6 YSOc_star+dust(IR2) 12.13 1.40 7.99 0.1590 1.61 4 5.40e+01 1.09e+00 1999-11-26T20:23:08 A 8.91e+01 2.46e+00 1999-11-26T20:23:08 A 1.05e+02 1.94e+00 1999-11-26T20:23:08 A 1.04e+02 5.39e+00 2004-02-10T08:34:30.264 A 9.72e+01 5.97e+00 2004-02-10T08:34:30.264 A 8.98e+01 4.44e+00 2005-09-16T09:56:12.848 A A 1 1.77 1.5 1.5 -45.0 8.92e+01 4.73e+00 2004-02-10T08:34:30.264 A 8.86e+01 5.68e+00 2004-02-10T08:34:30.264 A 9.26e+01 4.89e+00 2004-02-10T08:34:30.264 A A 1 2.97 1.7 1.7 -45.0 7.94e+01 3.82e+00 2004-02-10T08:34:30.264 A 8.45e+01 4.39e+00 2004-02-10T08:34:30.264 A 7.23e+01 3.41e+00 2005-09-16T09:56:12.848 A A 1 2.54 1.8 1.8 -45.0 7.10e+01 3.47e+00 2004-02-10T08:34:30.264 A 7.99e+01 4.06e+00 2004-02-10T08:34:30.264 A 7.46e+01 3.73e+00 2004-02-10T08:34:30.264 A A 1 4.98 1.8 1.8 -45.0 8.40e+01 7.78e+00 2004-09-19T22:12:44.497 A 8.56e+01 7.94e+00 2004-09-20T03:00:42.863 A 8.47e+01 7.84e+00 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032923.2+312030 052.3465267 5.18e-07 +31.3417298 5.18e-07 A A 03292314+3120303 -5.00 -8.30e-01 4.85e-02 9.87e+00 6 YSOc_star+dust(IR2) 9.33 1.41 9.97 0.1630 2.01 4 1.75e+01 3.55e-01 1999-11-26T20:23:08 A 2.23e+01 6.37e-01 1999-11-26T20:23:08 A 2.15e+01 4.55e-01 1999-11-26T20:23:08 A 1.81e+01 8.82e-01 2004-02-10T08:34:30.264 A 1.86e+01 9.55e-01 2004-02-10T08:34:30.264 A 1.74e+01 9.43e-01 2004-02-10T08:34:30.264 A A 1 3.09 1.5 1.5 -45.0 1.66e+01 8.14e-01 2004-02-10T08:34:30.264 A 1.60e+01 7.87e-01 2004-02-10T08:34:30.264 A 1.61e+01 8.04e-01 2004-02-10T08:34:30.264 A A 1 2.97 1.7 1.7 -45.0 1.44e+01 7.10e-01 2004-02-10T08:34:30.264 A 1.44e+01 7.18e-01 2004-02-10T08:34:30.264 A 1.43e+01 7.03e-01 2004-02-10T08:34:30.264 A A 1 3.92 1.8 1.8 -45.0 1.82e+01 8.83e-01 2004-02-10T08:34:30.264 A 1.85e+01 8.88e-01 2004-02-10T08:34:30.264 A 1.84e+01 8.74e-01 2004-02-10T08:34:30.264 A A 1 4.98 1.8 1.8 -45.0 2.97e+01 2.78e+00 2004-09-19T22:12:44.497 A 2.96e+01 2.75e+00 2004-09-20T03:00:42.863 A 2.99e+01 2.77e+00 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032923.2+312653 052.3468674 6.20e-07 +31.4480800 6.20e-07 A A 03292322+3126531 -5.00 -1.02e+00 5.03e-02 1.28e+00 6 YSOc_star+dust(IR2) 10.23 1.43 10.88 0.1660 1.87 4 5.82e+00 1.39e-01 1999-11-26T20:22:60 A 8.49e+00 2.43e-01 1999-11-26T20:22:60 A 8.45e+00 2.10e-01 1999-11-26T20:22:60 A 7.02e+00 3.68e-01 2004-02-10T08:34:30.264 A 8.07e+00 4.16e-01 2004-02-10T08:34:30.264 A 7.11e+00 4.12e-01 2004-02-10T08:34:30.264 A A 1 3.09 1.5 1.5 -45.0 7.12e+00 3.51e-01 2004-02-10T08:34:30.264 A 7.69e+00 3.92e-01 2004-02-10T08:34:30.264 A 7.33e+00 3.63e-01 2004-02-10T08:34:30.264 A A 1 2.97 1.7 1.7 -45.0 6.50e+00 3.33e-01 2004-02-10T08:34:30.264 A 7.13e+00 3.62e-01 2004-02-10T08:34:30.264 A 6.77e+00 3.38e-01 2004-02-10T08:34:30.264 A A 1 3.92 1.8 1.8 -45.0 7.26e+00 3.57e-01 2004-02-10T08:34:30.264 A 7.95e+00 3.98e-01 2004-02-10T08:34:30.264 A 7.66e+00 3.67e-01 2004-02-10T08:34:30.264 A A 1 4.98 1.8 1.8 -45.0 6.94e+00 7.00e-01 2004-09-19T22:12:44.497 A 7.48e+00 8.32e-01 2004-09-20T03:00:42.863 A 7.09e+00 6.99e-01 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032924.1+311958 052.3503733 7.16e-07 +31.3326746 7.16e-07 A A 03292407+3119577 -5.00 3.60e-01 4.89e-02 8.58e+01 6 YSOc_star+dust(IR2) 12.76 1.44 12.01 0.1630 0.80 4 9.86e-01 6.81e-02 1999-11-26T20:23:08 A 2.05e+00 1.10e-01 1999-11-26T20:23:08 A 2.49e+00 1.01e-01 1999-11-26T20:23:08 A 2.13e+00 1.11e-01 2004-02-10T08:34:30.264 A 2.03e+00 1.05e-01 2004-02-10T08:34:30.264 A 2.10e+00 1.10e-01 2004-02-10T08:34:30.264 A A 1 3.09 1.5 1.5 -45.0 2.04e+00 1.04e-01 2004-02-10T08:34:30.264 A 1.97e+00 9.72e-02 2004-02-10T08:34:30.264 A 2.00e+00 9.87e-02 2004-02-10T08:34:30.264 A A 1 2.97 1.7 1.7 -45.0 2.20e+00 1.34e-01 2004-02-10T08:34:30.264 A 2.05e+00 1.16e-01 2004-02-10T08:34:30.264 A 2.07e+00 1.17e-01 2004-02-10T08:34:30.264 A A 1 3.92 1.8 1.8 -45.0 3.58e+00 1.86e-01 2004-02-10T08:34:30.264 A 3.40e+00 2.01e-01 2004-02-10T08:34:30.264 A 3.50e+00 1.72e-01 2004-02-10T08:34:30.264 A A 1 4.98 1.8 1.8 -45.0 6.90e+01 6.41e+00 2004-09-19T22:12:44.497 A 6.64e+01 6.15e+00 2004-09-20T03:00:42.863 A 6.74e+01 6.23e+00 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032925.9+312640 052.3580403 7.74e-07 +31.4444702 7.74e-07 A A 03292591+3126401 -5.00 -6.00e-01 5.84e-02 1.67e+01 5 YSOc -999.00 -999.00 -999.00 -999.0000 -999.00 0 6.32e+01 -9.99e+02 1999-11-26T20:22:60 U 1.03e+02 3.23e+00 1999-11-26T20:22:60 A 1.05e+02 -9.99e+02 1999-11-26T20:22:60 U 8.61e+01 4.56e+00 2004-02-10T08:34:30.264 A 1.02e+02 6.11e+00 2004-02-10T08:34:30.264 A 8.16e+01 5.52e+00 2004-02-10T08:34:30.264 A B 1 3.09 1.5 1.5 -45.0 9.82e+01 5.52e+00 2004-02-10T08:34:30.264 A 9.32e+01 6.41e+00 2004-02-10T08:34:30.264 A 9.70e+01 5.45e+00 2004-02-10T08:34:30.264 A A 1 2.97 1.7 1.7 -45.0 1.10e+02 5.43e+00 2004-02-10T08:34:30.264 A 1.28e+02 6.37e+00 2004-02-10T08:34:30.264 A 1.17e+02 5.95e+00 2004-02-10T08:34:30.264 A B 1 3.92 1.8 1.8 -45.0 1.80e+02 9.07e+00 2004-02-10T08:34:30.264 A 1.79e+02 1.03e+01 2004-02-10T08:34:30.264 A 1.80e+02 9.94e+00 2004-02-10T08:34:30.264 A A 7 4.98 1.8 1.8 -45.0 1.39e+02 1.30e+01 2004-09-19T22:12:44.497 A 1.48e+02 1.37e+01 2004-09-20T03:00:42.863 A 1.43e+02 1.33e+01 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032926.8+312648 052.3617159 1.13e-06 +31.4465443 1.13e-06 A A 03292681+3126475 -5.00 -1.72e+00 5.04e-02 2.49e+01 6 YSOc_star+dust(MP1) 5.92 1.11 8.95 0.1010 0.93 7 7.41e+01 1.57e+00 1999-11-26T20:22:60 A 1.03e+02 2.66e+00 1999-11-26T20:22:60 A 8.96e+01 2.06e+00 1999-11-26T20:22:60 A 4.30e+01 2.71e+00 2004-02-10T08:34:30.264 A 4.76e+01 2.99e+00 2004-02-10T08:34:30.264 A 4.72e+01 2.73e+00 2004-02-10T08:34:30.264 A A 1 3.09 1.5 1.5 -45.0 3.09e+01 1.82e+00 2004-02-10T08:34:30.264 A 3.15e+01 2.10e+00 2004-02-10T08:34:30.264 A 3.20e+01 1.79e+00 2004-02-10T08:34:30.264 A A 1 2.97 1.7 1.7 -45.0 2.32e+01 1.37e+00 2004-02-10T08:34:30.264 A 2.31e+01 1.39e+00 2004-02-10T08:34:30.264 A 2.33e+01 1.29e+00 2004-02-10T08:34:30.264 A A 1 3.92 1.8 1.8 -45.0 2.09e+01 1.03e+00 2004-02-10T08:34:30.264 K 1.69e+01 1.06e+00 2004-02-10T08:34:30.264 A 1.70e+01 9.68e-01 2004-02-10T08:34:30.264 A B 1 2.54 1.8 1.8 -45.0 1.85e+01 1.76e+00 2004-09-19T22:12:44.497 A 1.96e+01 1.85e+00 2004-09-20T03:00:42.863 A 1.89e+01 1.78e+00 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032928.9+305842 052.3703364 4.19e-07 +30.9783008 4.19e-07 A A 03292889+3058418 -1.95 -6.40e-01 4.77e-02 3.73e+01 6 YSOc_star+dust(IR2) 2.57 1.39 11.73 0.1580 0.20 4 1.07e+01 2.37e-01 1999-11-26T20:23:25 A 1.29e+01 3.79e-01 1999-11-26T20:23:25 A 1.07e+01 2.47e-01 1999-11-26T20:23:25 A 4.36e+00 2.22e-01 2004-09-07T10:34:29.464 A 4.33e+00 2.22e-01 2004-09-07T10:34:29.464 A 4.79e+00 2.28e-01 2005-09-16T09:56:12.848 A A 1 1.77 1.5 1.5 -45.0 4.04e+00 2.04e-01 2004-09-07T10:34:29.464 A 3.95e+00 2.02e-01 2004-09-07T10:34:29.464 A 4.39e+00 2.06e-01 2005-09-16T09:56:12.848 A A 1 2.27 1.7 1.7 -45.0 4.34e+00 2.28e-01 2004-09-07T10:34:29.464 A 4.21e+00 2.21e-01 2004-09-07T10:34:29.464 A 4.48e+00 2.14e-01 2005-09-16T09:56:12.848 A A 1 2.54 1.8 1.8 -45.0 7.07e+00 3.46e-01 2004-09-07T10:34:29.464 A 7.00e+00 3.41e-01 2004-09-07T10:34:29.464 A 7.24e+00 3.38e-01 2005-09-16T09:56:12.848 A A 1 2.54 1.8 1.8 -45.0 1.40e+01 1.32e+00 2004-09-19T22:12:44.497 A 1.45e+01 1.37e+00 2004-09-20T03:00:42.863 A 1.43e+01 1.34e+00 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032929.8+312103 052.3741534 5.40e-07 +31.3507212 5.40e-07 A A 03292978+3121027 -5.00 -7.70e-01 4.89e-02 1.74e+01 6 YSOc_star+dust(IR3) 9.29 1.24 10.00 0.1270 1.47 5 1.39e+01 3.07e-01 1999-11-26T20:23:08 A 2.30e+01 5.93e-01 1999-11-26T20:23:08 A 2.28e+01 4.20e-01 1999-11-26T20:23:08 A 1.49e+01 7.42e-01 2004-02-10T08:34:30.264 A 1.60e+01 7.82e-01 2004-02-10T08:34:30.264 A 1.43e+01 7.95e-01 2004-02-10T08:34:30.264 A A 1 3.09 1.5 1.5 -45.0 1.24e+01 6.01e-01 2004-02-10T08:34:30.264 A 1.33e+01 6.45e-01 2004-02-10T08:34:30.264 A 1.23e+01 6.27e-01 2004-02-10T08:34:30.264 A A 1 2.97 1.7 1.7 -45.0 1.21e+01 5.94e-01 2004-02-10T08:34:30.264 A 1.35e+01 7.77e-01 2004-02-10T08:34:30.264 A 1.27e+01 6.54e-01 2004-02-10T08:34:30.264 A A 1 3.92 1.8 1.8 -45.0 1.78e+01 8.69e-01 2004-02-10T08:34:30.264 A 1.88e+01 9.06e-01 2004-02-10T08:34:30.264 A 1.79e+01 8.83e-01 2004-02-10T08:34:30.264 A A 1 4.98 1.8 1.8 -45.0 2.78e+01 2.60e+00 2004-09-19T22:12:44.497 A 2.79e+01 2.60e+00 2004-09-20T03:00:42.863 A 2.81e+01 2.61e+00 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032932.6+312437 052.3857265 5.57e-07 +31.4102488 5.57e-07 A A 03293255+3124370 -5.00 -1.05e+00 4.90e-02 5.08e+00 6 YSOc_star+dust(IR1) 11.60 2.37 10.34 0.3550 0.91 3 5.45e+00 1.36e-01 1999-11-26T20:22:60 A 1.09e+01 3.22e-01 1999-11-26T20:22:60 A 1.51e+01 3.47e-01 1999-11-26T20:22:60 A 2.00e+01 9.75e-01 2004-02-10T08:34:30.264 A 1.79e+01 9.03e-01 2004-02-10T08:34:30.264 A 1.80e+01 9.76e-01 2004-02-10T08:34:30.264 A A 1 3.09 1.5 1.5 -45.0 1.80e+01 8.91e-01 2004-02-10T08:34:30.264 A 1.70e+01 8.39e-01 2004-02-10T08:34:30.264 A 1.69e+01 8.47e-01 2004-02-10T08:34:30.264 A A 1 2.97 1.7 1.7 -45.0 1.46e+01 7.06e-01 2004-02-10T08:34:30.264 A 1.39e+01 6.91e-01 2004-02-10T08:34:30.264 A 1.42e+01 6.84e-01 2004-02-10T08:34:30.264 A A 1 3.92 1.8 1.8 -45.0 1.40e+01 7.08e-01 2004-02-10T08:34:30.264 A 1.35e+01 6.82e-01 2004-02-10T08:34:30.264 A 1.37e+01 6.72e-01 2004-02-10T08:34:30.264 A A 1 4.98 1.8 1.8 -45.0 1.79e+01 1.69e+00 2004-09-19T22:12:44.497 A 1.97e+01 1.86e+00 2004-09-20T03:00:42.863 A 1.83e+01 1.72e+00 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032932.9+312713 052.3870035 6.90e-07 +31.4534900 6.90e-07 A A 03293286+3127126 -1.69 -1.48e+00 5.31e-02 1.47e+00 6 YSOc_star+dust(IR2) 7.22 1.42 11.30 0.1650 0.97 4 7.27e+00 1.74e-01 1999-11-26T20:22:60 A 9.36e+00 2.67e-01 1999-11-26T20:22:60 A 8.13e+00 2.17e-01 1999-11-26T20:22:60 A 6.67e+00 3.29e-01 2004-02-10T08:34:30.264 A 5.77e+00 2.87e-01 2004-02-10T08:34:30.264 A 5.73e+00 3.30e-01 2004-02-10T08:34:30.264 A B 1 3.09 1.5 1.5 -45.0 5.02e+00 2.56e-01 2004-02-10T08:34:30.264 A 4.63e+00 2.35e-01 2004-02-10T08:34:30.264 A 4.78e+00 2.42e-01 2004-02-10T08:34:30.264 A A 1 2.97 1.7 1.7 -45.0 4.38e+00 2.28e-01 2004-02-10T08:34:30.264 A 3.84e+00 2.00e-01 2004-02-10T08:34:30.264 A 4.10e+00 2.06e-01 2004-02-10T08:34:30.264 A A 1 3.92 1.8 1.8 -45.0 4.03e+00 2.09e-01 2004-02-10T08:34:30.264 A 3.71e+00 1.96e-01 2004-02-10T08:34:30.264 A 3.87e+00 1.98e-01 2004-02-10T08:34:30.264 A A 1 4.98 1.8 1.8 -45.0 2.54e+00 3.17e-01 2004-09-19T22:12:44.497 A 2.49e+00 3.06e-01 2004-09-20T03:00:42.863 A 2.51e+00 2.75e-01 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032937.7+312202 052.4072278 6.74e-07 +31.3673538 6.74e-07 A A 03293773+3122024 -1.79 -8.10e-01 4.95e-02 2.32e+01 6 YSOc_star+dust(IR2) 8.68 1.40 11.79 0.1600 2.09 4 4.05e+00 1.08e-01 1999-11-26T20:26:44 A 4.52e+00 1.42e-01 1999-11-26T20:26:44 A 4.37e+00 1.33e-01 1999-11-26T20:26:44 A 3.35e+00 1.66e-01 2004-02-10T08:34:30.264 A 3.44e+00 1.69e-01 2004-02-10T08:34:30.264 A 3.39e+00 1.69e-01 2004-02-10T08:34:30.264 A A 1 3.09 1.5 1.5 -45.0 2.75e+00 1.43e-01 2004-02-10T08:34:30.264 A 2.90e+00 1.47e-01 2004-02-10T08:34:30.264 A 2.84e+00 1.42e-01 2004-02-10T08:34:30.264 A A 1 2.97 1.7 1.7 -45.0 2.51e+00 1.40e-01 2004-02-10T08:34:30.264 A 2.48e+00 1.32e-01 2004-02-10T08:34:30.264 A 2.49e+00 1.30e-01 2004-02-10T08:34:30.264 A A 1 3.92 1.8 1.8 -45.0 2.97e+00 1.57e-01 2004-02-10T08:34:30.264 A 2.99e+00 1.57e-01 2004-02-10T08:34:30.264 A 3.00e+00 1.52e-01 2004-02-10T08:34:30.264 A A 1 4.98 1.8 1.8 -45.0 7.82e+00 7.78e-01 2004-09-19T22:12:44.497 A 7.71e+00 7.76e-01 2004-09-20T03:00:42.863 A 7.70e+00 7.48e-01 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J032944.2+311947 052.4339927 7.54e-07 +31.3298079 7.54e-07 A A 03294415+3119478 -1.90 -6.30e-01 6.03e-02 1.13e+01 6 YSOc -999.00 -999.00 -999.00 -999.0000 -999.00 0 1.77e-01 4.73e-02 1999-11-26T20:26:44 D 3.16e-01 -9.99e+02 1999-11-26T20:26:44 U 5.10e-01 7.00e-02 1999-11-26T20:26:44 B 1.23e+00 6.13e-02 2004-02-10T08:34:30.264 A 1.30e+00 6.85e-02 2004-02-10T08:34:30.264 A 1.23e+00 6.36e-02 2004-02-10T08:34:30.264 A A 1 3.09 1.5 1.5 -45.0 1.71e+00 8.57e-02 2004-02-10T08:34:30.264 A 1.74e+00 8.89e-02 2004-02-10T08:34:30.264 A 1.67e+00 8.58e-02 2004-02-10T08:34:30.264 A A 1 2.97 1.7 1.7 -45.0 1.77e+00 8.92e-02 2004-02-10T08:34:30.264 A -9.99e+02 -9.99e+02 null U 1.80e+00 8.94e-02 2004-02-10T08:34:30.264 A Q 1 3.92 1.8 1.8 -45.0 1.81e+00 9.98e-02 2004-02-10T08:34:30.264 A 1.89e+00 1.03e-01 2004-02-10T08:34:30.264 A 1.87e+00 9.69e-02 2004-02-10T08:34:30.264 A A 1 4.98 1.8 1.8 -45.0 1.66e+00 2.65e-01 2004-09-19T22:12:44.497 B 1.49e+00 3.12e-01 2004-09-20T03:00:42.863 C 1.78e+00 2.28e-01 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J033022.4+313241 052.5935339 6.48e-07 +31.5445910 6.48e-07 A A 03302246+3132403 -1.77 4.70e-01 5.04e-02 3.10e+00 6 YSOc_star+dust(IR2) 25.55 2.50 11.78 0.2270 2.22 3 5.11e-01 -9.99e+02 1998-01-22T14:27:45 U 4.73e-01 8.80e-02 1998-01-22T14:27:45 C 7.45e-01 7.20e-02 1998-01-22T14:27:45 A 1.15e+00 5.84e-02 2004-09-07T10:34:29.464 A 1.15e+00 6.27e-02 2004-09-07T10:34:29.464 A 1.16e+00 5.80e-02 2004-09-07T10:34:29.464 A A 1 3.09 1.5 1.5 -45.0 1.58e+00 8.01e-02 2004-09-07T10:34:29.464 A 1.57e+00 7.90e-02 2004-09-07T10:34:29.464 A 1.58e+00 7.69e-02 2004-09-07T10:34:29.464 A A 1 2.97 1.7 1.7 -45.0 2.24e+00 1.28e-01 2004-09-07T10:34:29.464 A 2.18e+00 1.29e-01 2004-09-07T10:34:29.464 A 2.22e+00 1.19e-01 2004-09-07T10:34:29.464 A A 1 3.92 1.8 1.8 -45.0 4.29e+00 2.15e-01 2004-09-07T10:34:29.464 A 4.25e+00 2.13e-01 2004-09-07T10:34:29.464 A 4.27e+00 2.06e-01 2004-09-07T10:34:29.464 A A 1 4.98 1.8 1.8 -45.0 1.82e+01 1.70e+00 2004-09-19T22:12:44.497 A 1.86e+01 1.75e+00 2004-09-20T03:00:42.863 A 1.83e+01 1.70e+00 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J033024.1+311404 052.6003436 6.19e-07 +31.2345417 6.19e-07 A A 03302409+3114043 -5.00 -1.02e+00 4.94e-02 1.12e+01 6 YSOc_star+dust(IR2) 7.44 1.39 11.44 0.1580 1.44 4 6.83e+00 1.57e-01 1998-01-22T14:27:27 A 7.73e+00 1.64e-01 1998-01-22T14:27:27 A 6.93e+00 1.34e-01 1998-01-22T14:27:27 A 4.95e+00 2.46e-01 2004-09-07T10:34:29.464 A 4.98e+00 2.52e-01 2004-09-07T10:34:29.464 A 4.99e+00 2.43e-01 2004-09-07T10:34:29.464 A A 1 3.09 1.5 1.5 -45.0 4.13e+00 2.06e-01 2004-09-07T10:34:29.464 A 3.89e+00 2.29e-01 2004-09-07T10:34:29.464 A 3.96e+00 2.11e-01 2004-09-07T10:34:29.464 A A 1 2.97 1.7 1.7 -45.0 3.87e+00 2.04e-01 2004-09-07T10:34:29.464 A 3.75e+00 1.96e-01 2004-09-07T10:34:29.464 A 3.80e+00 1.91e-01 2004-09-07T10:34:29.464 A A 1 3.92 1.8 1.8 -45.0 5.21e+00 2.57e-01 2004-09-07T10:34:29.464 A 4.99e+00 2.67e-01 2004-09-07T10:34:29.464 A 5.04e+00 2.52e-01 2004-09-07T10:34:29.464 A A 1 4.98 1.8 1.8 -45.0 5.93e+00 6.28e-01 2004-09-19T22:12:44.497 A 5.23e+00 5.32e-01 2004-09-20T03:00:42.863 A 5.43e+00 5.34e-01 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J033026.0+310218 052.6082064 4.48e-07 +31.0383112 4.48e-07 A A 03302598+3102179 -5.00 -2.45e+00 4.86e-02 2.45e+00 6 YSOc_star+dust(MP1) 5.16 1.09 8.70 0.0976 0.07 7 1.03e+02 1.99e+00 1998-01-22T14:27:10 A 1.44e+02 2.12e+00 1998-01-22T14:27:10 A 1.22e+02 1.91e+00 1998-01-22T14:27:10 A 6.62e+01 3.46e+00 2004-09-07T10:34:29.464 A 6.83e+01 3.61e+00 2004-09-07T10:34:29.464 A 6.72e+01 3.24e+00 2005-09-16T09:56:12.848 A A 1 1.77 1.5 1.5 -45.0 4.42e+01 2.30e+00 2004-09-07T10:34:29.464 A 4.50e+01 2.36e+00 2004-09-07T10:34:29.464 A 4.40e+01 2.10e+00 2005-09-16T09:56:12.848 A A 1 2.27 1.7 1.7 -45.0 2.98e+01 1.47e+00 2004-09-07T10:34:29.464 A 2.97e+01 1.48e+00 2004-09-07T10:34:29.464 A 2.96e+01 1.39e+00 2005-09-16T09:56:12.848 A A 1 2.54 1.8 1.8 -45.0 1.76e+01 8.48e-01 2004-09-07T10:34:29.464 A 1.79e+01 8.66e-01 2004-09-07T10:34:29.464 A 1.78e+01 8.31e-01 2005-09-16T09:56:12.848 A A 1 2.54 1.8 1.8 -45.0 4.55e+00 4.73e-01 2004-09-19T22:12:44.497 A 4.60e+00 4.80e-01 2004-09-20T03:00:42.863 A 4.63e+00 4.55e-01 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J033120.1+304918 052.8337854 4.92e-07 +30.8215724 4.92e-07 A A 03312011+3049176 -5.00 -7.70e-01 4.81e-02 4.58e+00 6 YSOc_star+dust(IR1) 6.76 2.36 11.60 0.3550 0.60 3 5.61e+00 1.24e-01 1998-01-22T14:40:51 A 7.18e+00 1.46e-01 1998-01-22T14:40:51 A 7.73e+00 1.57e-01 1998-01-22T14:40:51 A 8.20e+00 4.14e-01 2004-09-07T10:34:29.464 A 8.25e+00 4.20e-01 2004-09-07T10:34:29.464 A 8.28e+00 4.06e-01 2004-09-07T10:34:29.464 A A 1 3.09 1.5 1.5 -45.0 7.77e+00 3.84e-01 2004-09-07T10:34:29.464 A 7.68e+00 3.97e-01 2004-09-07T10:34:29.464 A 7.74e+00 3.79e-01 2004-09-07T10:34:29.464 A A 1 2.97 1.7 1.7 -45.0 7.43e+00 3.78e-01 2004-09-07T10:34:29.464 A 7.33e+00 3.66e-01 2004-09-07T10:34:29.464 A 7.37e+00 3.60e-01 2004-09-07T10:34:29.464 A A 1 3.92 1.8 1.8 -45.0 8.68e+00 4.20e-01 2004-09-07T10:34:29.464 A 8.66e+00 4.19e-01 2004-09-07T10:34:29.464 A 8.66e+00 4.11e-01 2004-09-07T10:34:29.464 A A 1 4.98 1.8 1.8 -45.0 1.48e+01 1.40e+00 2004-09-19T22:12:44.497 A 1.49e+01 1.41e+00 2004-09-20T03:00:42.863 A 1.48e+01 1.39e+00 2004-09-20T12:36:43.680 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J033131.2+304411 052.8798953 4.07e-06 +30.7364150 4.07e-06 A A 03313120+3044111 -2.00 -1.14e+00 7.57e-02 2.01e+01 6 YSOc -999.00 -999.00 -999.00 -999.0000 -999.00 0 5.18e-01 -9.99e+02 1998-01-22T14:40:51 U 6.46e-01 -9.99e+02 1998-01-22T14:40:51 U 2.06e+00 1.38e-01 1998-01-22T14:40:51 A 1.66e+00 1.67e-01 2004-09-07T10:34:29.464 K 9.24e-01 1.47e-01 2004-09-07T10:34:29.464 B 9.16e-01 1.18e-01 2004-09-07T10:34:29.464 A B 7 1.77 1.5 1.5 -45.0 3.66e+00 3.61e-01 2004-09-07T10:34:29.464 A 4.51e+00 4.01e-01 2004-09-07T10:34:29.464 K 3.42e+00 3.02e-01 2004-09-07T10:34:29.464 A A 7 2.27 1.7 1.7 -45.0 -9.99e+02 -9.99e+02 null U 2.27e+00 2.21e-01 2004-09-07T10:34:29.464 K 1.65e+00 1.53e-01 2004-09-07T10:34:29.464 A Q 7 2.54 1.8 1.8 -45.0 1.62e+00 1.90e-01 2004-09-07T10:34:29.464 A 1.51e+00 1.82e-01 2004-09-07T10:34:29.464 A 1.56e+00 1.57e-01 2004-09-07T10:34:29.464 A A 7 4.98 1.8 1.8 -45.0 1.47e+00 3.26e-01 2004-09-19T22:12:44.497 C 1.45e+00 2.58e-01 2004-09-20T03:00:42.863 B 1.46e+00 2.25e-01 2004-09-20T12:36:43.680 B A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J033142.4+310625 052.9266820 4.54e-07 +31.1069127 4.54e-07 A A 03314240+3106249 -5.00 -1.27e+00 4.81e-02 1.24e+01 6 YSOc_star+dust(IR4) 14.55 1.12 8.84 0.1050 1.61 6 1.44e+01 2.79e-01 1998-01-22T14:41:08 A 2.99e+01 4.41e-01 1998-01-22T14:41:08 A 3.61e+01 6.32e-01 1998-01-22T14:41:08 A 3.07e+01 1.57e+00 2004-09-07T10:34:29.464 A 3.05e+01 1.66e+00 2004-09-07T10:34:29.464 A 3.05e+01 1.56e+00 2004-09-07T10:34:29.464 A A 1 3.09 1.5 1.5 -45.0 2.60e+01 1.27e+00 2004-09-07T10:34:29.464 A 2.56e+01 1.27e+00 2004-09-07T10:34:29.464 A 2.60e+01 1.25e+00 2004-09-07T10:34:29.464 A A 1 2.97 1.7 1.7 -45.0 2.04e+01 9.91e-01 2004-09-07T10:34:29.464 A 1.99e+01 9.85e-01 2004-09-07T10:34:29.464 A 2.02e+01 9.68e-01 2004-09-07T10:34:29.464 A A 1 3.92 1.8 1.8 -45.0 1.82e+01 8.71e-01 2004-09-07T10:34:29.464 A 1.83e+01 8.75e-01 2004-09-07T10:34:29.464 A 1.83e+01 8.61e-01 2004-09-07T10:34:29.464 A A 1 4.98 1.8 1.8 -45.0 2.44e+01 2.29e+00 2004-09-18T21:03:27.740 A 2.70e+01 2.51e+00 2004-09-19T02:56:44.617 A 2.52e+01 2.35e+00 2004-09-19T12:00:6.179 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J033233.0+310222 053.1374521 5.19e-07 +31.0393516 5.19e-07 A A 03323300+3102216 -5.00 -8.80e-01 4.82e-02 1.89e+00 6 YSOc_star+dust(IR1) 11.42 2.36 9.00 0.3540 0.87 3 1.96e+01 3.98e-01 1998-01-22T15:06:03 A 3.87e+01 7.13e-01 1998-01-22T15:06:03 A 5.29e+01 8.76e-01 1998-01-22T15:06:03 A 5.44e+01 2.98e+00 2004-09-07T07:03:48.646 A 5.55e+01 3.04e+00 2004-09-07T07:03:48.646 A 5.61e+01 2.88e+00 2004-09-07T07:03:48.646 A A 1 3.09 1.5 1.5 -45.0 5.97e+01 2.91e+00 2004-09-07T07:03:48.646 A 5.67e+01 2.97e+00 2004-09-07T07:03:48.646 A 5.74e+01 2.95e+00 2004-09-07T07:03:48.646 A A 1 2.97 1.7 1.7 -45.0 5.12e+01 2.53e+00 2004-09-07T07:03:48.646 A 5.06e+01 2.48e+00 2004-09-07T07:03:48.646 A 5.13e+01 2.47e+00 2004-09-07T07:03:48.646 A A 1 3.92 1.8 1.8 -45.0 5.79e+01 2.78e+00 2004-09-07T07:03:48.646 A 5.72e+01 2.79e+00 2004-09-07T07:03:48.646 A 5.79e+01 2.79e+00 2004-09-07T07:03:48.646 A A 1 4.98 1.8 1.8 -45.0 7.76e+01 7.19e+00 2004-09-18T21:03:27.740 A 7.62e+01 7.08e+00 2004-09-19T02:56:44.617 A 7.74e+01 7.16e+00 2004-09-19T12:00:6.179 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J033346.9+305350 053.4455153 6.08e-07 +30.8972623 6.08e-07 A A 03334692+3053500 -1.72 -1.04e+00 4.89e-02 1.11e+01 6 YSOc_star+dust(IR2) 7.50 1.40 11.51 0.1590 1.69 4 6.50e+00 1.38e-01 1998-01-22T15:19:52 A 7.12e+00 1.51e-01 1998-01-22T15:19:52 A 6.37e+00 1.29e-01 1998-01-22T15:19:52 A 4.54e+00 2.36e-01 2004-09-07T07:03:48.646 A 4.66e+00 2.43e-01 2004-09-07T07:03:48.646 A 4.67e+00 2.33e-01 2004-09-07T07:03:48.646 A A 1 3.09 1.5 1.5 -45.0 3.92e+00 1.97e-01 2004-09-07T07:03:48.646 A 3.98e+00 2.00e-01 2004-09-07T07:03:48.646 A 4.00e+00 1.95e-01 2004-09-07T07:03:48.646 A A 1 2.97 1.7 1.7 -45.0 3.65e+00 1.93e-01 2004-09-07T07:03:48.646 A 3.53e+00 1.87e-01 2004-09-07T07:03:48.646 A 3.63e+00 1.83e-01 2004-09-07T07:03:48.646 A A 1 3.92 1.8 1.8 -45.0 3.77e+00 1.88e-01 2004-09-07T07:03:48.646 A 3.77e+00 1.89e-01 2004-09-07T07:03:48.646 A 3.83e+00 1.85e-01 2004-09-07T07:03:48.646 A A 1 4.98 1.8 1.8 -45.0 6.14e+00 6.45e-01 2004-09-18T21:03:27.740 A 6.07e+00 5.97e-01 2004-09-19T02:56:44.617 A 6.07e+00 5.88e-01 2004-09-19T12:00:6.179 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J033351.1+311228 053.4628074 4.88e-07 +31.2077246 4.88e-07 A A 03335108+3112278 -5.00 -2.15e+00 4.94e-02 7.20e+00 6 YSOc_star+dust(MP1) 7.45 1.09 9.21 0.0981 0.30 7 4.15e+01 8.03e-01 1998-01-22T15:20:10 A 6.37e+01 1.17e+00 1998-01-22T15:20:10 A 5.87e+01 1.14e+00 1998-01-22T15:20:10 A 3.38e+01 1.78e+00 2004-09-07T07:03:48.646 A 3.48e+01 1.84e+00 2004-09-07T07:03:48.646 A 3.50e+01 1.76e+00 2004-09-07T07:03:48.646 A A 1 3.09 1.5 1.5 -45.0 2.47e+01 1.23e+00 2004-09-07T07:03:48.646 A 2.47e+01 1.27e+00 2004-09-07T07:03:48.646 A 2.50e+01 1.23e+00 2004-09-07T07:03:48.646 A A 1 2.97 1.7 1.7 -45.0 1.71e+01 8.34e-01 2004-09-07T07:03:48.646 A 1.71e+01 8.30e-01 2004-09-07T07:03:48.646 A 1.72e+01 8.20e-01 2004-09-07T07:03:48.646 A A 1 3.92 1.8 1.8 -45.0 1.04e+01 5.03e-01 2004-09-07T07:03:48.646 A 1.05e+01 5.06e-01 2004-09-07T07:03:48.646 A 1.07e+01 5.06e-01 2004-09-07T07:03:48.646 A A 1 4.98 1.8 1.8 -45.0 4.97e+00 5.10e-01 2004-09-18T21:03:27.740 A 5.58e+00 6.16e-01 2004-09-19T02:56:44.617 A 5.06e+00 5.04e-01 2004-09-19T12:00:6.179 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J033430.8+311324 053.6282643 5.26e-07 +31.2234314 5.26e-07 A A 03343079+3113243 -5.00 -1.04e+00 4.87e-02 4.32e+00 6 YSOc_star+dust(IR2) 9.44 1.40 10.32 0.1610 1.04 4 1.06e+01 2.25e-01 1998-01-22T15:30:49 A 1.64e+01 3.33e-01 1998-01-22T15:30:49 A 1.60e+01 2.65e-01 1998-01-22T15:30:49 A 1.26e+01 6.60e-01 2004-09-07T07:03:48.646 A 1.19e+01 6.73e-01 2004-09-07T07:03:48.646 A 1.23e+01 6.40e-01 2004-09-07T07:03:48.646 A A 1 3.09 1.5 1.5 -45.0 1.10e+01 5.53e-01 2004-09-07T07:03:48.646 A 1.11e+01 5.53e-01 2004-09-07T07:03:48.646 A 1.12e+01 5.44e-01 2004-09-07T07:03:48.646 A A 1 2.97 1.7 1.7 -45.0 1.10e+01 5.40e-01 2004-09-07T07:03:48.646 A 1.05e+01 5.77e-01 2004-09-07T07:03:48.646 A 1.09e+01 5.45e-01 2004-09-07T07:03:48.646 A A 1 3.92 1.8 1.8 -45.0 1.30e+01 6.25e-01 2004-09-07T07:03:48.646 A 1.31e+01 6.28e-01 2004-09-07T07:03:48.646 A 1.33e+01 6.27e-01 2004-09-07T07:03:48.646 A A 1 4.98 1.8 1.8 -45.0 1.18e+01 1.14e+00 2004-09-18T21:03:27.740 A 1.20e+01 1.16e+00 2004-09-19T02:56:44.617 A 1.16e+01 1.11e+00 2004-09-19T12:00:6.179 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J033915.8+312431 054.8158715 4.27e-07 +31.4085399 4.27e-07 A A 03391582+3124306 -5.00 -7.40e-01 4.77e-02 1.28e+01 6 YSOc_star+dust(IR2) 16.99 1.39 9.79 0.1580 1.73 4 3.08e+00 7.93e-02 1998-10-05T20:15:04 A 8.20e+00 1.74e-01 1998-10-05T20:15:04 A 1.21e+01 2.12e-01 1998-10-05T20:15:04 A 1.22e+01 6.06e-01 2004-09-07T07:03:48.646 A 1.28e+01 6.25e-01 2004-09-07T07:03:48.646 A 1.27e+01 6.11e-01 2004-09-07T07:03:48.646 A A 1 3.09 1.5 1.5 -45.0 1.15e+01 5.61e-01 2004-09-07T07:03:48.646 A 1.18e+01 5.81e-01 2004-09-07T07:03:48.646 A 1.18e+01 5.65e-01 2004-09-07T07:03:48.646 A A 1 2.97 1.7 1.7 -45.0 9.97e+00 4.92e-01 2004-09-07T07:03:48.646 A 9.92e+00 4.95e-01 2004-09-07T07:03:48.646 A 1.00e+01 4.82e-01 2004-09-07T07:03:48.646 A A 1 3.92 1.8 1.8 -45.0 1.25e+01 6.05e-01 2004-09-07T07:03:48.646 A 1.24e+01 6.06e-01 2004-09-07T07:03:48.646 A 1.26e+01 5.99e-01 2004-09-07T07:03:48.646 A A 1 4.98 1.8 1.8 -45.0 2.79e+01 2.81e+00 2004-09-18T21:03:27.740 A 2.77e+01 2.58e+00 2004-09-19T02:56:44.617 A 2.75e+01 2.56e+00 2004-09-19T12:00:6.179 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034001.5+311017 055.0062144 4.61e-07 +31.1714739 4.61e-07 A A 03400150+3110172 -5.00 -1.34e+00 4.86e-02 5.93e+00 6 YSOc_star+dust(IR4) 15.03 1.13 10.03 0.1050 1.58 6 3.96e+00 1.02e-01 1998-10-05T20:25:26 A 9.71e+00 2.59e-01 1998-10-05T20:25:26 A 1.12e+01 2.58e-01 1998-10-05T20:25:26 A 1.02e+01 5.01e-01 2004-09-07T07:03:48.646 A 9.72e+00 5.18e-01 2004-09-07T07:03:48.646 A 1.00e+01 4.85e-01 2004-09-07T07:03:48.646 A A 1 3.09 1.5 1.5 -45.0 7.96e+00 3.88e-01 2004-09-07T07:03:48.646 A 8.41e+00 4.00e-01 2004-09-07T07:03:48.646 A 8.29e+00 3.92e-01 2004-09-07T07:03:48.646 A A 1 2.97 1.7 1.7 -45.0 6.84e+00 3.39e-01 2004-09-07T07:03:48.646 A 6.63e+00 3.25e-01 2004-09-07T07:03:48.646 A 6.81e+00 3.27e-01 2004-09-07T07:03:48.646 A A 1 3.92 1.8 1.8 -45.0 6.04e+00 2.92e-01 2004-09-07T07:03:48.646 A 5.92e+00 2.89e-01 2004-09-07T07:03:48.646 A 6.13e+00 2.95e-01 2004-09-07T07:03:48.646 A A 1 4.98 1.8 1.8 -45.0 6.45e+00 6.37e-01 2004-09-18T16:14:42.183 A 6.16e+00 6.46e-01 2004-09-19T02:56:44.617 A 6.43e+00 6.21e-01 2004-09-18T19:24:35.443 A A 1 5.73 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034004.8+315415 055.0199908 2.73e-08 +31.9041316 2.73e-08 A A 03400478+3154148 -1.48 -2.38e+00 6.77e-02 1.08e+01 6 YSOc_star+dust(MP1) 5.96 1.10 10.69 0.1000 0.14 7 1.17e+01 2.37e-01 1998-10-05T20:24:43 A 2.04e+01 4.12e-01 1998-10-05T20:24:43 A 1.87e+01 3.27e-01 1998-10-05T20:24:43 A 1.00e+01 5.93e-01 2004-09-07T07:03:48.646 A 1.03e+01 5.86e-01 2004-09-07T07:03:48.646 A 1.04e+01 5.63e-01 2004-09-07T07:03:48.646 A A 1 3.09 1.5 1.5 -45.0 6.41e+00 4.07e-01 2004-09-07T07:03:48.646 A 6.60e+00 4.15e-01 2004-09-07T07:03:48.646 A 6.60e+00 3.82e-01 2004-09-07T07:03:48.646 A A 1 2.97 1.7 1.7 -45.0 4.45e+00 2.78e-01 2004-09-07T07:03:48.646 A 4.28e+00 2.63e-01 2004-09-07T07:03:48.646 A 4.41e+00 2.48e-01 2004-09-07T07:03:48.646 A A 1 3.92 1.8 1.8 -45.0 2.72e+00 1.59e-01 2004-09-07T07:03:48.646 A 2.66e+00 1.56e-01 2004-09-07T07:03:48.646 A 2.75e+00 1.46e-01 2004-09-07T07:03:48.646 A A 1 4.98 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 2.94e+00 7.56e-01 2004-09-18T19:24:35.443 C Q -2 8.04 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034011.8+315523 055.0492867 1.91e-08 +31.9231520 1.91e-08 A A 03401184+3155233 -5.00 -2.29e+00 6.56e-02 1.32e+01 6 YSOc_star+dust(MP1) 7.48 1.09 10.56 0.0980 0.11 7 9.51e+00 1.84e-01 1998-10-05T20:24:43 A 1.85e+01 3.58e-01 1998-10-05T20:24:43 A 1.80e+01 3.15e-01 1998-10-05T20:24:43 A 1.02e+01 5.20e-01 2004-09-07T07:03:48.646 A 1.05e+01 5.27e-01 2004-09-07T07:03:48.646 A 1.05e+01 5.11e-01 2004-09-07T07:03:48.646 A A 1 3.09 1.5 1.5 -45.0 6.84e+00 3.33e-01 2004-09-07T07:03:48.646 A 6.67e+00 3.25e-01 2004-09-07T07:03:48.646 A 6.81e+00 3.25e-01 2004-09-07T07:03:48.646 A A 1 2.97 1.7 1.7 -45.0 4.79e+00 2.47e-01 2004-09-07T07:03:48.646 A 4.85e+00 2.43e-01 2004-09-07T07:03:48.646 A 4.89e+00 2.39e-01 2004-09-07T07:03:48.646 A A 1 3.92 1.8 1.8 -45.0 2.93e+00 1.53e-01 2004-09-07T07:03:48.646 A 2.96e+00 1.53e-01 2004-09-07T07:03:48.646 A 3.01e+00 1.49e-01 2004-09-07T07:03:48.646 A A 1 4.98 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 5.21e+00 1.48e+00 2004-09-18T19:24:35.443 C Q -2 8.04 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034114.1+315946 055.3088093 5.02e-07 +31.9961882 5.02e-07 A A 03411412+3159462 -5.00 -2.02e+00 4.86e-02 1.19e+01 6 YSOc_star+dust(MP1) 3.09 1.09 9.07 0.0980 1.58 7 1.57e+02 2.74e+00 1998-10-05T20:47:28 A 1.37e+02 2.41e+00 1998-10-05T20:47:28 A 1.06e+02 1.86e+00 1998-10-05T20:47:28 A 5.02e+01 2.51e+00 2004-09-07T07:03:48.646 A 4.87e+01 2.59e+00 2004-09-07T07:03:48.646 A 5.00e+01 2.51e+00 2004-09-07T07:03:48.646 A A 1 3.09 1.5 1.5 -45.0 3.17e+01 1.58e+00 2004-09-07T07:03:48.646 A 3.23e+01 1.61e+00 2004-09-07T07:03:48.646 A 3.24e+01 1.57e+00 2004-09-07T07:03:48.646 A A 1 2.97 1.7 1.7 -45.0 2.35e+01 1.13e+00 2004-09-07T07:03:48.646 A 2.28e+01 1.10e+00 2004-09-07T07:03:48.646 A 2.34e+01 1.11e+00 2004-09-07T07:03:48.646 A A 1 3.92 1.8 1.8 -45.0 1.81e+01 9.11e-01 2004-09-07T07:03:48.646 A 1.80e+01 9.03e-01 2004-09-07T07:03:48.646 A 1.85e+01 9.00e-01 2004-09-07T07:03:48.646 A A 1 4.98 1.8 1.8 -45.0 9.95e+00 9.96e-01 2004-09-18T16:14:42.183 A 9.30e+00 9.14e-01 2004-09-18T22:34:28.703 A 9.64e+00 9.20e-01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034119.2+320204 055.3299417 4.86e-07 +32.0343801 4.86e-07 A A 03411921+3202037 -5.00 -1.35e+00 4.84e-02 1.08e+00 6 YSOc_star+dust(IR1) 8.11 2.36 10.42 0.3540 0.13 3 1.08e+01 2.29e-01 1998-10-05T20:47:28 A 1.80e+01 3.48e-01 1998-10-05T20:47:28 A 1.94e+01 3.39e-01 1998-10-05T20:47:28 A 1.75e+01 8.67e-01 2004-09-07T07:03:48.646 A 1.87e+01 9.15e-01 2004-09-07T07:03:48.646 A 1.83e+01 8.85e-01 2004-09-07T07:03:48.646 A A 1 3.09 1.5 1.5 -45.0 1.59e+01 7.91e-01 2004-09-07T07:03:48.646 A 1.70e+01 9.06e-01 2004-09-07T07:03:48.646 A 1.66e+01 8.30e-01 2004-09-07T07:03:48.646 A A 1 2.97 1.7 1.7 -45.0 1.34e+01 6.70e-01 2004-09-07T07:03:48.646 A 1.38e+01 6.80e-01 2004-09-07T07:03:48.646 A 1.37e+01 6.62e-01 2004-09-07T07:03:48.646 A A 1 3.92 1.8 1.8 -45.0 1.24e+01 6.00e-01 2004-09-07T07:03:48.646 A 1.29e+01 6.24e-01 2004-09-07T07:03:48.646 A 1.29e+01 6.11e-01 2004-09-07T07:03:48.646 A A 1 4.98 1.8 1.8 -45.0 9.07e+00 9.15e-01 2004-09-18T16:14:42.183 A 9.66e+00 9.49e-01 2004-09-18T22:34:28.703 A 9.45e+00 9.09e-01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034124.4+315328 055.3517693 7.87e-07 +31.8910887 7.87e-07 A A 03412443+3153278 -1.82 -8.30e-01 5.91e-02 5.15e+00 6 YSOc_star+dust(IR1) 16.96 2.79 12.54 0.4100 0.53 3 1.81e-01 4.26e-02 1998-10-05T20:52:22 D 6.41e-01 7.50e-02 1998-10-05T20:52:22 B 1.13e+00 7.60e-02 1998-10-05T20:52:22 A 1.88e+00 1.02e-01 2004-09-07T07:03:48.646 A 2.12e+00 1.10e-01 2004-09-07T07:03:48.646 A 2.03e+00 1.03e-01 2004-09-07T07:03:48.646 A A 1 3.09 1.5 1.5 -45.0 2.16e+00 1.11e-01 2004-09-07T07:03:48.646 A 2.26e+00 1.13e-01 2004-09-07T07:03:48.646 A 2.24e+00 1.10e-01 2004-09-07T07:03:48.646 A A 1 2.97 1.7 1.7 -45.0 2.07e+00 1.33e-01 2004-09-07T07:03:48.646 A 2.16e+00 1.36e-01 2004-09-07T07:03:48.646 A 2.15e+00 1.21e-01 2004-09-07T07:03:48.646 A A 1 3.92 1.8 1.8 -45.0 2.11e+00 1.47e-01 2004-09-07T07:03:48.646 A 2.09e+00 1.46e-01 2004-09-07T07:03:48.646 A 2.15e+00 1.27e-01 2004-09-07T07:03:48.646 A A 1 4.98 1.8 1.8 -45.0 2.46e+00 4.15e-01 2004-09-18T16:14:42.183 B 2.17e+00 3.41e-01 2004-09-18T22:34:28.703 B 2.31e+00 3.03e-01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034125.0+315657 055.3541516 1.55e-06 +31.9492361 1.55e-06 A A 03412500+3156571 -2.18 1.90e-01 5.17e-02 2.69e+01 6 YSOc_PAH-em -999.00 -999.00 -999.00 -999.0000 -999.00 0 6.26e-01 5.48e-02 1998-10-05T20:52:31 A 2.50e+00 1.70e-01 1998-10-05T20:52:31 A 3.97e+00 1.86e-01 1998-10-05T20:52:31 A 3.28e+00 2.51e-01 2004-09-07T07:03:48.646 A 3.39e+00 2.54e-01 2004-09-07T07:03:48.646 A 3.39e+00 2.29e-01 2004-09-07T07:03:48.646 A A 2 5.44 2.4 2.0 -55.8 4.14e+00 2.87e-01 2004-09-07T07:03:48.646 K 4.19e+00 2.81e-01 2004-09-07T07:03:48.646 K 3.73e+00 2.14e-01 2004-09-07T07:03:48.646 A A 1 2.27 1.7 1.7 -45.0 6.45e+00 3.80e-01 2004-09-07T07:03:48.646 A 6.19e+00 3.63e-01 2004-09-07T07:03:48.646 A 6.36e+00 3.47e-01 2004-09-07T07:03:48.646 A A 1 3.92 1.8 1.8 -45.0 1.38e+01 8.12e-01 2004-09-07T07:03:48.646 A 1.44e+01 8.46e-01 2004-09-07T07:03:48.646 A 1.43e+01 7.82e-01 2004-09-07T07:03:48.646 A A 1 4.98 1.8 1.8 -45.0 3.22e+01 3.00e+00 2004-09-18T16:14:42.183 A 3.21e+01 3.02e+00 2004-09-18T22:34:28.703 A 3.24e+01 3.01e+00 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034139.2+313611 055.4132246 5.04e-07 +31.6029654 5.04e-07 A A 03413918+3136106 -5.00 -7.60e-01 4.78e-02 7.65e+01 6 YSOc_star+dust(IR4) 1.65 1.13 9.10 0.1050 1.90 6 2.37e+02 4.79e+00 1998-10-05T20:52:05 A 1.68e+02 3.24e+00 1998-10-05T20:52:05 A 1.16e+02 2.03e+00 1998-10-05T20:52:05 A 4.98e+01 2.67e+00 2004-09-07T07:03:48.646 A 4.98e+01 2.59e+00 2004-09-07T07:03:48.646 A 5.05e+01 2.48e+00 2004-09-07T07:03:48.646 A A 1 3.09 1.5 1.5 -45.0 3.47e+01 1.72e+00 2004-09-07T07:03:48.646 A 3.66e+01 1.80e+00 2004-09-07T07:03:48.646 A 3.61e+01 1.74e+00 2004-09-07T07:03:48.646 A A 1 2.97 1.7 1.7 -45.0 -9.99e+02 -9.99e+02 null U 2.93e+01 1.42e+00 2004-09-07T07:03:48.646 A 2.99e+01 1.41e+00 2004-09-07T07:03:48.646 A Q 1 3.92 1.8 1.8 -45.0 5.15e+01 2.57e+00 2004-09-07T07:03:48.646 A 5.21e+01 2.58e+00 2004-09-07T07:03:48.646 A 5.26e+01 2.54e+00 2004-09-07T07:03:48.646 A A 1 4.98 1.8 1.8 -45.0 1.48e+02 1.38e+01 2004-09-18T16:14:42.183 A 1.52e+02 1.41e+01 2004-09-18T22:34:28.703 A 1.51e+02 1.40e+01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034141.1+314805 055.4212183 5.45e-07 +31.8012880 5.45e-07 A A 03414110+3148045 -2.88 -1.10e-01 5.09e-02 2.76e+01 6 YSOc -999.00 -999.00 -999.00 -999.0000 -999.00 0 6.11e-02 -9.99e+02 1998-10-05T20:52:22 U 2.54e-01 -9.99e+02 1998-10-05T20:52:22 U 7.96e-01 7.41e-02 1998-10-05T20:52:22 A 2.98e+00 1.51e-01 2004-09-07T07:03:48.646 A 3.51e+00 1.76e-01 2004-09-07T07:03:48.646 A 3.26e+00 1.61e-01 2004-09-07T07:03:48.646 A B 1 3.09 1.5 1.5 -45.0 4.58e+00 2.30e-01 2004-09-07T07:03:48.646 A 5.14e+00 2.57e-01 2004-09-07T07:03:48.646 A 4.91e+00 2.40e-01 2004-09-07T07:03:48.646 A A 1 2.97 1.7 1.7 -45.0 5.96e+00 3.06e-01 2004-09-07T07:03:48.646 A 6.44e+00 3.26e-01 2004-09-07T07:03:48.646 A 6.25e+00 3.07e-01 2004-09-07T07:03:48.646 A A 1 3.92 1.8 1.8 -45.0 7.61e+00 3.73e-01 2004-09-07T07:03:48.646 A 8.28e+00 4.06e-01 2004-09-07T07:03:48.646 A 8.09e+00 3.86e-01 2004-09-07T07:03:48.646 A A 1 4.98 1.8 1.8 -45.0 1.06e+01 1.08e+00 2004-09-18T16:14:42.183 A 1.10e+01 1.10e+00 2004-09-18T22:34:28.703 A 1.09e+01 1.05e+00 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034153.3+315019 055.4719227 6.23e-07 +31.8386741 6.23e-07 A A 03415327+3150192 -1.98 -2.50e-01 4.95e-02 1.79e+00 6 YSOc_star+dust(IR1) 16.81 2.57 12.29 0.3830 2.69 3 2.83e-01 4.15e-02 1998-10-05T20:52:22 C 7.52e-01 6.86e-02 1998-10-05T20:52:22 A 1.53e+00 8.61e-02 1998-10-05T20:52:22 A 2.79e+00 1.40e-01 2004-09-07T07:03:48.646 A 2.71e+00 1.41e-01 2004-09-07T07:03:48.646 A 2.79e+00 1.38e-01 2004-09-07T07:03:48.646 A A 1 3.09 1.5 1.5 -45.0 3.16e+00 1.58e-01 2004-09-07T07:03:48.646 A 3.12e+00 1.59e-01 2004-09-07T07:03:48.646 A 3.19e+00 1.56e-01 2004-09-07T07:03:48.646 A A 1 2.97 1.7 1.7 -45.0 3.74e+00 2.01e-01 2004-09-07T07:03:48.646 A 3.51e+00 1.87e-01 2004-09-07T07:03:48.646 A 3.66e+00 1.85e-01 2004-09-07T07:03:48.646 A A 1 3.92 1.8 1.8 -45.0 5.09e+00 2.55e-01 2004-09-07T07:03:48.646 A 5.13e+00 2.55e-01 2004-09-07T07:03:48.646 A 5.21e+00 2.50e-01 2004-09-07T07:03:48.646 A A 1 4.98 1.8 1.8 -45.0 9.87e+00 9.73e-01 2004-09-18T16:14:42.183 A 9.83e+00 9.62e-01 2004-09-18T22:34:28.703 A 1.00e+01 9.52e-01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034157.8+314801 055.4906356 4.61e-07 +31.8002320 4.61e-07 A A 03415776+3148006 -5.00 -7.00e-01 4.81e-02 3.56e+00 6 YSOc_star+dust(IR2) 16.21 1.40 8.25 0.1610 1.06 4 1.41e+01 2.47e-01 1998-10-05T21:01:44 A 3.84e+01 6.02e-01 1998-10-05T21:01:44 A 5.62e+01 8.29e-01 1998-10-05T21:01:44 A 5.34e+01 2.81e+00 2004-09-07T07:03:48.646 A 5.49e+01 3.06e+00 2004-09-07T07:03:48.646 A 5.44e+01 2.83e+00 2004-09-07T07:03:48.646 A A 1 3.09 1.5 1.5 -45.0 5.34e+01 2.64e+00 2004-09-07T07:03:48.646 A 5.49e+01 2.71e+00 2004-09-07T07:03:48.646 A 5.48e+01 2.64e+00 2004-09-07T07:03:48.646 A A 1 2.97 1.7 1.7 -45.0 5.34e+01 2.60e+00 2004-09-07T07:03:48.646 A 5.31e+01 2.57e+00 2004-09-07T07:03:48.646 A 5.39e+01 2.57e+00 2004-09-07T07:03:48.646 A A 1 3.92 1.8 1.8 -45.0 6.48e+01 3.12e+00 2004-09-07T07:03:48.646 A 6.39e+01 3.07e+00 2004-09-07T07:03:48.646 A 6.54e+01 3.10e+00 2004-09-07T07:03:48.646 A A 1 4.98 1.8 1.8 -45.0 1.12e+02 1.04e+01 2004-09-18T16:14:42.183 A 1.10e+02 1.02e+01 2004-09-18T22:34:28.703 A 1.12e+02 1.04e+01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034158.7+314821 055.4944499 5.91e-07 +31.8059524 5.91e-07 A A 03415867+3148212 -2.96 5.90e-01 5.10e-02 4.34e+00 6 YSOc_red -999.00 -999.00 -999.00 -999.0000 -999.00 0 1.23e-01 -9.99e+02 1998-10-05T21:01:35 U 6.01e-01 -9.99e+02 1998-10-05T21:01:35 U 6.23e-01 7.34e-02 1998-10-05T21:01:35 B 1.93e+00 1.01e-01 2004-09-07T07:03:48.646 A 1.93e+00 1.08e-01 2004-09-07T07:03:48.646 A 1.96e+00 1.00e-01 2004-09-07T07:03:48.646 A A 1 3.09 1.5 1.5 -45.0 3.20e+00 1.59e-01 2004-09-07T07:03:48.646 A 3.31e+00 1.66e-01 2004-09-07T07:03:48.646 A 3.29e+00 1.60e-01 2004-09-07T07:03:48.646 A A 1 2.97 1.7 1.7 -45.0 4.75e+00 2.56e-01 2004-09-07T07:03:48.646 A 4.96e+00 2.60e-01 2004-09-07T07:03:48.646 A 4.88e+00 2.48e-01 2004-09-07T07:03:48.646 A A 1 3.92 1.8 1.8 -45.0 7.45e+00 3.70e-01 2004-09-07T07:03:48.646 A 7.65e+00 3.79e-01 2004-09-07T07:03:48.646 A 7.68e+00 3.70e-01 2004-09-07T07:03:48.646 A A 1 4.98 1.8 1.8 -45.0 3.66e+01 3.40e+00 2004-09-18T16:14:42.183 A 3.55e+01 3.30e+00 2004-09-18T22:34:28.703 A 3.65e+01 3.38e+00 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034201.0+314913 055.5042185 5.85e-07 +31.8203751 5.85e-07 A A 03420102+3149132 -5.00 -1.18e+00 5.02e-02 3.32e+00 6 YSOc_star+dust(IR3) 14.64 1.23 10.75 0.1240 2.04 5 2.48e+00 6.61e-02 1998-10-05T21:01:35 A 4.88e+00 1.17e-01 1998-10-05T21:01:35 A 6.20e+00 1.43e-01 1998-10-05T21:01:35 A 5.30e+00 2.68e-01 2004-09-07T07:03:48.646 A 5.39e+00 2.71e-01 2004-09-07T07:03:48.646 A 5.43e+00 2.65e-01 2004-09-07T07:03:48.646 A A 1 3.09 1.5 1.5 -45.0 4.57e+00 2.26e-01 2004-09-07T07:03:48.646 A 4.65e+00 2.33e-01 2004-09-07T07:03:48.646 A 4.67e+00 2.26e-01 2004-09-07T07:03:48.646 A A 1 2.97 1.7 1.7 -45.0 4.18e+00 2.23e-01 2004-09-07T07:03:48.646 A 4.16e+00 2.20e-01 2004-09-07T07:03:48.646 A 4.22e+00 2.13e-01 2004-09-07T07:03:48.646 A A 1 3.92 1.8 1.8 -45.0 4.04e+00 2.09e-01 2004-09-07T07:03:48.646 A 4.10e+00 2.12e-01 2004-09-07T07:03:48.646 A 4.14e+00 2.03e-01 2004-09-07T07:03:48.646 A A 1 4.98 1.8 1.8 -45.0 4.38e+00 4.90e-01 2004-09-18T16:14:42.183 A 4.95e+00 5.53e-01 2004-09-18T22:34:28.703 A 4.61e+00 4.75e-01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034204.3+314712 055.5180676 4.48e-07 +31.7865476 4.48e-07 A A 03420434+3147114 -5.00 -7.80e-01 4.77e-02 1.76e+00 6 YSOc_star+dust(IR1) 9.62 2.36 10.65 0.3540 0.47 3 6.39e+00 1.29e-01 1998-10-05T21:01:44 A 1.13e+01 2.29e-01 1998-10-05T21:01:44 A 1.37e+01 2.28e-01 1998-10-05T21:01:44 A 1.37e+01 6.85e-01 2004-09-07T07:03:48.646 A 1.40e+01 6.96e-01 2004-09-07T07:03:48.646 A 1.40e+01 6.80e-01 2004-09-07T07:03:48.646 A A 1 3.09 1.5 1.5 -45.0 1.33e+01 6.66e-01 2004-09-07T07:03:48.646 A 1.36e+01 6.70e-01 2004-09-07T07:03:48.646 A 1.36e+01 6.58e-01 2004-09-07T07:03:48.646 A A 1 2.97 1.7 1.7 -45.0 1.36e+01 6.65e-01 2004-09-07T07:03:48.646 A 1.34e+01 6.55e-01 2004-09-07T07:03:48.646 A 1.37e+01 6.54e-01 2004-09-07T07:03:48.646 A A 1 3.92 1.8 1.8 -45.0 1.62e+01 7.78e-01 2004-09-07T07:03:48.646 A 1.64e+01 7.87e-01 2004-09-07T07:03:48.646 A 1.66e+01 7.83e-01 2004-09-07T07:03:48.646 A A 1 4.98 1.8 1.8 -45.0 2.23e+01 2.08e+00 2004-09-18T16:14:42.183 A 2.15e+01 2.01e+00 2004-09-18T22:34:28.703 A 2.22e+01 2.06e+00 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034210.7+314706 055.5445217 6.53e-07 +31.7848767 6.53e-07 A A 03421070+3147054 -1.88 -6.30e-01 5.35e-02 5.83e+00 6 YSOc_star+dust(IR3) 36.91 2.16 10.14 0.1750 1.61 4 7.48e-02 -9.99e+02 1998-10-05T21:01:44 U 3.40e-01 6.92e-02 1998-10-05T21:01:44 D 1.31e+00 7.73e-02 1998-10-05T21:01:44 A 2.23e+00 1.12e-01 2004-09-07T07:03:48.646 A 2.30e+00 1.15e-01 2004-09-07T07:03:48.646 A 2.27e+00 1.11e-01 2004-09-07T07:03:48.646 A A 1 3.09 1.5 1.5 -45.0 2.60e+00 1.30e-01 2004-09-07T07:03:48.646 A 2.61e+00 1.32e-01 2004-09-07T07:03:48.646 A 2.64e+00 1.29e-01 2004-09-07T07:03:48.646 A A 1 2.97 1.7 1.7 -45.0 -9.99e+02 -9.99e+02 null U 1.67e+00 9.30e-01 2004-09-07T07:03:48.646 E 2.96e+00 1.52e-01 2004-09-07T07:03:48.646 A Q 1 3.92 1.8 1.8 -45.0 3.25e+00 1.72e-01 2004-09-07T07:03:48.646 A 3.27e+00 1.76e-01 2004-09-07T07:03:48.646 A 3.32e+00 1.66e-01 2004-09-07T07:03:48.646 A A 1 4.98 1.8 1.8 -45.0 3.05e+00 4.47e-01 2004-09-18T16:14:42.183 B 3.50e+00 4.24e-01 2004-09-18T22:34:28.703 A 3.33e+00 3.77e-01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034220.3+320531 055.5846709 5.05e-07 +32.0919473 5.05e-07 A A 03422033+3205310 -5.00 -1.00e+00 4.79e-02 2.47e+01 6 YSOc_star+dust(IR4) 11.10 1.13 10.06 0.1050 1.26 6 9.70e+00 2.06e-01 1998-10-05T21:01:18 A 1.68e+01 3.25e-01 1998-10-05T21:01:18 A 1.72e+01 3.01e-01 1998-10-05T21:01:18 A 1.21e+01 6.11e-01 2004-09-07T07:03:48.646 A 1.21e+01 6.16e-01 2004-09-07T07:03:48.646 A 1.23e+01 6.04e-01 2004-09-07T07:03:48.646 A A 1 3.09 1.5 1.5 -45.0 9.54e+00 4.85e-01 2004-09-07T07:03:48.646 A 9.44e+00 4.79e-01 2004-09-07T07:03:48.646 A 9.59e+00 4.71e-01 2004-09-07T07:03:48.646 A A 1 2.97 1.7 1.7 -45.0 7.70e+00 3.83e-01 2004-09-07T07:03:48.646 A 7.85e+00 3.94e-01 2004-09-07T07:03:48.646 A 7.85e+00 3.80e-01 2004-09-07T07:03:48.646 A A 1 3.92 1.8 1.8 -45.0 9.84e+00 4.82e-01 2004-09-07T07:03:48.646 A 9.90e+00 4.84e-01 2004-09-07T07:03:48.646 A 1.00e+01 4.76e-01 2004-09-07T07:03:48.646 A A 1 4.98 1.8 1.8 -45.0 1.77e+01 1.67e+00 2004-09-18T16:14:42.183 A 1.82e+01 1.71e+00 2004-09-18T22:34:28.703 A 1.81e+01 1.69e+00 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034223.3+315743 055.5971917 5.20e-07 +31.9618627 5.20e-07 A A 03422333+3157426 -5.00 -1.99e+00 4.87e-02 8.58e+00 6 YSOc_star+dust(IR4) 2.72 1.13 9.10 0.1050 0.27 6 1.60e+02 3.10e+00 1998-10-05T21:01:26 A 1.39e+02 2.17e+00 1998-10-05T21:01:26 A 1.07e+02 1.78e+00 1998-10-05T21:01:26 A 5.17e+01 2.64e+00 2004-09-07T07:03:48.646 A 5.15e+01 2.62e+00 2004-09-07T07:03:48.646 A 5.22e+01 2.58e+00 2004-09-07T07:03:48.646 A A 1 3.09 1.5 1.5 -45.0 3.31e+01 1.63e+00 2004-09-07T07:03:48.646 A 3.38e+01 1.63e+00 2004-09-07T07:03:48.646 A 3.39e+01 1.62e+00 2004-09-07T07:03:48.646 A A 1 2.97 1.7 1.7 -45.0 2.46e+01 1.53e+00 2004-09-07T07:03:48.646 A 2.39e+01 1.19e+00 2004-09-07T07:03:48.646 A 2.50e+01 1.31e+00 2004-09-07T07:03:48.646 A A 1 3.92 1.8 1.8 -45.0 2.14e+01 1.12e+00 2004-09-07T07:03:48.646 A 2.13e+01 1.11e+00 2004-09-07T07:03:48.646 A 2.19e+01 1.10e+00 2004-09-07T07:03:48.646 A A 1 4.98 1.8 1.8 -45.0 9.44e+00 9.68e-01 2004-09-18T16:14:42.183 A 9.26e+00 8.94e-01 2004-09-18T22:34:28.703 A 9.45e+00 9.02e-01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034227.1+314433 055.6129978 5.23e-07 +31.7424675 5.23e-07 A A 03422713+3144329 -5.00 -9.30e-01 4.82e-02 1.12e+02 6 YSOc_star+dust(MP1) 11.57 1.10 9.89 0.0983 1.84 7 7.06e+00 1.56e-01 1999-11-05T20:22:25 A 1.84e+01 5.41e-01 1999-11-05T20:22:25 A 2.31e+01 4.90e-01 1999-11-05T20:22:25 A 1.31e+01 6.77e-01 2004-09-07T07:03:48.646 A 1.29e+01 6.90e-01 2004-09-07T07:03:48.646 A 1.32e+01 6.64e-01 2004-09-07T07:03:48.646 A A 1 3.09 1.5 1.5 -45.0 1.00e+01 4.91e-01 2004-09-07T07:03:48.646 A 9.94e+00 4.91e-01 2004-09-07T07:03:48.646 A 1.01e+01 4.86e-01 2004-09-07T07:03:48.646 A A 1 2.97 1.7 1.7 -45.0 7.83e+00 3.92e-01 2004-09-07T07:03:48.646 A 7.75e+00 3.84e-01 2004-09-07T07:03:48.646 A 7.87e+00 3.80e-01 2004-09-07T07:03:48.646 A A 1 3.92 1.8 1.8 -45.0 6.02e+00 2.99e-01 2004-09-07T07:03:48.646 A 5.89e+00 2.95e-01 2004-09-07T07:03:48.646 A 6.05e+00 2.91e-01 2004-09-07T07:03:48.646 A A 1 4.98 1.8 1.8 -45.0 4.79e+01 4.48e+00 2004-09-18T16:14:42.183 A 4.51e+01 4.18e+00 2004-09-18T22:34:28.703 A 4.72e+01 4.40e+00 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034232.1+315250 055.6337668 9.62e-07 +31.8804291 9.62e-07 A A 03423212+3152493 -1.60 -1.14e+00 7.27e-02 1.00e+00 6 YSOc_star+dust(IR2) 11.49 1.43 12.66 0.1620 1.07 4 6.79e-01 4.69e-02 1999-11-05T20:22:16 A 1.44e+00 6.90e-02 1999-11-05T20:22:16 A 1.45e+00 6.69e-02 1999-11-05T20:22:16 A 1.21e+00 6.62e-02 2004-09-07T07:03:48.646 A 1.25e+00 6.93e-02 2004-09-07T07:03:48.646 A 1.25e+00 6.47e-02 2004-09-07T07:03:48.646 A A 1 3.09 1.5 1.5 -45.0 1.12e+00 5.84e-02 2004-09-07T07:03:48.646 A 1.15e+00 5.97e-02 2004-09-07T07:03:48.646 A 1.15e+00 5.71e-02 2004-09-07T07:03:48.646 A A 1 2.97 1.7 1.7 -45.0 1.06e+00 1.02e-01 2004-09-07T07:03:48.646 A 1.02e+00 8.12e-02 2004-09-07T07:03:48.646 A 1.05e+00 7.60e-02 2004-09-07T07:03:48.646 A A 1 3.92 1.8 1.8 -45.0 1.10e+00 8.80e-02 2004-09-07T07:03:48.646 A 1.21e+00 9.36e-02 2004-09-07T07:03:48.646 A 1.18e+00 7.58e-02 2004-09-07T07:03:48.646 A A 1 4.98 1.8 1.8 -45.0 8.21e-01 3.42e-01 2004-09-18T16:14:42.183 D 1.08e+00 3.24e-01 2004-09-18T22:34:28.703 C 1.01e+00 2.48e-01 2004-09-18T19:24:35.443 C A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034232.9+314221 055.6371100 4.28e-07 +31.7057242 4.28e-07 A A 03423291+3142205 -5.00 -9.60e-01 4.77e-02 1.70e+01 6 YSOc_star+dust(IR2) 12.11 1.40 9.25 0.1590 1.32 4 1.43e+01 3.16e-01 1999-11-05T20:22:25 A 3.01e+01 8.04e-01 1999-11-05T20:22:25 A 3.16e+01 5.53e-01 1999-11-05T20:22:25 A 2.75e+01 1.39e+00 2004-09-07T07:03:48.646 A 2.83e+01 1.41e+00 2004-09-07T07:03:48.646 A 2.81e+01 1.37e+00 2004-09-07T07:03:48.646 A A 1 3.09 1.5 1.5 -45.0 2.41e+01 1.18e+00 2004-09-07T07:03:48.646 A 2.46e+01 1.20e+00 2004-09-07T07:03:48.646 A 2.46e+01 1.18e+00 2004-09-07T07:03:48.646 A A 1 2.97 1.7 1.7 -45.0 2.10e+01 1.02e+00 2004-09-07T07:03:48.646 A 2.04e+01 1.00e+00 2004-09-07T07:03:48.646 A 2.09e+01 1.00e+00 2004-09-07T07:03:48.646 A A 1 3.92 1.8 1.8 -45.0 2.08e+01 1.00e+00 2004-09-07T07:03:48.646 A 2.07e+01 9.99e-01 2004-09-07T07:03:48.646 A 2.11e+01 1.00e+00 2004-09-07T07:03:48.646 A A 1 4.98 1.8 1.8 -45.0 4.46e+01 4.14e+00 2004-09-18T16:14:42.183 A 4.52e+01 4.19e+00 2004-09-18T22:34:28.703 A 4.50e+01 4.17e+00 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034233.1+315215 055.6380345 7.18e-07 +31.8707512 7.18e-07 A A 03423313+3152145 -1.79 -8.20e-01 4.97e-02 2.73e+01 6 YSOc_star+dust(IR3) 12.34 1.24 11.60 0.1250 1.68 5 1.75e+00 6.27e-02 1999-11-05T20:22:16 A 3.31e+00 1.10e-01 1999-11-05T20:22:16 A 3.52e+00 9.06e-02 1999-11-05T20:22:16 A 2.85e+00 1.44e-01 2004-09-07T07:03:48.646 A 2.78e+00 1.43e-01 2004-09-07T07:03:48.646 A 2.86e+00 1.41e-01 2004-09-07T07:03:48.646 A A 1 3.09 1.5 1.5 -45.0 2.35e+00 1.20e-01 2004-09-07T07:03:48.646 A 2.34e+00 1.21e-01 2004-09-07T07:03:48.646 A 2.38e+00 1.17e-01 2004-09-07T07:03:48.646 A A 1 2.97 1.7 1.7 -45.0 2.02e+00 1.21e-01 2004-09-07T07:03:48.646 A 2.03e+00 1.22e-01 2004-09-07T07:03:48.646 A 2.05e+00 1.11e-01 2004-09-07T07:03:48.646 A A 1 3.92 1.8 1.8 -45.0 2.26e+00 1.29e-01 2004-09-07T07:03:48.646 A 2.28e+00 1.31e-01 2004-09-07T07:03:48.646 A 2.29e+00 1.19e-01 2004-09-07T07:03:48.646 A A 1 4.98 1.8 1.8 -45.0 7.08e+00 7.20e-01 2004-09-18T16:14:42.183 A 6.66e+00 7.09e-01 2004-09-18T22:34:28.703 A 6.89e+00 6.78e-01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034234.2+315101 055.6424427 8.34e-07 +31.8502779 8.34e-07 A A 03423420+3151008 -1.99 -9.80e-01 5.08e-02 6.50e+01 6 YSOc_star+dust(IR4) 9.03 1.14 11.86 0.1070 0.41 6 2.64e+00 7.30e-02 1999-11-05T20:22:16 A 4.25e+00 1.41e-01 1999-11-05T20:22:16 A 4.35e+00 1.12e-01 1999-11-05T20:22:16 A 2.65e+00 1.41e-01 2004-09-07T07:03:48.646 A 2.72e+00 1.42e-01 2004-09-07T07:03:48.646 A 2.73e+00 1.37e-01 2004-09-07T07:03:48.646 A A 1 3.09 1.5 1.5 -45.0 2.06e+00 1.04e-01 2004-09-07T07:03:48.646 A 2.03e+00 1.08e-01 2004-09-07T07:03:48.646 A 2.07e+00 1.03e-01 2004-09-07T07:03:48.646 A A 1 2.97 1.7 1.7 -45.0 1.47e+00 9.78e-02 2004-09-07T07:03:48.646 A 1.46e+00 9.55e-02 2004-09-07T07:03:48.646 A 1.47e+00 8.60e-02 2004-09-07T07:03:48.646 A A 1 3.92 1.8 1.8 -45.0 1.42e+00 1.04e-01 2004-09-07T07:03:48.646 A 1.39e+00 1.04e-01 2004-09-07T07:03:48.646 A 1.42e+00 8.57e-02 2004-09-07T07:03:48.646 A A 1 4.98 1.8 1.8 -45.0 6.27e+00 6.66e-01 2004-09-18T16:14:42.183 A 6.46e+00 6.62e-01 2004-09-18T22:34:28.703 A 6.50e+00 6.40e-01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034236.5+315518 055.6519435 8.02e-07 +31.9215554 8.02e-07 A A 03423648+3155174 -1.50 -9.00e-01 5.66e-02 5.23e+00 6 YSOc_star+dust(IR3) 15.61 1.26 11.78 0.1260 1.97 5 6.89e-01 4.64e-02 1999-11-05T20:22:16 A 1.72e+00 8.23e-02 1999-11-05T20:22:16 A 2.09e+00 7.69e-02 1999-11-05T20:22:16 A 1.94e+00 1.02e-01 2004-09-07T07:03:48.646 A 1.99e+00 1.04e-01 2004-09-07T07:03:48.646 A 1.99e+00 9.95e-02 2004-09-07T07:03:48.646 A A 1 3.09 1.5 1.5 -45.0 1.68e+00 8.71e-02 2004-09-07T07:03:48.646 A 1.73e+00 8.85e-02 2004-09-07T07:03:48.646 A 1.73e+00 8.51e-02 2004-09-07T07:03:48.646 A A 1 2.97 1.7 1.7 -45.0 1.62e+00 1.08e-01 2004-09-07T07:03:48.646 A 1.67e+00 1.09e-01 2004-09-07T07:03:48.646 A 1.67e+00 9.78e-02 2004-09-07T07:03:48.646 A A 1 3.92 1.8 1.8 -45.0 1.65e+00 1.14e-01 2004-09-07T07:03:48.646 A 2.41e+00 2.13e-01 2004-09-07T07:03:48.646 A 1.82e+00 1.37e-01 2004-09-07T07:03:48.646 A B 1 4.98 1.8 1.8 -45.0 3.02e+00 3.89e-01 2004-09-18T16:14:42.183 A 3.03e+00 4.33e-01 2004-09-18T22:34:28.703 B 3.04e+00 3.53e-01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034244.5+315959 055.6854175 8.26e-07 +31.9996269 8.26e-07 A A 03424451+3159585 -1.82 -1.23e+00 7.28e-02 1.23e+00 6 YSOc_star+dust(IR1) 13.59 2.51 12.69 0.3760 0.90 3 3.76e-01 4.02e-02 1999-11-05T20:22:08 B 9.11e-01 7.30e-02 1999-11-05T20:22:08 A 1.41e+00 7.81e-02 1999-11-05T20:22:08 A 1.54e+00 8.04e-02 2004-09-07T07:03:48.646 A 1.56e+00 8.28e-02 2004-09-07T07:03:48.646 A 1.57e+00 7.86e-02 2004-09-07T07:03:48.646 A A 1 3.09 1.5 1.5 -45.0 1.50e+00 7.82e-02 2004-09-07T07:03:48.646 A 1.52e+00 7.67e-02 2004-09-07T07:03:48.646 A 1.53e+00 7.51e-02 2004-09-07T07:03:48.646 A A 1 2.97 1.7 1.7 -45.0 1.36e+00 8.83e-02 2004-09-07T07:03:48.646 A 1.32e+00 8.82e-02 2004-09-07T07:03:48.646 A 1.36e+00 7.92e-02 2004-09-07T07:03:48.646 A A 1 3.92 1.8 1.8 -45.0 1.15e+00 9.31e-02 2004-09-07T07:03:48.646 A 1.25e+00 1.14e-01 2004-09-07T07:03:48.646 A 1.22e+00 7.68e-02 2004-09-07T07:03:48.646 A A 1 4.98 1.8 1.8 -45.0 1.02e+00 3.24e-01 2004-09-18T16:14:42.183 C 8.58e-01 3.03e-01 2004-09-18T22:34:28.703 D 8.89e-01 2.30e-01 2004-09-18T19:24:35.443 C A 7 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034249.2+315011 055.7049226 4.36e-07 +31.8364307 4.36e-07 A A 03424919+3150110 -5.00 -1.27e+00 4.80e-02 5.91e+00 6 YSOc_star+dust(IR4) 13.09 1.13 9.48 0.1050 1.61 6 9.46e+00 1.92e-01 1999-11-05T20:22:16 A 2.20e+01 6.29e-01 1999-11-05T20:22:16 A 2.38e+01 3.94e-01 1999-11-05T20:22:16 A 1.78e+01 8.71e-01 2004-09-07T07:03:48.646 A 1.79e+01 8.79e-01 2004-09-07T07:03:48.646 A 1.81e+01 8.69e-01 2004-09-07T07:03:48.646 A A 1 3.09 1.5 1.5 -45.0 1.47e+01 7.24e-01 2004-09-07T07:03:48.646 A 1.49e+01 7.31e-01 2004-09-07T07:03:48.646 A 1.49e+01 7.19e-01 2004-09-07T07:03:48.646 A A 1 2.97 1.7 1.7 -45.0 1.26e+01 6.20e-01 2004-09-07T07:03:48.646 A 1.26e+01 6.18e-01 2004-09-07T07:03:48.646 A 1.27e+01 6.08e-01 2004-09-07T07:03:48.646 A A 1 3.92 1.8 1.8 -45.0 1.28e+01 6.18e-01 2004-09-07T07:03:48.646 A 1.29e+01 6.23e-01 2004-09-07T07:03:48.646 A 1.30e+01 6.14e-01 2004-09-07T07:03:48.646 A A 1 4.98 1.8 1.8 -45.0 1.29e+01 1.24e+00 2004-09-18T16:14:42.183 A 1.30e+01 1.25e+00 2004-09-18T22:34:28.703 A 1.30e+01 1.23e+00 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034254.7+314345 055.7277832 4.73e-08 +31.7292380 4.73e-08 A A 03425467+3143452 -5.00 -2.25e+00 5.84e-02 5.81e+00 6 YSOc_star+dust(MP1) 5.03 1.10 9.79 0.0984 0.29 7 4.28e+01 9.46e-01 1999-11-05T20:22:25 A 5.32e+01 1.57e+00 1999-11-05T20:22:25 A 4.53e+01 9.59e-01 1999-11-05T20:22:25 A 2.35e+01 1.15e+00 2004-09-07T07:03:48.646 A 2.34e+01 1.16e+00 2004-09-07T07:03:48.646 A 2.37e+01 1.15e+00 2004-09-07T07:03:48.646 A A 1 3.09 1.5 1.5 -45.0 1.56e+01 7.79e-01 2004-09-07T07:03:48.646 A 1.59e+01 7.86e-01 2004-09-07T07:03:48.646 A 1.59e+01 7.70e-01 2004-09-07T07:03:48.646 A A 1 2.97 1.7 1.7 -45.0 1.12e+01 5.57e-01 2004-09-07T07:03:48.646 A 1.10e+01 5.49e-01 2004-09-07T07:03:48.646 A 1.12e+01 5.42e-01 2004-09-07T07:03:48.646 A A 1 3.92 1.8 1.8 -45.0 7.14e+00 3.86e-01 2004-09-07T07:03:48.646 A 7.08e+00 3.69e-01 2004-09-07T07:03:48.646 A 7.14e+00 4.02e-01 2004-09-07T07:03:48.646 A A 1 4.98 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 3.46e+00 5.04e-01 2004-09-18T19:24:35.443 B Q -2 8.04 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034256.1+315645 055.7335566 5.74e-07 +31.9457819 5.74e-07 A A 03425607+3156446 -2.85 1.50e-01 5.14e-02 6.85e+01 6 YSOc -999.00 -999.00 -999.00 -999.0000 -999.00 0 1.00e-01 -9.99e+02 1999-11-05T20:22:08 U 1.72e-01 -9.99e+02 1999-11-05T20:22:08 U 6.12e-01 6.71e-02 1999-11-05T20:22:08 B 2.68e+00 1.39e-01 2004-09-07T07:03:48.646 A 2.91e+00 1.51e-01 2004-09-07T07:03:48.646 A 2.84e+00 1.41e-01 2004-09-07T07:03:48.646 A A 1 3.09 1.5 1.5 -45.0 4.44e+00 2.24e-01 2004-09-07T07:03:48.646 A 4.74e+00 2.37e-01 2004-09-07T07:03:48.646 A 4.64e+00 2.26e-01 2004-09-07T07:03:48.646 A A 1 2.97 1.7 1.7 -45.0 7.01e+00 3.55e-01 2004-09-07T07:03:48.646 A 7.28e+00 3.74e-01 2004-09-07T07:03:48.646 A 7.21e+00 3.54e-01 2004-09-07T07:03:48.646 A A 1 3.92 1.8 1.8 -45.0 1.32e+01 6.60e-01 2004-09-07T07:03:48.646 A 1.32e+01 6.35e-01 2004-09-07T07:03:48.646 A 1.35e+01 6.46e-01 2004-09-07T07:03:48.646 A A 1 4.98 1.8 1.8 -45.0 1.10e+01 1.10e+00 2004-09-18T16:14:42.183 A 1.05e+01 1.03e+00 2004-09-18T22:34:28.703 A 1.06e+01 1.02e+00 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034301.9+314436 055.7580726 5.79e-07 +31.7432158 5.79e-07 A A 03430194+3144355 -1.70 -9.50e-01 5.53e-02 8.40e-01 6 YSOc_star+dust(IR1) 8.46 2.38 12.38 0.3570 0.42 3 1.73e+00 6.06e-02 2000-01-09T16:18:29 A 2.74e+00 1.04e-01 2000-01-09T16:18:29 A 3.14e+00 8.10e-02 2000-01-09T16:18:29 A 2.83e+00 1.47e-01 2004-09-07T07:03:48.646 A 2.91e+00 1.47e-01 2004-09-07T07:03:48.646 A 2.95e+00 1.44e-01 2004-09-07T07:03:48.646 A A 1 3.09 1.5 1.5 -45.0 2.82e+00 1.40e-01 2004-09-07T07:03:48.646 A 2.94e+00 1.42e-01 2004-09-07T07:03:48.646 A 2.93e+00 1.40e-01 2004-09-07T07:03:48.646 A A 1 2.97 1.7 1.7 -45.0 2.87e+00 1.60e-01 2004-09-07T07:03:48.646 A 2.83e+00 1.50e-01 2004-09-07T07:03:48.646 A 2.88e+00 1.46e-01 2004-09-07T07:03:48.646 A A 1 3.92 1.8 1.8 -45.0 3.19e+00 1.75e-01 2004-09-07T07:03:48.646 A 3.24e+00 1.70e-01 2004-09-07T07:03:48.646 A 3.28e+00 1.64e-01 2004-09-07T07:03:48.646 A A 1 4.98 1.8 1.8 -45.0 3.23e+00 6.39e-01 2004-09-18T16:14:42.183 B 3.05e+00 4.40e-01 2004-09-18T22:34:28.703 B 3.11e+00 4.09e-01 2004-09-18T19:24:35.443 A A 7 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034306.8+314821 055.7782195 5.97e-07 +31.8056946 5.97e-07 A A 03430679+3148204 -5.00 -2.34e+00 5.78e-02 1.35e+00 6 YSOc_star+dust(MP1) 7.77 1.10 9.82 0.0982 0.18 7 2.06e+01 4.36e-01 2000-01-09T16:18:20 A 3.38e+01 9.02e-01 2000-01-09T16:18:20 A 3.40e+01 7.21e-01 2000-01-09T16:18:20 A 1.91e+01 1.20e+00 2004-09-07T07:03:48.646 A 1.97e+01 9.89e-01 2004-09-07T07:03:48.646 A 1.96e+01 9.55e-01 2004-09-07T07:03:48.646 A A 1 3.09 1.5 1.5 -45.0 1.36e+01 6.77e-01 2004-09-07T07:03:48.646 A 1.33e+01 6.70e-01 2004-09-07T07:03:48.646 A 1.36e+01 6.62e-01 2004-09-07T07:03:48.646 A A 1 2.97 1.7 1.7 -45.0 9.45e+00 5.10e-01 2004-09-07T07:03:48.646 A 9.59e+00 4.76e-01 2004-09-07T07:03:48.646 A 9.64e+00 4.68e-01 2004-09-07T07:03:48.646 A A 1 3.92 1.8 1.8 -45.0 5.80e+00 3.10e-01 2004-09-07T07:03:48.646 A 5.92e+00 3.19e-01 2004-09-07T07:03:48.646 A 5.96e+00 3.02e-01 2004-09-07T07:03:48.646 A A 1 4.98 1.8 1.8 -45.0 1.72e+00 2.93e-01 2004-09-18T16:14:42.183 B 2.10e+00 4.47e-01 2004-09-18T22:34:28.703 C 1.80e+00 2.71e-01 2004-09-18T19:24:35.443 B A 7 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034313.7+320045 055.8070845 4.54e-07 +32.0125437 4.54e-07 A A 03431371+3200451 -5.00 -6.80e-01 4.82e-02 7.09e+00 6 YSOc_star+dust(IR2) 12.96 1.39 10.33 0.1580 0.08 4 3.14e+00 6.94e-02 2000-01-09T16:18:12 A 9.64e+00 2.75e-01 2000-01-09T16:18:12 A 1.31e+01 2.66e-01 2000-01-09T16:18:12 A 1.26e+01 6.33e-01 2004-09-07T07:03:48.646 A 1.29e+01 6.41e-01 2004-09-07T07:03:48.646 A 9.22e+00 4.38e-01 2005-09-16T09:10:33.865 A A 1 1.77 1.5 1.5 -45.0 1.11e+01 5.58e-01 2004-09-07T07:03:48.646 A 1.17e+01 5.74e-01 2004-09-07T07:03:48.646 A 1.16e+01 5.60e-01 2004-09-07T07:03:48.646 A A 1 2.97 1.7 1.7 -45.0 1.09e+01 5.37e-01 2004-09-07T07:03:48.646 A 1.11e+01 5.54e-01 2004-09-07T07:03:48.646 A 1.08e+01 5.09e-01 2005-09-16T09:10:33.865 A A 1 2.54 1.8 1.8 -45.0 1.40e+01 6.88e-01 2004-09-07T07:03:48.646 A 1.42e+01 6.95e-01 2004-09-07T07:03:48.646 A 1.43e+01 6.82e-01 2004-09-07T07:03:48.646 A A 1 4.98 1.8 1.8 -45.0 2.04e+01 1.99e+00 2004-09-18T16:14:42.183 A 2.09e+01 2.05e+00 2004-09-18T22:34:28.703 A 2.06e+01 1.97e+00 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034321.5+314246 055.8394533 5.91e-07 +31.7128642 5.91e-07 A A 03432148+3142462 -1.74 -1.22e+00 5.40e-02 1.44e+00 6 YSOc_star+dust(IR3) 15.78 1.23 10.90 0.1240 1.86 5 1.52e+00 5.31e-02 2000-01-09T16:18:29 A 3.68e+00 1.19e-01 2000-01-09T16:18:29 A 4.68e+00 1.08e-01 2000-01-09T16:18:29 A 4.32e+00 2.18e-01 2004-09-07T07:03:48.646 A 4.43e+00 2.18e-01 2004-09-07T07:03:48.646 A 4.44e+00 2.15e-01 2004-09-07T07:03:48.646 A A 1 3.09 1.5 1.5 -45.0 3.71e+00 1.86e-01 2004-09-07T07:03:48.646 A 3.77e+00 1.87e-01 2004-09-07T07:03:48.646 A 3.80e+00 1.84e-01 2004-09-07T07:03:48.646 A A 1 2.97 1.7 1.7 -45.0 3.36e+00 1.81e-01 2004-09-07T07:03:48.646 A 3.44e+00 1.87e-01 2004-09-07T07:03:48.646 A 3.43e+00 1.75e-01 2004-09-07T07:03:48.646 A A 1 3.92 1.8 1.8 -45.0 3.63e+00 1.94e-01 2004-09-07T07:03:48.646 A 3.58e+00 1.92e-01 2004-09-07T07:03:48.646 A 3.65e+00 1.83e-01 2004-09-07T07:03:48.646 A A 1 4.98 1.8 1.8 -45.0 2.96e+00 5.00e-01 2004-09-18T16:14:42.183 B 2.71e+00 3.77e-01 2004-09-18T22:34:28.703 A 2.84e+00 3.51e-01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034322.2+314614 055.8425688 4.77e-07 +31.7704524 4.77e-07 A A 03432222+3146136 -5.00 -8.90e-01 4.82e-02 2.93e+00 6 YSOc_star+dust(IR1) 11.96 2.36 9.94 0.3540 0.54 3 6.86e+00 1.58e-01 2000-01-09T16:18:20 A 1.52e+01 4.05e-01 2000-01-09T16:18:20 A 2.07e+01 4.01e-01 2000-01-09T16:18:20 A 2.18e+01 1.17e+00 2004-09-07T07:03:48.646 A 2.25e+01 1.22e+00 2004-09-07T07:03:48.646 A 2.26e+01 1.16e+00 2004-09-07T07:03:48.646 A A 1 3.09 1.5 1.5 -45.0 2.24e+01 1.12e+00 2004-09-07T07:03:48.646 A 2.31e+01 1.14e+00 2004-09-07T07:03:48.646 A 2.31e+01 1.12e+00 2004-09-07T07:03:48.646 A A 1 2.97 1.7 1.7 -45.0 2.02e+01 9.91e-01 2004-09-07T07:03:48.646 A 2.00e+01 9.78e-01 2004-09-07T07:03:48.646 A 2.03e+01 9.70e-01 2004-09-07T07:03:48.646 A A 1 3.92 1.8 1.8 -45.0 2.14e+01 1.03e+00 2004-09-07T07:03:48.646 A 2.16e+01 1.03e+00 2004-09-07T07:03:48.646 A 2.19e+01 1.03e+00 2004-09-07T07:03:48.646 A A 1 4.98 1.8 1.8 -45.0 3.22e+01 3.04e+00 2004-09-18T16:14:42.183 A 3.10e+01 2.90e+00 2004-09-18T22:34:28.703 A 3.20e+01 2.99e+00 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034323.6+321226 055.8482098 9.06e-07 +32.2071839 9.06e-07 A A 03432355+3212258 -1.57 -1.39e+00 5.55e-02 4.09e+00 6 YSOc_star+dust(IR3) 10.42 1.24 11.34 0.1270 1.98 5 3.69e+00 8.50e-02 2000-01-09T16:17:54 A 5.64e+00 1.72e-01 2000-01-09T16:17:54 A 5.42e+00 1.40e-01 2000-01-09T16:17:54 A 4.30e+00 2.12e-01 2004-02-11T00:28:52.750 A 4.14e+00 2.15e-01 2004-02-11T00:28:52.750 A 4.06e+00 2.24e-01 2004-02-11T00:28:52.750 A A 1 3.09 1.5 1.5 -45.0 3.47e+00 1.76e-01 2004-02-11T00:28:52.750 A 3.37e+00 1.82e-01 2004-02-11T00:28:52.750 A 3.40e+00 1.75e-01 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 2.80e+00 1.58e-01 2004-02-11T00:28:52.750 A 2.90e+00 1.67e-01 2004-02-11T00:28:52.750 A 2.83e+00 1.52e-01 2004-02-11T00:28:52.750 A A 1 3.92 1.8 1.8 -45.0 2.55e+00 1.47e-01 2004-02-11T00:28:52.750 A 2.66e+00 1.55e-01 2004-02-11T00:28:52.750 A 2.61e+00 1.37e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 1.90e+00 4.10e-01 2004-09-18T16:14:42.183 C 2.50e+00 3.94e-01 2004-09-18T22:34:28.703 B 2.56e+00 3.15e-01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034325.5+315517 055.8561675 5.41e-07 +31.9212583 5.41e-07 A A 03432548+3155165 -5.00 -1.06e+00 4.81e-02 5.25e+01 6 YSOc_star+dust(MP1) 21.23 1.11 9.99 0.0983 1.30 7 8.96e-01 4.70e-02 2000-01-09T16:18:12 A 3.68e+00 1.39e-01 2000-01-09T16:18:12 A 6.72e+00 1.61e-01 2000-01-09T16:18:12 A 7.21e+00 3.55e-01 2004-02-11T00:28:52.750 A 7.29e+00 3.67e-01 2004-02-11T00:28:52.750 K 7.11e+00 3.34e-01 2004-02-11T00:28:52.750 A A 1 1.77 1.5 1.5 -45.0 6.20e+00 3.09e-01 2004-02-11T00:28:52.750 A 6.17e+00 3.16e-01 2004-02-11T00:28:52.750 A 6.09e+00 2.89e-01 2004-02-11T00:28:52.750 A A 1 2.27 1.7 1.7 -45.0 5.00e+00 2.57e-01 2004-02-11T00:28:52.750 A 4.82e+00 2.76e-01 2004-02-11T00:28:52.750 A 4.71e+00 2.26e-01 2004-02-11T00:28:52.750 A A 1 2.54 1.8 1.8 -45.0 3.49e+00 1.89e-01 2004-02-11T00:28:52.750 A 3.50e+00 1.97e-01 2004-02-11T00:28:52.750 A 3.32e+00 1.62e-01 2004-02-11T00:28:52.750 A A 1 2.54 1.8 1.8 -45.0 1.35e+01 1.31e+00 2004-09-18T16:14:42.183 A 1.23e+01 1.21e+00 2004-09-18T22:34:28.703 A 1.29e+01 1.22e+00 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034328.2+320159 055.8675326 4.54e-07 +32.0330954 4.54e-07 A A 03432820+3201591 -5.00 -1.22e+00 4.78e-02 5.56e+00 6 YSOc_star+dust(IR2) 11.26 1.39 8.71 0.1580 1.07 4 3.16e+01 6.98e-01 2000-01-09T16:18:12 A 5.29e+01 1.36e+00 2000-01-09T16:18:12 A 6.00e+01 1.22e+00 2000-01-09T16:18:12 A 5.58e+01 2.77e+00 2004-02-11T00:28:52.750 A 5.69e+01 3.05e+00 2004-02-11T00:28:52.750 A 4.81e+01 2.33e+00 2004-02-11T00:28:52.750 A A 1 1.77 1.5 1.5 -45.0 5.02e+01 2.40e+00 2004-02-11T00:28:52.750 A 5.20e+01 2.69e+00 2004-02-11T00:28:52.750 A 5.21e+01 2.52e+00 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 4.00e+01 1.92e+00 2004-02-11T00:28:52.750 A 4.34e+01 2.10e+00 2004-02-11T00:28:52.750 A 3.84e+01 1.80e+00 2004-02-11T00:28:52.750 A A 1 2.54 1.8 1.8 -45.0 3.60e+01 1.70e+00 2004-02-11T00:28:52.750 A 3.86e+01 1.90e+00 2004-02-11T00:28:52.750 A 3.72e+01 1.78e+00 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 3.91e+01 3.65e+00 2004-09-18T16:14:42.183 A 4.04e+01 3.75e+00 2004-09-18T22:34:28.703 A 4.02e+01 3.73e+00 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034328.5+320506 055.8685829 8.03e-07 +32.0849472 8.03e-07 A A 03432845+3205058 -5.00 -7.70e-01 5.02e-02 4.52e+00 6 YSOc_star+dust(IR2) 17.66 1.42 10.84 0.1620 1.28 4 9.57e-01 4.94e-02 2000-01-09T16:18:03 A 2.68e+00 1.11e-01 2000-01-09T16:18:03 A 4.65e+00 1.24e-01 2000-01-09T16:18:03 A 4.27e+00 2.10e-01 2004-02-11T00:28:52.750 A 4.92e+00 2.61e-01 2004-02-11T00:28:52.750 A 4.51e+00 2.39e-01 2004-02-11T00:28:52.750 A A 1 3.09 1.5 1.5 -45.0 4.18e+00 2.13e-01 2004-02-11T00:28:52.750 A 5.16e+00 2.59e-01 2004-02-11T00:28:52.750 A 4.58e+00 2.36e-01 2004-02-11T00:28:52.750 A B 1 2.97 1.7 1.7 -45.0 3.55e+00 1.98e-01 2004-02-11T00:28:52.750 A 4.88e+00 2.49e-01 2004-02-11T00:28:52.750 A 4.15e+00 2.21e-01 2004-02-11T00:28:52.750 A C 1 3.92 1.8 1.8 -45.0 4.25e+00 2.33e-01 2004-02-11T00:28:52.750 A 5.76e+00 3.05e-01 2004-02-11T00:28:52.750 A 4.85e+00 2.52e-01 2004-02-11T00:28:52.750 A B 1 4.98 1.8 1.8 -45.0 8.75e+00 8.98e-01 2004-09-18T16:14:42.183 A 8.37e+00 8.69e-01 2004-09-18T22:34:28.703 A 8.58e+00 8.41e-01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034336.0+315009 055.9000659 4.83e-07 +31.8358311 4.83e-07 A A 03433602+3150089 -2.58 1.40e-01 4.79e-02 5.64e+00 6 YSOc_star+dust(IR1) 15.12 2.42 12.03 0.3630 0.99 3 4.76e-01 3.77e-02 1999-12-27T17:16:22 A 1.34e+00 6.55e-02 1999-12-27T17:16:22 A 2.21e+00 7.52e-02 1999-12-27T17:16:22 A 2.70e+00 1.29e-01 2004-02-11T00:28:52.750 A 2.32e+00 1.39e-01 2004-02-11T00:28:52.750 A 2.63e+00 1.26e-01 2004-02-11T00:28:52.750 A B 1 1.77 1.5 1.5 -45.0 3.23e+00 1.60e-01 2004-09-07T07:03:48.646 A 3.12e+00 1.67e-01 2004-09-07T07:03:48.646 A 3.32e+00 1.58e-01 2005-09-16T09:10:33.865 A A 1 2.27 1.7 1.7 -45.0 4.23e+00 2.05e-01 2004-02-11T00:28:52.750 A 3.58e+00 2.14e-01 2004-02-11T00:28:52.750 A 4.12e+00 1.99e-01 2004-02-11T00:28:52.750 A B 1 2.54 1.8 1.8 -45.0 7.16e+00 3.54e-01 2004-09-07T07:03:48.646 A 7.14e+00 3.57e-01 2004-09-07T07:03:48.646 A 7.20e+00 3.38e-01 2005-09-16T09:10:33.865 A A 1 2.54 1.8 1.8 -45.0 2.63e+01 2.45e+00 2004-09-18T16:14:42.183 A 2.50e+01 2.34e+00 2004-09-18T22:34:28.703 A 2.59e+01 2.41e+00 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034348.8+321552 055.9534537 5.88e-07 +32.2643064 5.88e-07 A A 03434881+3215515 -4.71 -8.40e-01 4.90e-02 7.25e+00 6 YSOc_star+dust(IR2) 7.76 1.42 10.75 0.1650 0.66 4 1.00e+01 2.21e-01 1999-12-27T17:16:57 A 1.43e+01 3.69e-01 1999-12-27T17:16:57 A 1.30e+01 3.11e-01 1999-12-27T17:16:57 A 9.71e+00 4.73e-01 2004-02-11T00:28:52.750 A 9.42e+00 4.83e-01 2004-02-11T00:28:52.750 A 9.07e+00 5.15e-01 2004-02-11T00:28:52.750 A A 1 3.09 1.5 1.5 -45.0 8.58e+00 4.27e-01 2004-02-11T00:28:52.750 A 8.95e+00 4.41e-01 2004-02-11T00:28:52.750 A 8.80e+00 4.32e-01 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 9.00e+00 4.53e-01 2004-02-11T00:28:52.750 A 9.12e+00 4.54e-01 2004-02-11T00:28:52.750 A 9.09e+00 4.43e-01 2004-02-11T00:28:52.750 A A 1 3.92 1.8 1.8 -45.0 1.17e+01 5.68e-01 2004-02-11T00:28:52.750 A 1.17e+01 5.92e-01 2004-02-11T00:28:52.750 A 1.18e+01 5.63e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 1.34e+01 1.27e+00 2004-09-18T16:14:42.183 A 1.39e+01 1.33e+00 2004-09-18T22:34:28.703 A 1.37e+01 1.28e+00 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034351.0+320325 055.9623313 1.97e-06 +32.0568640 1.97e-06 A A null -2.16 1.51e+00 5.99e-02 1.50e+01 5 YSOc_red -999.00 -999.00 -999.00 -999.0000 -999.00 0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 3.44e-01 2.28e-02 2004-02-11T00:28:52.750 A Q 7 1.77 1.5 1.5 -45.0 1.10e+00 1.41e-01 2004-02-11T00:28:52.750 A 1.01e+00 7.89e-02 2004-02-11T00:28:52.750 A 1.14e+00 8.96e-02 2004-02-11T00:28:52.750 A A 7 2.97 1.7 1.7 -45.0 1.78e+00 1.58e-01 2004-02-11T00:28:52.750 A 1.78e+00 1.55e-01 2004-02-11T00:28:52.750 A 1.52e+00 1.41e-01 2004-02-11T00:28:52.750 A A 2 8.57 3.6 2.1 -62.0 2.60e+00 1.79e-01 2004-02-11T00:28:52.750 A 2.51e+00 1.50e-01 2004-02-11T00:28:52.750 A 2.58e+00 1.49e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 5.46e+01 5.06e+00 2004-09-18T16:14:42.183 A 5.42e+01 5.02e+00 2004-09-18T22:34:28.703 A 5.41e+01 5.03e+00 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034355.0+320103 055.9791799 2.63e-06 +32.0175333 2.63e-06 A A 03435500+3201032 -5.00 -4.20e-01 6.38e-02 6.37e+01 6 YSOc_star+dust(IR2) 15.70 2.27 12.74 0.2090 0.96 3 1.35e-01 4.21e-02 1999-12-27T17:16:39 D 6.33e-01 -9.99e+02 1999-12-27T17:16:39 U 1.14e+00 8.58e-02 1999-12-27T17:16:39 A 7.76e-01 8.14e-02 2004-02-11T00:28:52.750 A 1.07e+00 1.12e-01 2004-02-11T00:28:52.750 K 8.23e-01 5.01e-02 2004-02-11T00:28:52.750 K B 7 1.77 1.5 1.5 -45.0 3.56e+00 2.01e-01 2004-02-11T00:28:52.750 A 3.64e+00 3.09e-01 2004-02-11T00:28:52.750 A 3.56e+00 2.62e-01 2004-02-11T00:28:52.750 A A 2 7.23 2.9 2.2 -64.5 1.68e+00 1.54e-01 2004-02-11T00:28:52.750 A 1.40e+00 1.45e-01 2004-02-11T00:28:52.750 A 1.50e+00 9.56e-02 2004-02-11T00:28:52.750 A A 2 2.76 2.3 1.5 -56.5 1.41e+00 1.38e-01 2004-02-11T00:28:52.750 K 9.67e-01 1.21e-01 2004-02-11T00:28:52.750 A 9.64e-01 9.39e-02 2004-02-11T00:28:52.750 A B 7 2.54 1.8 1.8 -45.0 4.99e+00 7.78e-01 2004-09-18T16:14:42.183 B 5.45e+00 7.33e-01 2004-09-18T22:34:28.703 A 5.13e+00 6.40e-01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034355.2+315532 055.9801751 4.11e-07 +31.9255863 4.11e-07 A A 03435524+3155320 -5.00 -7.80e-01 4.75e-02 5.45e+01 6 YSOc_star+dust(IR1) 15.75 2.36 9.61 0.3540 0.11 3 3.27e+00 7.84e-02 1999-12-27T17:16:31 A 1.21e+01 3.13e-01 1999-12-27T17:16:31 A 1.85e+01 3.57e-01 1999-12-27T17:16:31 A 2.47e+01 1.21e+00 2004-02-11T00:28:52.750 A 2.37e+01 1.20e+00 2004-02-11T00:28:52.750 A 2.70e+01 1.29e+00 2004-02-11T00:28:52.750 A A 1 1.77 1.5 1.5 -45.0 -9.99e+02 -9.99e+02 null N 2.14e+01 1.06e+00 2004-02-11T00:28:52.750 A 2.46e+01 1.16e+00 2004-02-11T00:28:52.750 A Q 1 2.27 1.7 1.7 -45.0 1.95e+01 9.26e-01 2004-02-11T00:28:52.750 A 1.74e+01 8.35e-01 2004-02-11T00:28:52.750 A 2.04e+01 9.58e-01 2004-02-11T00:28:52.750 A A 1 2.54 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null N 1.29e+01 6.53e-01 2004-02-11T00:28:52.750 A 1.51e+01 7.08e-01 2004-02-11T00:28:52.750 A Q 1 2.54 1.8 1.8 -45.0 7.69e+01 7.12e+00 2004-09-18T16:14:42.183 A 7.89e+01 7.30e+00 2004-09-18T22:34:28.703 A 7.71e+01 7.14e+00 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034355.3+320753 055.9803258 7.64e-07 +32.1314700 7.64e-07 A A 03435526+3207533 -5.00 -9.50e-01 5.14e-02 3.41e+00 6 YSOc_star+dust(IR2) 8.48 1.42 11.47 0.1650 1.96 4 5.37e+00 1.33e-01 1999-12-27T17:16:48 A 6.40e+00 1.83e-01 1999-12-27T17:16:48 A 5.89e+00 1.52e-01 1999-12-27T17:16:48 A 4.68e+00 2.45e-01 2004-02-11T00:28:52.750 A 5.09e+00 2.57e-01 2004-02-11T00:28:52.750 A 4.62e+00 2.63e-01 2004-02-11T00:28:52.750 A A 1 3.09 1.5 1.5 -45.0 4.43e+00 2.19e-01 2004-02-11T00:28:52.750 A 4.55e+00 2.27e-01 2004-02-11T00:28:52.750 A 4.50e+00 2.20e-01 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 4.25e+00 2.27e-01 2004-02-11T00:28:52.750 A 4.26e+00 2.28e-01 2004-02-11T00:28:52.750 A 4.28e+00 2.17e-01 2004-02-11T00:28:52.750 A A 1 3.92 1.8 1.8 -45.0 5.22e+00 2.76e-01 2004-02-11T00:28:52.750 A 5.12e+00 2.80e-01 2004-02-11T00:28:52.750 A 5.17e+00 2.63e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 5.70e+00 6.19e-01 2004-09-18T16:14:42.183 A 5.22e+00 6.34e-01 2004-09-18T22:34:28.703 A 5.65e+00 5.79e-01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034355.5+320415 055.9813233 2.25e-08 +32.0708705 2.25e-08 A A 03435551+3204151 -2.06 -2.90e-01 9.52e-02 3.49e+01 6 YSOc -999.00 -999.00 -999.00 -999.0000 -999.00 0 2.13e-01 -9.99e+02 1999-12-27T17:16:39 U 2.76e-01 -9.99e+02 1999-12-27T17:16:39 U 4.23e-01 6.74e-02 1999-12-27T17:16:39 C 4.73e-01 5.47e-02 2004-02-11T00:28:52.750 A 4.39e-01 5.34e-02 2004-02-11T00:28:52.750 A 5.20e-01 3.11e-02 2004-02-11T00:28:52.750 K A 7 1.77 1.5 1.5 -45.0 2.31e+00 1.99e-01 2004-02-11T00:28:52.750 A 2.31e+00 2.32e-01 2004-02-11T00:28:52.750 A 1.99e+00 1.74e-01 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 1.15e+00 1.16e-01 2004-02-11T00:28:52.750 A 1.19e+00 1.16e-01 2004-02-11T00:28:52.750 A 1.15e+00 7.04e-02 2004-02-11T00:28:52.750 A A 1 2.54 1.8 1.8 -45.0 8.84e-01 9.89e-02 2004-02-11T00:28:52.750 A 9.61e-01 1.08e-01 2004-02-11T00:28:52.750 A 9.13e-01 8.26e-02 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 1.79e+00 5.13e-01 2004-09-18T19:24:35.443 C Q -2 8.04 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034356.0+320213 055.9834629 5.25e-07 +32.0370212 5.25e-07 A A 03435602+3202132 -5.00 -8.30e-01 4.82e-02 1.00e+00 6 YSOc_star+dust(IR2) 18.54 1.39 7.33 0.1580 1.33 4 1.93e+01 4.26e-01 1999-12-27T17:16:39 A 6.12e+01 1.63e+00 1999-12-27T17:16:39 A 1.03e+02 2.37e+00 1999-12-27T17:16:39 A 1.02e+02 5.47e+00 2004-02-11T00:28:52.750 A 1.11e+02 6.25e+00 2004-02-11T00:28:52.750 A 1.09e+02 5.24e+00 2004-02-11T00:28:52.750 A A 2 2.41 2.6 1.2 -66.4 1.13e+02 5.82e+00 2004-02-11T00:28:52.750 A 1.06e+02 6.77e+00 2004-02-11T00:28:52.750 A 1.15e+02 5.94e+00 2004-02-11T00:28:52.750 A A 2 6.52 2.5 2.3 -88.1 1.07e+02 5.15e+00 2004-02-11T00:28:52.750 A 1.11e+02 5.55e+00 2004-02-11T00:28:52.750 A 1.08e+02 5.05e+00 2004-02-11T00:28:52.750 A A 1 2.54 1.8 1.8 -45.0 1.19e+02 5.73e+00 2004-02-11T00:28:52.750 A 1.18e+02 6.21e+00 2004-02-11T00:28:52.750 A 1.19e+02 6.08e+00 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 1.68e+02 1.55e+01 2004-09-18T16:14:42.183 A 1.59e+02 1.48e+01 2004-09-18T22:34:28.703 A 1.63e+02 1.51e+01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034357.2+320134 055.9884413 6.61e-07 +32.0260327 6.61e-07 A A 03435721+3201337 -1.95 -9.80e-01 5.10e-02 2.21e+00 6 YSOc_star+dust(IR2) 18.07 1.42 10.86 0.1590 1.91 4 9.19e-01 5.25e-02 1999-12-27T17:16:39 A 2.48e+00 1.28e-01 1999-12-27T17:16:39 A 4.09e+00 1.58e-01 1999-12-27T17:16:39 A 4.49e+00 2.49e-01 2004-02-11T00:28:52.750 A 4.57e+00 2.42e-01 2004-02-11T00:28:52.750 A 4.38e+00 2.07e-01 2004-02-11T00:28:52.750 A A 1 1.77 1.5 1.5 -45.0 4.51e+00 2.26e-01 2004-02-11T00:28:52.750 A 4.40e+00 2.39e-01 2004-02-11T00:28:52.750 A 4.59e+00 2.28e-01 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 4.23e+00 2.23e-01 2004-02-11T00:28:52.750 A 4.16e+00 2.17e-01 2004-02-11T00:28:52.750 A 3.95e+00 1.87e-01 2004-02-11T00:28:52.750 A A 1 2.54 1.8 1.8 -45.0 4.08e+00 2.24e-01 2004-02-11T00:28:52.750 A 3.92e+00 2.24e-01 2004-02-11T00:28:52.750 A 4.03e+00 2.08e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 5.26e+00 6.62e-01 2004-09-18T16:14:42.183 A 5.13e+00 5.59e-01 2004-09-18T22:34:28.703 A 5.19e+00 5.42e-01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034358.6+321728 055.9940592 5.91e-07 +32.2909746 5.91e-07 A A 03435856+3217275 -5.00 -8.70e-01 4.91e-02 1.24e+01 6 YSOc_star+dust(IR2) 9.35 1.43 9.46 0.1660 0.91 4 2.41e+01 5.11e-01 1999-12-27T17:16:57 A 3.64e+01 9.72e-01 1999-12-27T17:16:57 A 3.64e+01 8.37e-01 1999-12-27T17:16:57 A 3.29e+01 1.64e+00 2004-02-11T00:28:52.750 A 2.93e+01 1.47e+00 2004-02-11T00:28:52.750 A 2.74e+01 1.60e+00 2004-02-11T00:28:52.750 A A 1 3.09 1.5 1.5 -45.0 2.93e+01 1.44e+00 2004-02-11T00:28:52.750 A 2.42e+01 1.25e+00 2004-02-11T00:28:52.750 A 2.58e+01 1.31e+00 2004-02-11T00:28:52.750 A B 1 2.97 1.7 1.7 -45.0 2.32e+01 1.13e+00 2004-02-11T00:28:52.750 A 2.12e+01 1.03e+00 2004-02-11T00:28:52.750 A 2.22e+01 1.07e+00 2004-02-11T00:28:52.750 A A 1 3.92 1.8 1.8 -45.0 2.70e+01 1.30e+00 2004-02-11T00:28:52.750 A 2.69e+01 1.36e+00 2004-02-11T00:28:52.750 A 2.69e+01 1.31e+00 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 4.92e+01 4.56e+00 2004-09-18T16:14:42.183 A 4.54e+01 4.21e+00 2004-09-18T22:34:28.703 A 4.79e+01 4.43e+00 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034358.9+321127 055.9954931 5.60e-07 +32.1908754 5.60e-07 A A 03435890+3211270 -5.00 -5.10e-01 4.92e-02 2.64e+01 6 YSOc_star+dust(IR2) 11.02 1.42 9.10 0.1650 0.63 4 2.21e+01 4.68e-01 1999-12-27T17:16:48 A 3.83e+01 9.51e-01 1999-12-27T17:16:48 A 4.53e+01 1.00e+00 1999-12-27T17:16:48 A 3.85e+01 1.89e+00 2004-02-11T00:28:52.750 A 3.69e+01 1.86e+00 2004-02-11T00:28:52.750 A 3.39e+01 1.95e+00 2004-02-11T00:28:52.750 A A 1 3.09 1.5 1.5 -45.0 3.85e+01 1.88e+00 2004-02-11T00:28:52.750 A 3.33e+01 1.71e+00 2004-02-11T00:28:52.750 A 3.52e+01 1.77e+00 2004-02-11T00:28:52.750 A B 1 2.97 1.7 1.7 -45.0 3.53e+01 1.69e+00 2004-02-11T00:28:52.750 A 2.87e+01 1.38e+00 2004-02-11T00:28:52.750 A 3.12e+01 1.51e+00 2004-02-11T00:28:52.750 A B 1 3.92 1.8 1.8 -45.0 4.80e+01 2.34e+00 2004-02-11T00:28:52.750 A 3.47e+01 1.76e+00 2004-02-11T00:28:52.750 A 3.95e+01 1.98e+00 2004-02-11T00:28:52.750 A C 1 4.98 1.8 1.8 -45.0 1.47e+02 1.36e+01 2004-09-18T16:14:42.183 A 1.48e+02 1.37e+01 2004-09-18T22:34:28.703 A 1.47e+02 1.36e+01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034359.1+321421 055.9962352 6.20e-07 +32.2392349 6.20e-07 A A 03435907+3214213 -5.00 -8.90e-01 4.89e-02 9.56e+00 6 YSOc_star+dust(IR2) 12.79 1.41 9.97 0.1630 0.83 4 6.42e+00 1.48e-01 1999-12-27T17:16:48 A 1.25e+01 3.33e-01 1999-12-27T17:16:48 A 1.86e+01 4.45e-01 1999-12-27T17:16:48 A 1.33e+01 6.66e-01 2004-02-11T00:28:52.750 A 1.47e+01 7.54e-01 2004-02-11T00:28:52.750 A 1.33e+01 7.20e-01 2004-02-11T00:28:52.750 A A 1 3.09 1.5 1.5 -45.0 1.40e+01 6.87e-01 2004-02-11T00:28:52.750 A 1.50e+01 7.67e-01 2004-02-11T00:28:52.750 A 1.46e+01 7.19e-01 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 1.20e+01 5.98e-01 2004-02-11T00:28:52.750 A 1.20e+01 6.00e-01 2004-02-11T00:28:52.750 A 1.20e+01 5.90e-01 2004-02-11T00:28:52.750 A A 1 3.92 1.8 1.8 -45.0 1.41e+01 6.91e-01 2004-02-11T00:28:52.750 A 1.34e+01 6.71e-01 2004-02-11T00:28:52.750 A 1.37e+01 6.59e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 2.29e+01 2.21e+00 2004-09-18T16:14:42.183 A 2.32e+01 2.21e+00 2004-09-18T22:34:28.703 A 2.33e+01 2.19e+00 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034359.9+320441 055.9995036 7.16e-07 +32.0781688 7.16e-07 A A 03435987+3204414 -1.68 -9.00e-01 5.09e-02 2.02e+01 6 YSOc_star+dust(IR2) 9.19 1.40 11.73 0.1580 1.44 4 3.41e+00 9.75e-02 1999-12-27T17:16:39 A 4.52e+00 1.33e-01 1999-12-27T17:16:39 A 4.46e+00 1.23e-01 1999-12-27T17:16:39 A 3.12e+00 1.70e-01 2004-02-11T00:28:52.750 A 3.45e+00 1.74e-01 2004-02-11T00:28:52.750 A 3.43e+00 1.65e-01 2004-02-11T00:28:52.750 A A 1 1.77 1.5 1.5 -45.0 2.65e+00 1.32e-01 2004-02-11T00:28:52.750 A 2.88e+00 1.44e-01 2004-02-11T00:28:52.750 A 2.79e+00 1.38e-01 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 2.23e+00 1.33e-01 2004-02-11T00:28:52.750 A 2.48e+00 1.41e-01 2004-02-11T00:28:52.750 A 2.52e+00 1.25e-01 2004-02-11T00:28:52.750 A A 1 2.54 1.8 1.8 -45.0 2.86e+00 1.80e-01 2004-02-11T00:28:52.750 A 2.90e+00 1.80e-01 2004-02-11T00:28:52.750 A 2.90e+00 1.60e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 6.79e+00 7.62e-01 2004-09-18T16:14:42.183 A 6.34e+00 7.05e-01 2004-09-18T22:34:28.703 A 6.60e+00 6.77e-01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034400.5+320433 056.0020071 9.19e-07 +32.0757488 9.19e-07 A A 03440047+3204327 -1.93 -3.60e-01 5.27e-02 3.90e+00 6 YSOc_red -999.00 -999.00 -999.00 -999.0000 -999.00 0 7.35e-02 -9.99e+02 1999-12-27T17:16:39 U 1.40e-01 -9.99e+02 1999-12-27T17:16:39 U 1.02e+00 6.20e-02 1999-12-27T17:16:39 A 1.59e+00 8.76e-02 2004-02-11T00:28:52.750 A 1.59e+00 9.07e-02 2004-02-11T00:28:52.750 A 1.58e+00 7.81e-02 2004-02-11T00:28:52.750 A A 1 1.77 1.5 1.5 -45.0 1.76e+00 8.95e-02 2004-02-11T00:28:52.750 A 1.76e+00 9.37e-02 2004-02-11T00:28:52.750 A 1.77e+00 8.97e-02 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 1.96e+00 1.15e-01 2004-02-11T00:28:52.750 A 1.93e+00 1.18e-01 2004-02-11T00:28:52.750 A 1.92e+00 9.79e-02 2004-02-11T00:28:52.750 A A 1 2.54 1.8 1.8 -45.0 2.01e+00 1.41e-01 2004-02-11T00:28:52.750 A 2.07e+00 1.39e-01 2004-02-11T00:28:52.750 A 2.06e+00 1.20e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 5.69e+00 7.93e-01 2004-09-18T16:14:42.183 A 6.28e+00 7.67e-01 2004-09-18T22:34:28.703 A 6.29e+00 6.50e-01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034401.6+322359 056.0065606 1.03e-06 +32.3996846 1.03e-06 A A 03440157+3223588 -5.00 -1.00e+00 5.04e-02 1.22e+01 6 YSOc_star+dust(IR2) 7.72 1.43 11.32 0.1660 0.60 4 5.93e+00 1.42e-01 1999-12-27T17:17:05 A 8.56e+00 2.36e-01 1999-12-27T17:17:05 A 7.80e+00 1.87e-01 1999-12-27T17:17:05 A -9.99e+02 -9.99e+02 null N 5.28e+00 3.07e-01 2004-02-11T00:28:52.750 A 5.40e+00 3.14e-01 2004-02-11T00:28:52.750 A Q 1 3.09 1.5 1.5 -45.0 4.89e+00 2.40e-01 2004-02-11T00:28:52.750 A 5.28e+00 5.06e-01 2004-02-11T00:28:52.750 A 4.99e+00 2.44e-01 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 -9.99e+02 -9.99e+02 null N 4.08e+00 2.28e-01 2004-02-11T00:28:52.750 A 4.08e+00 2.28e-01 2004-02-11T00:28:52.750 A Q 1 3.92 1.8 1.8 -45.0 4.73e+00 2.40e-01 2004-02-11T00:28:52.750 A 5.47e+00 5.80e-01 2004-02-11T00:28:52.750 A 4.80e+00 2.43e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 7.76e+00 7.81e-01 2004-09-18T16:14:42.183 A 7.49e+00 7.56e-01 2004-09-18T22:34:28.703 A 7.59e+00 7.39e-01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034402.9+315228 056.0121489 6.12e-07 +31.8743650 6.12e-07 A A 03440291+3152277 -1.69 -1.06e+00 5.05e-02 1.72e+00 6 YSOc_star+dust(IR2) 12.76 1.39 11.31 0.1580 1.50 4 2.08e+00 4.98e-02 1999-12-27T17:16:31 A 3.83e+00 1.13e-01 1999-12-27T17:16:31 A 4.55e+00 1.05e-01 1999-12-27T17:16:31 A 4.15e+00 2.01e-01 2004-02-11T00:28:52.750 A 3.99e+00 2.18e-01 2004-02-11T00:28:52.750 A 4.02e+00 1.91e-01 2004-02-11T00:28:52.750 A A 1 1.77 1.5 1.5 -45.0 -9.99e+02 -9.99e+02 null N 3.54e+00 2.31e-01 2004-02-11T00:28:52.750 K 3.60e+00 1.75e-01 2004-02-11T00:28:52.750 A Q 1 2.27 1.7 1.7 -45.0 3.34e+00 1.67e-01 2004-02-11T00:28:52.750 A 3.14e+00 1.79e-01 2004-09-07T07:03:48.646 A 3.48e+00 1.67e-01 2004-02-11T00:28:52.750 A A 1 2.54 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null N 3.23e+00 1.73e-01 2004-02-11T00:28:52.750 A 3.74e+00 1.80e-01 2004-02-11T00:28:52.750 A Q 1 2.54 1.8 1.8 -45.0 3.87e+00 4.33e-01 2004-09-18T16:14:42.183 A 3.99e+00 5.12e-01 2004-09-18T22:34:28.703 A 3.86e+00 4.12e-01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034405.8+320001 056.0240691 7.06e-07 +32.0003141 7.06e-07 A A 03440576+3200010 -1.98 -5.20e-01 4.96e-02 3.54e+00 6 YSOc_star+dust(IR1) 18.58 2.61 11.87 0.3850 0.35 3 2.17e-01 4.32e-02 1999-12-27T17:16:39 C 9.67e-01 6.24e-02 1999-12-27T17:16:39 A 1.76e+00 7.46e-02 1999-12-27T17:16:39 A 2.55e+00 1.31e-01 2004-02-11T00:28:52.750 A 2.59e+00 1.31e-01 2004-02-11T00:28:52.750 A 2.61e+00 1.26e-01 2004-02-11T00:28:52.750 A A 1 1.77 1.5 1.5 -45.0 2.63e+00 1.31e-01 2004-02-11T00:28:52.750 A 2.61e+00 1.30e-01 2004-02-11T00:28:52.750 A 2.66e+00 1.30e-01 2004-02-11T00:28:52.750 A A 1 2.27 1.7 1.7 -45.0 2.78e+00 1.70e-01 2004-02-11T00:28:52.750 A 2.63e+00 1.60e-01 2004-02-11T00:28:52.750 A 2.75e+00 1.39e-01 2004-02-11T00:28:52.750 A A 1 2.54 1.8 1.8 -45.0 3.12e+00 1.67e-01 2004-02-11T00:28:52.750 A 3.09e+00 1.69e-01 2004-02-11T00:28:52.750 A 3.11e+00 1.57e-01 2004-02-11T00:28:52.750 A A 1 2.54 1.8 1.8 -45.0 7.42e+00 7.49e-01 2004-09-18T16:14:42.183 A 6.81e+00 7.07e-01 2004-09-18T22:34:28.703 A 7.25e+00 7.06e-01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034405.8+320028 056.0240765 5.06e-07 +32.0079096 5.06e-07 A A 03440577+3200284 -5.00 -3.80e-01 4.85e-02 1.06e+02 6 YSOc -999.00 -999.00 -999.00 -999.0000 -999.00 0 4.15e-02 -9.99e+02 1999-12-27T17:16:39 U 1.33e-01 -9.99e+02 1999-12-27T17:16:39 U 1.82e+00 7.55e-02 1999-12-27T17:16:39 A 1.37e+01 6.86e-01 2004-02-11T00:28:52.750 A 1.28e+01 6.47e-01 2004-02-11T00:28:52.750 A 1.40e+01 6.73e-01 2004-02-11T00:28:52.750 A A 1 1.77 1.5 1.5 -45.0 2.28e+01 1.12e+00 2004-02-11T00:28:52.750 A 2.21e+01 1.11e+00 2004-02-11T00:28:52.750 A 2.14e+01 1.12e+00 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 2.88e+01 1.41e+00 2004-02-11T00:28:52.750 A 2.45e+01 1.21e+00 2004-02-11T00:28:52.750 A 2.74e+01 1.29e+00 2004-02-11T00:28:52.750 A B 1 2.54 1.8 1.8 -45.0 2.48e+01 1.21e+00 2004-02-11T00:28:52.750 A 2.27e+01 1.12e+00 2004-02-11T00:28:52.750 A 2.39e+01 1.15e+00 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 1.79e+01 1.68e+00 2004-09-18T16:14:42.183 A 1.80e+01 1.70e+00 2004-09-18T22:34:28.703 A 1.82e+01 1.70e+00 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034406.0+321532 056.0250403 8.80e-07 +32.2589212 8.80e-07 A A 03440599+3215321 -1.57 -1.05e+00 5.62e-02 1.36e+00 6 YSOc_star+dust(IR1) 5.38 2.38 12.72 0.3580 0.50 3 2.77e+00 8.17e-02 1999-12-27T17:16:57 A 3.14e+00 1.19e-01 1999-12-27T17:16:57 A 3.15e+00 9.86e-02 1999-12-27T17:16:57 A 2.70e+00 1.37e-01 2004-02-11T00:28:52.750 A 2.68e+00 1.39e-01 2004-02-11T00:28:52.750 A 2.73e+00 1.38e-01 2004-02-11T00:28:52.750 A A 1 3.09 1.5 1.5 -45.0 2.47e+00 1.24e-01 2004-02-11T00:28:52.750 A 2.49e+00 1.32e-01 2004-02-11T00:28:52.750 A 2.47e+00 1.25e-01 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 2.41e+00 1.41e-01 2004-02-11T00:28:52.750 A 2.51e+00 1.53e-01 2004-02-11T00:28:52.750 A 2.47e+00 1.35e-01 2004-02-11T00:28:52.750 A A 1 3.92 1.8 1.8 -45.0 2.44e+00 1.60e-01 2004-02-11T00:28:52.750 A 2.85e+00 1.96e-01 2004-02-11T00:28:52.750 A 2.60e+00 1.48e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 2.61e+00 4.17e-01 2004-09-18T16:14:42.183 B 3.04e+00 4.72e-01 2004-09-18T22:34:28.703 B 2.77e+00 3.48e-01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034406.5+314325 056.0269527 5.78e-07 +31.7236284 5.78e-07 A A 03440646+3143250 -5.00 -2.34e+00 5.68e-02 2.40e+00 6 YSOc_star+dust(MP1) 2.92 1.33 9.98 0.1120 1.23 6 5.14e+01 -9.99e+02 1999-12-27T17:16:22 U 6.34e+01 2.16e+00 1999-12-27T17:16:22 A 4.94e+01 1.50e+00 1999-12-27T17:16:22 A 2.35e+01 1.22e+00 2004-09-07T07:03:48.646 A 2.17e+01 1.38e+00 2004-09-07T07:03:48.646 A 2.06e+01 1.26e+00 2004-09-07T07:03:48.646 A A 1 3.09 1.5 1.5 -45.0 1.61e+01 7.94e-01 2004-09-07T07:03:48.646 A 1.52e+01 9.11e-01 2004-09-07T07:03:48.646 A 1.55e+01 7.42e-01 2005-09-16T09:10:33.865 A A 1 2.27 1.7 1.7 -45.0 1.10e+01 5.37e-01 2004-09-07T07:03:48.646 A 1.03e+01 5.78e-01 2004-09-07T07:03:48.646 A 1.03e+01 5.61e-01 2004-09-07T07:03:48.646 A A 1 3.92 1.8 1.8 -45.0 6.83e+00 3.34e-01 2004-09-07T07:03:48.646 A 6.85e+00 3.59e-01 2004-09-07T07:03:48.646 A 6.95e+00 3.28e-01 2005-09-16T09:10:33.865 A A 1 2.54 1.8 1.8 -45.0 2.28e+00 3.69e-01 2004-09-18T16:14:42.183 B 1.82e+00 3.53e-01 2004-09-18T22:34:28.703 B 2.14e+00 2.83e-01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034406.8+320754 056.0283265 7.42e-07 +32.1316729 7.42e-07 A A 03440678+3207540 -5.00 -9.80e-01 4.91e-02 8.90e+00 6 YSOc_star+dust(IR2) 9.49 1.41 10.47 0.1630 1.76 4 9.83e+00 2.17e-01 1999-12-27T17:16:48 A 1.42e+01 3.54e-01 1999-12-27T17:16:48 A 1.33e+01 3.06e-01 1999-12-27T17:16:48 A 1.05e+01 5.37e-01 2004-02-11T00:28:52.750 A 1.16e+01 6.24e-01 2004-02-11T00:28:52.750 A 1.09e+01 6.01e-01 2004-02-11T00:28:52.750 A A 1 3.09 1.5 1.5 -45.0 9.25e+00 4.62e-01 2004-02-11T00:28:52.750 A 1.00e+01 5.08e-01 2004-02-11T00:28:52.750 A 9.35e+00 4.76e-01 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 8.02e+00 4.09e-01 2004-02-11T00:28:52.750 A 9.00e+00 4.63e-01 2004-02-11T00:28:52.750 A 8.46e+00 4.25e-01 2004-02-11T00:28:52.750 A A 1 3.92 1.8 1.8 -45.0 8.77e+00 4.51e-01 2004-02-11T00:28:52.750 A 9.54e+00 5.07e-01 2004-02-11T00:28:52.750 A 9.30e+00 4.60e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 1.45e+01 1.39e+00 2004-09-18T16:14:42.183 A 1.39e+01 1.33e+00 2004-09-18T22:34:28.703 A 1.43e+01 1.34e+00 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034409.2+320709 056.0381794 6.09e-07 +32.1192439 6.09e-07 A A 03440915+3207093 -5.00 -2.27e+00 5.09e-02 4.66e+00 6 YSOc_star+dust(MP1) 1.31 1.09 8.42 0.0985 1.80 7 4.55e+02 1.05e+01 1998-10-05T21:29:14 A 3.24e+02 5.36e+00 1998-10-05T21:29:14 A 2.39e+02 3.96e+00 1998-10-05T21:29:14 A 1.05e+02 5.26e+00 2004-02-11T00:28:52.750 A 1.01e+02 5.37e+00 2004-02-11T00:28:52.750 A 9.79e+01 5.33e+00 2004-02-11T00:28:52.750 A A 1 1.77 1.5 1.5 -45.0 6.80e+01 3.29e+00 2004-02-11T00:28:52.750 A 6.39e+01 3.36e+00 2004-02-11T00:28:52.750 A 6.64e+01 3.23e+00 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 4.62e+01 2.22e+00 2004-02-11T00:28:52.750 A 4.59e+01 2.28e+00 2004-02-11T00:28:52.750 A 4.63e+01 2.21e+00 2004-02-11T00:28:52.750 A A 1 3.92 1.8 1.8 -45.0 3.62e+01 1.86e+00 2004-02-11T00:28:52.750 A 3.55e+01 1.99e+00 2004-02-11T00:28:52.750 A 3.68e+01 1.91e+00 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 1.12e+01 1.23e+00 2004-09-18T16:14:42.183 A 9.68e+00 1.04e+00 2004-09-18T22:34:28.703 A 1.00e+01 1.02e+00 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034410.1+320405 056.0422024 6.50e-07 +32.0679211 6.50e-07 A A 03441012+3204045 -5.00 -9.40e-01 5.26e-02 9.71e+00 6 YSOc_star+dust(IR2) 9.63 1.39 10.67 0.1580 1.48 4 8.19e+00 1.81e-01 1998-10-05T21:29:14 A 1.11e+01 2.15e-01 1998-10-05T21:29:14 A 1.13e+01 2.18e-01 1998-10-05T21:29:14 A 9.10e+00 4.51e-01 2004-02-11T00:28:52.750 A 9.42e+00 4.77e-01 2004-02-11T00:28:52.750 A 8.80e+00 4.21e-01 2004-02-11T00:28:52.750 A A 1 1.77 1.5 1.5 -45.0 8.35e+00 4.10e-01 2004-02-11T00:28:52.750 A 8.08e+00 3.99e-01 2004-02-11T00:28:52.750 A 8.12e+00 4.02e-01 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 7.10e+00 4.67e-01 2004-02-11T00:28:52.750 A 7.75e+00 4.54e-01 2004-02-11T00:28:52.750 A 7.36e+00 3.59e-01 2004-02-11T00:28:52.750 A A 1 2.54 1.8 1.8 -45.0 8.35e+00 5.00e-01 2004-02-11T00:28:52.750 A 7.40e+00 5.17e-01 2004-02-11T00:28:52.750 A 7.89e+00 4.67e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 1.44e+01 1.81e+00 2004-09-18T16:14:42.183 A -9.99e+02 -9.99e+02 null U 1.46e+01 1.60e+00 2004-09-18T19:24:35.443 A Q 3 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034410.2+320734 056.0426472 1.54e-08 +32.1262402 1.54e-08 A A 03441022+3207344 -1.52 -1.44e+00 8.40e-02 7.90e-01 6 YSOc_star+dust(IR2) 9.71 1.42 12.16 0.1630 1.82 4 2.04e+00 5.84e-02 1998-10-05T21:29:05 A 2.84e+00 9.16e-02 1998-10-05T21:29:05 A 2.75e+00 8.86e-02 1998-10-05T21:29:05 A 2.29e+00 1.13e-01 2004-02-11T00:28:52.750 A 2.24e+00 1.25e-01 2004-02-11T00:28:52.750 A 2.26e+00 1.22e-01 2004-02-11T00:28:52.750 A A 1 3.09 1.5 1.5 -45.0 2.00e+00 1.03e-01 2004-02-11T00:28:52.750 A 1.92e+00 9.97e-02 2004-02-11T00:28:52.750 A 1.96e+00 9.87e-02 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 1.75e+00 1.26e-01 2004-02-11T00:28:52.750 A 1.85e+00 1.28e-01 2004-02-11T00:28:52.750 A 1.80e+00 1.11e-01 2004-02-11T00:28:52.750 A A 1 3.92 1.8 1.8 -45.0 1.30e+00 1.61e-01 2004-02-11T00:28:52.750 A 1.47e+00 1.75e-01 2004-02-11T00:28:52.750 A 1.41e+00 1.35e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 1.48e+00 4.73e-01 2004-09-18T19:24:35.443 C Q -2 8.04 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034411.6+320313 056.0484413 5.61e-07 +32.0536368 5.61e-07 A A 03441162+3203131 -5.00 -1.10e+00 4.85e-02 1.75e+00 6 YSOc_star+dust(IR3) 18.22 1.23 8.07 0.1250 1.88 5 1.24e+01 2.28e-01 1998-10-05T21:29:14 A 3.34e+01 5.53e-01 1998-10-05T21:29:14 A 4.92e+01 8.16e-01 1998-10-05T21:29:14 A 5.64e+01 2.74e+00 2004-02-11T00:28:52.750 A 4.91e+01 2.64e+00 2004-02-11T00:28:52.750 A 5.42e+01 2.63e+00 2004-02-11T00:28:52.750 A A 1 1.77 1.5 1.5 -45.0 4.72e+01 2.35e+00 2004-02-11T00:28:52.750 A 4.05e+01 2.29e+00 2004-02-11T00:28:52.750 A 4.38e+01 2.30e+00 2004-02-11T00:28:52.750 A B 1 2.97 1.7 1.7 -45.0 4.50e+01 2.58e+00 2004-02-11T00:28:52.750 A 4.17e+01 2.02e+00 2004-02-11T00:28:52.750 A 4.80e+01 2.26e+00 2004-02-11T00:28:52.750 A A 1 2.54 1.8 1.8 -45.0 4.64e+01 2.34e+00 2004-02-11T00:28:52.750 A 4.58e+01 2.37e+00 2004-02-11T00:28:52.750 A 4.67e+01 2.28e+00 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 4.03e+01 3.94e+00 2004-09-18T16:14:42.183 A 4.01e+01 3.99e+00 2004-09-18T22:34:28.703 A 4.06e+01 3.87e+00 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034415.8+315937 056.0659829 5.56e-07 +31.9935402 5.56e-07 A A 03441583+3159367 -1.70 -8.10e-01 4.89e-02 1.67e+01 6 YSOc_star+dust(IR2) 8.55 1.39 11.73 0.1580 1.25 4 3.76e+00 7.63e-02 1998-10-05T21:29:14 A 5.05e+00 1.12e-01 1998-10-05T21:29:14 A 4.72e+00 1.13e-01 1998-10-05T21:29:14 A 3.35e+00 1.64e-01 2004-02-11T00:28:52.750 A 3.48e+00 1.77e-01 2004-02-11T00:28:52.750 A 3.54e+00 1.69e-01 2004-02-11T00:28:52.750 A A 1 1.77 1.5 1.5 -45.0 2.86e+00 1.44e-01 2004-02-11T00:28:52.750 A 2.93e+00 1.50e-01 2004-02-11T00:28:52.750 A 3.09e+00 1.46e-01 2004-02-11T00:28:52.750 A A 1 2.27 1.7 1.7 -45.0 2.61e+00 1.42e-01 2004-02-11T00:28:52.750 A 2.73e+00 1.51e-01 2004-02-11T00:28:52.750 A 2.79e+00 1.36e-01 2004-02-11T00:28:52.750 A A 1 2.54 1.8 1.8 -45.0 3.54e+00 1.85e-01 2004-02-11T00:28:52.750 A 3.19e+00 1.74e-01 2004-02-11T00:28:52.750 A 3.68e+00 1.76e-01 2004-02-11T00:28:52.750 A A 1 2.54 1.8 1.8 -45.0 7.94e+00 8.27e-01 2004-09-18T16:14:42.183 A 6.59e+00 6.75e-01 2004-09-18T22:34:28.703 A 7.23e+00 7.10e-01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034418.1+321053 056.0754230 6.40e-07 +32.1815206 6.40e-07 A A 03441810+3210534 -5.00 -9.00e-01 5.50e-02 5.04e+00 5 YSOc_star+dust(IR4) 25.83 1.78 8.48 0.1260 1.69 4 8.14e-01 4.50e-02 1998-10-05T21:29:05 A 3.73e+00 -9.99e+02 1998-10-05T21:29:05 U 8.49e+00 -9.99e+02 1998-10-05T21:29:05 U 1.94e+01 9.99e-01 2004-02-11T00:28:52.750 A 2.23e+01 1.17e+00 2004-02-11T00:28:52.750 A 2.05e+01 1.10e+00 2004-02-11T00:28:52.750 A A 1 3.09 1.5 1.5 -45.0 1.84e+01 9.18e-01 2004-02-11T00:28:52.750 A 2.14e+01 1.06e+00 2004-02-11T00:28:52.750 A 1.96e+01 9.86e-01 2004-02-11T00:28:52.750 A B 1 2.97 1.7 1.7 -45.0 1.70e+01 8.27e-01 2004-02-11T00:28:52.750 A 1.80e+01 1.56e+00 2004-02-11T00:28:52.750 A 1.79e+01 8.74e-01 2004-02-11T00:28:52.750 A A 1 3.92 1.8 1.8 -45.0 1.73e+01 8.66e-01 2004-02-11T00:28:52.750 A 1.85e+01 9.35e-01 2004-02-11T00:28:52.750 A 1.81e+01 8.77e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 2.84e+01 2.69e+00 2004-09-18T16:14:42.183 A 2.76e+01 2.62e+00 2004-09-18T22:34:28.703 A 2.80e+01 2.63e+00 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034418.2+320457 056.0756968 5.54e-07 +32.0824939 5.54e-07 A A 03441816+3204570 -5.00 -4.90e-01 4.82e-02 7.47e+01 6 YSOc_star+dust(IR3) 14.94 1.23 7.94 0.1260 0.31 5 2.32e+01 4.49e-01 1998-10-05T21:29:14 A 6.22e+01 1.03e+00 1998-10-05T21:29:14 A 8.88e+01 1.47e+00 1998-10-05T21:29:14 A 8.01e+01 4.47e+00 2004-02-11T00:28:52.750 A 6.06e+01 3.61e+00 2004-02-11T00:28:52.750 A 7.56e+01 3.74e+00 2004-02-11T00:28:52.750 K B 7 1.77 1.5 1.5 -45.0 6.97e+01 3.47e+00 2004-02-11T00:28:52.750 A 5.52e+01 2.87e+00 2004-02-11T00:28:52.750 A 5.41e+01 3.03e+00 2004-02-11T00:28:52.750 A B 1 2.97 1.7 1.7 -45.0 5.82e+01 2.81e+00 2004-02-11T00:28:52.750 A 4.64e+01 2.28e+00 2004-02-11T00:28:52.750 A 6.62e+01 3.12e+00 2004-02-11T00:28:52.750 A B 1 2.54 1.8 1.8 -45.0 5.47e+01 2.76e+00 2004-02-11T00:28:52.750 A 5.17e+01 2.69e+00 2004-02-11T00:28:52.750 A 5.34e+01 2.63e+00 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 4.82e+02 4.48e+01 2004-09-18T16:14:42.183 A 4.61e+02 4.30e+01 2004-09-18T22:34:28.703 A 4.69e+02 4.36e+01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034418.2+320959 056.0758816 8.07e-07 +32.1664836 8.07e-07 A A 03441820+3209593 -5.00 -1.21e+00 5.20e-02 2.10e+01 6 YSOc_star+dust(IR3) 8.09 1.23 10.85 0.1270 1.60 5 8.25e+00 1.60e-01 1998-10-05T21:29:05 A 1.27e+01 2.11e-01 1998-10-05T21:29:05 A 1.19e+01 2.20e-01 1998-10-05T21:29:05 A 7.75e+00 3.90e-01 2004-02-11T00:28:52.750 A 7.18e+00 3.91e-01 2004-02-11T00:28:52.750 A 6.93e+00 4.01e-01 2004-02-11T00:28:52.750 A A 1 3.09 1.5 1.5 -45.0 6.05e+00 3.11e-01 2004-02-11T00:28:52.750 A 5.97e+00 2.99e-01 2004-02-11T00:28:52.750 A 6.01e+00 3.01e-01 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 4.92e+00 2.68e-01 2004-02-11T00:28:52.750 A 5.00e+00 2.69e-01 2004-02-11T00:28:52.750 A 4.97e+00 2.54e-01 2004-02-11T00:28:52.750 A A 1 3.92 1.8 1.8 -45.0 4.31e+00 2.61e-01 2004-02-11T00:28:52.750 A 4.56e+00 2.79e-01 2004-02-11T00:28:52.750 A 4.47e+00 2.42e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 8.15e+00 9.14e-01 2004-09-18T16:14:42.183 A 8.60e+00 9.72e-01 2004-09-18T22:34:28.703 A 8.40e+00 8.70e-01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034418.3+320732 056.0761297 2.02e-08 +32.1256863 2.02e-08 A A null -1.59 -1.55e+00 8.21e-02 6.66e+00 5 YSOc_star+dust(IR4) 16.41 6.66 11.07 0.3880 0.14 3 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 3.75e+00 1.89e-01 2004-02-11T00:28:52.750 A 3.94e+00 2.03e-01 2004-02-11T00:28:52.750 A 3.77e+00 1.91e-01 2004-02-11T00:28:52.750 A A 1 1.77 1.5 1.5 -45.0 2.77e+00 1.43e-01 2004-02-11T00:28:52.750 A 2.87e+00 1.49e-01 2004-02-11T00:28:52.750 A 2.85e+00 1.45e-01 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 2.07e+00 1.49e-01 2004-02-11T00:28:52.750 A 2.02e+00 1.62e-01 2004-02-11T00:28:52.750 A 2.12e+00 1.31e-01 2004-02-11T00:28:52.750 A A 1 3.92 1.8 1.8 -45.0 2.16e+00 1.65e-01 2004-02-11T00:28:52.750 A 2.30e+00 1.85e-01 2004-02-11T00:28:52.750 A 2.24e+00 1.46e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 2.13e+00 3.58e-01 2004-09-18T16:14:42.183 B 1.89e+00 4.39e-01 2004-09-18T22:34:28.703 C 1.91e+00 4.10e-01 2004-09-18T19:24:35.443 C A -2 8.04 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034418.6+321253 056.0774695 5.08e-07 +32.2147489 5.08e-07 A A 03441857+3212530 -5.00 -8.30e-01 4.82e-02 2.29e+00 6 YSOc_star+dust(IR1) 13.60 2.36 10.06 0.3540 2.22 3 4.74e+00 1.09e-01 1998-10-05T21:29:05 A 9.92e+00 2.10e-01 1998-10-05T21:29:05 A 1.63e+01 3.16e-01 1998-10-05T21:29:05 A 2.26e+01 1.10e+00 2004-02-11T00:28:52.750 A 2.58e+01 1.29e+00 2004-02-11T00:28:52.750 A 2.34e+01 1.21e+00 2004-02-11T00:28:52.750 A A 1 3.09 1.5 1.5 -45.0 2.38e+01 1.14e+00 2004-02-11T00:28:52.750 A 2.74e+01 1.35e+00 2004-02-11T00:28:52.750 A 2.52e+01 1.23e+00 2004-02-11T00:28:52.750 A B 1 2.97 1.7 1.7 -45.0 2.30e+01 1.11e+00 2004-02-11T00:28:52.750 A 2.64e+01 1.27e+00 2004-02-11T00:28:52.750 A 2.49e+01 1.19e+00 2004-02-11T00:28:52.750 A B 1 3.92 1.8 1.8 -45.0 2.36e+01 1.13e+00 2004-02-11T00:28:52.750 A 2.52e+01 1.27e+00 2004-02-11T00:28:52.750 A 2.46e+01 1.17e+00 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 3.15e+01 2.94e+00 2004-09-18T16:14:42.183 A 2.86e+01 2.68e+00 2004-09-18T22:34:28.703 A 3.01e+01 2.80e+00 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034419.1+320931 056.0797334 6.88e-07 +32.1586865 6.88e-07 A A 03441912+3209313 -5.00 -2.37e+00 5.59e-02 6.04e+00 6 YSOc_star+dust(MP1) 0.78 1.09 9.64 0.0993 0.74 7 1.37e+02 2.65e+00 1998-10-05T21:29:05 A 1.18e+02 1.96e+00 1998-10-05T21:29:05 A 8.32e+01 1.38e+00 1998-10-05T21:29:05 A 3.53e+01 1.84e+00 2004-02-11T00:28:52.750 A 3.61e+01 1.85e+00 2004-02-11T00:28:52.750 A 3.24e+01 1.95e+00 2004-02-11T00:28:52.750 A A 1 3.09 1.5 1.5 -45.0 2.38e+01 1.18e+00 2004-02-11T00:28:52.750 A 2.33e+01 1.20e+00 2004-02-11T00:28:52.750 A 2.33e+01 1.20e+00 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 1.58e+01 7.64e-01 2004-02-11T00:28:52.750 A 1.60e+01 8.03e-01 2004-02-11T00:28:52.750 A 1.60e+01 7.68e-01 2004-02-11T00:28:52.750 A A 1 3.92 1.8 1.8 -45.0 9.67e+00 4.95e-01 2004-02-11T00:28:52.750 A 9.83e+00 5.08e-01 2004-02-11T00:28:52.750 A 9.81e+00 4.83e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 3.58e+00 5.47e-01 2004-09-18T16:14:42.183 B 3.50e+00 6.39e-01 2004-09-18T22:34:28.703 B 3.78e+00 4.79e-01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034419.1+321718 056.0797337 1.26e-06 +32.2883878 1.26e-06 A A 03441913+3217177 -5.00 -2.66e+00 7.25e-02 2.82e+01 6 YSOc -999.00 -999.00 -999.00 -999.0000 -999.00 0 5.71e+04 1.04e+04 1998-10-05T21:28:57 C 3.22e+04 4.93e+03 1998-10-05T21:28:57 C 2.17e+04 4.96e+03 1998-10-05T21:28:57 D 1.92e+03 3.92e+02 2004-02-11T00:28:52.750 C 1.77e+03 3.54e+02 2004-02-11T00:28:52.750 C 1.98e+03 3.99e+02 2004-02-11T00:28:52.750 C A 7 3.09 1.5 1.5 -45.0 2.86e+03 3.31e+02 2004-02-11T00:28:52.750 A 1.24e+03 2.21e+02 2004-02-11T00:28:52.750 B 1.13e+03 2.06e+02 2004-02-11T00:28:52.750 B C 7 2.97 1.7 1.7 -45.0 3.28e+03 1.63e+02 2004-02-11T00:28:52.750 A 2.73e+03 3.07e+02 2004-02-11T00:28:52.750 A 3.32e+03 2.54e+02 2004-02-11T00:28:52.750 A A 7 3.92 1.8 1.8 -45.0 2.05e+03 1.14e+02 2004-02-11T00:28:52.750 A 2.04e+03 1.22e+02 2004-02-11T00:28:52.750 A 2.12e+03 1.28e+02 2004-02-11T00:28:52.750 A A 2 288.80 17.7 14.4 -85.0 2.05e+02 1.90e+01 2004-09-18T16:14:42.183 A 1.99e+02 1.85e+01 2004-09-18T22:34:28.703 A 2.04e+02 1.89e+01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034419.2+320735 056.0801896 7.96e-07 +32.1263094 7.96e-07 A A null -5.00 -1.26e+00 5.68e-02 4.05e+00 5 YSOc_star+dust(IR4) 31.61 6.51 8.49 0.3780 0.11 3 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 1.43e+01 7.40e-01 2004-02-11T00:28:52.750 A 1.72e+01 1.27e+00 2004-02-11T00:28:52.750 A 1.61e+01 8.21e-01 2004-02-11T00:28:52.750 A A 1 1.77 1.5 1.5 -45.0 1.21e+01 6.00e-01 2004-02-11T00:28:52.750 A 1.52e+01 7.63e-01 2004-02-11T00:28:52.750 A 1.35e+01 7.09e-01 2004-02-11T00:28:52.750 A B 1 2.97 1.7 1.7 -45.0 1.19e+01 6.50e-01 2004-02-11T00:28:52.750 A 1.40e+01 7.10e-01 2004-02-11T00:28:52.750 A 1.27e+01 6.46e-01 2004-02-11T00:28:52.750 A B 1 3.92 1.8 1.8 -45.0 1.42e+01 7.60e-01 2004-02-11T00:28:52.750 A 1.44e+01 8.06e-01 2004-02-11T00:28:52.750 A 1.44e+01 7.36e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 8.21e+00 8.85e-01 2004-09-18T16:14:42.183 A 8.87e+00 1.18e+00 2004-09-18T22:34:28.703 A 8.29e+00 8.39e-01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034420.2+320857 056.0841041 8.39e-07 +32.1490327 8.39e-07 A A 03442017+3208565 -5.00 -1.39e+00 5.93e-02 4.58e+00 6 YSOc_star+dust(IR2) 5.65 1.43 10.76 0.1670 0.01 4 1.32e+01 2.44e-01 1998-10-05T21:29:05 A 1.97e+01 3.27e-01 1998-10-05T21:29:05 A 1.82e+01 3.02e-01 1998-10-05T21:29:05 A 1.13e+01 5.83e-01 2004-02-11T00:28:52.750 A 1.06e+01 5.84e-01 2004-02-11T00:28:52.750 A 9.79e+00 5.87e-01 2004-02-11T00:28:52.750 A A 1 3.09 1.5 1.5 -45.0 9.41e+00 4.68e-01 2004-02-11T00:28:52.750 A 9.16e+00 4.75e-01 2004-02-11T00:28:52.750 A 9.33e+00 4.71e-01 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 8.57e+00 4.41e-01 2004-02-11T00:28:52.750 A 8.32e+00 4.51e-01 2004-02-11T00:28:52.750 A 8.33e+00 4.33e-01 2004-02-11T00:28:52.750 A A 1 3.92 1.8 1.8 -45.0 8.72e+00 5.17e-01 2004-02-11T00:28:52.750 A 8.63e+00 5.67e-01 2004-02-11T00:28:52.750 A 8.63e+00 4.98e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U 5.82e+00 9.37e-01 2004-09-18T22:34:28.703 B 5.62e+00 7.82e-01 2004-09-18T19:24:35.443 A Q 7 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034421.2+320115 056.0884625 7.00e-07 +32.0206995 7.00e-07 A A 03442122+3201144 -2.00 -9.20e-01 4.98e-02 2.78e+01 6 YSOc_star+dust(IR4) 19.57 1.15 10.95 0.1060 0.48 6 4.35e-01 4.29e-02 1998-10-05T21:29:14 B 2.00e+00 7.91e-02 1998-10-05T21:29:14 A 3.56e+00 9.85e-02 1998-10-05T21:29:14 A 3.26e+00 1.60e-01 2004-02-11T00:28:52.750 A 3.39e+00 1.75e-01 2004-02-11T00:28:52.750 A 3.25e+00 1.57e-01 2004-02-11T00:28:52.750 A A 1 1.77 1.5 1.5 -45.0 2.86e+00 1.39e-01 2004-02-11T00:28:52.750 A 2.78e+00 1.40e-01 2004-02-11T00:28:52.750 A 2.80e+00 1.41e-01 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 2.20e+00 1.26e-01 2004-02-11T00:28:52.750 A 2.27e+00 1.31e-01 2004-02-11T00:28:52.750 A 2.26e+00 1.12e-01 2004-02-11T00:28:52.750 A A 1 2.54 1.8 1.8 -45.0 2.10e+00 1.23e-01 2004-02-11T00:28:52.750 A 2.25e+00 1.35e-01 2004-02-11T00:28:52.750 A 2.18e+00 1.17e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 6.73e+00 7.13e-01 2004-09-18T16:14:42.183 A 6.14e+00 6.30e-01 2004-09-18T22:34:28.703 A 6.47e+00 6.36e-01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034421.3+321237 056.0886893 7.91e-07 +32.2103397 7.91e-07 A A 03442127+3212372 -1.68 -1.35e+00 5.68e-02 2.34e+00 6 YSOc_star+dust(IR2) 7.58 1.44 11.54 0.1660 0.59 4 5.28e+00 1.99e-01 1998-10-05T21:29:05 A 6.97e+00 3.02e-01 1998-10-05T21:29:05 A 6.59e+00 2.31e-01 1998-10-05T21:29:05 E 4.86e+00 2.51e-01 2004-02-11T00:28:52.750 A 4.97e+00 2.56e-01 2004-02-11T00:28:52.750 A 4.45e+00 2.59e-01 2004-02-11T00:28:52.750 A A 1 3.09 1.5 1.5 -45.0 4.40e+00 2.14e-01 2004-02-11T00:28:52.750 A 4.00e+00 2.08e-01 2004-02-11T00:28:52.750 A 4.30e+00 2.09e-01 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 3.65e+00 2.05e-01 2004-02-11T00:28:52.750 A 3.62e+00 2.00e-01 2004-02-11T00:28:52.750 A 3.72e+00 1.91e-01 2004-02-11T00:28:52.750 A A 1 3.92 1.8 1.8 -45.0 3.26e+00 2.19e-01 2004-02-11T00:28:52.750 A 3.09e+00 2.20e-01 2004-02-11T00:28:52.750 A 3.23e+00 1.90e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 3.42e+00 5.28e-01 2004-09-18T16:14:42.183 B 2.85e+00 3.88e-01 2004-09-18T22:34:28.703 A 3.02e+00 3.68e-01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034421.3+321156 056.0887800 7.06e-07 +32.1989666 7.06e-07 A A 03442129+3211563 -5.00 -1.04e+00 5.05e-02 1.20e+01 6 YSOc_star+dust(IR3) 7.88 1.23 10.31 0.1270 1.67 5 1.37e+01 2.65e-01 1998-10-05T21:29:05 A 2.20e+01 3.65e-01 1998-10-05T21:29:05 A 1.97e+01 3.27e-01 1998-10-05T21:29:05 A 1.40e+01 6.92e-01 2004-02-11T00:28:52.750 A 1.22e+01 6.32e-01 2004-02-11T00:28:52.750 A 1.15e+01 6.85e-01 2004-02-11T00:28:52.750 A A 1 3.09 1.5 1.5 -45.0 1.19e+01 5.95e-01 2004-02-11T00:28:52.750 A 9.28e+00 4.55e-01 2004-02-11T00:28:52.750 A 1.00e+01 4.95e-01 2004-02-11T00:28:52.750 A B 1 2.97 1.7 1.7 -45.0 1.10e+01 5.48e-01 2004-02-11T00:28:52.750 A 8.04e+00 4.16e-01 2004-02-11T00:28:52.750 A 9.60e+00 4.90e-01 2004-02-11T00:28:52.750 A C 1 3.92 1.8 1.8 -45.0 1.44e+01 7.66e-01 2004-02-11T00:28:52.750 A 1.02e+01 6.23e-01 2004-02-11T00:28:52.750 A 1.16e+01 6.22e-01 2004-02-11T00:28:52.750 A C 1 4.98 1.8 1.8 -45.0 1.36e+01 1.40e+00 2004-09-18T16:14:42.183 A 1.38e+01 1.36e+00 2004-09-18T22:34:28.703 A 1.38e+01 1.33e+00 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034421.6+321510 056.0898746 8.20e-07 +32.2527060 8.20e-07 A A 03442156+3215098 -5.00 -1.61e+00 5.72e-02 4.09e+01 6 YSOc_star+dust(MP1) 5.74 1.10 11.21 0.0998 0.89 7 1.01e+01 1.77e-01 1998-10-05T21:29:05 A 1.31e+01 2.30e-01 1998-10-05T21:29:05 A 1.15e+01 2.02e-01 1998-10-05T21:29:05 A 6.20e+00 3.05e-01 2004-02-11T00:28:52.750 A 5.93e+00 3.10e-01 2004-02-11T00:28:52.750 A 5.66e+00 3.22e-01 2004-02-11T00:28:52.750 A A 1 3.09 1.5 1.5 -45.0 4.16e+00 2.07e-01 2004-02-11T00:28:52.750 A 4.39e+00 2.19e-01 2004-02-11T00:28:52.750 A 4.29e+00 2.10e-01 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 2.97e+00 1.73e-01 2004-02-11T00:28:52.750 A 3.08e+00 1.77e-01 2004-02-11T00:28:52.750 A 3.04e+00 1.61e-01 2004-02-11T00:28:52.750 A A 1 3.92 1.8 1.8 -45.0 1.97e+00 1.56e-01 2004-02-11T00:28:52.750 A 2.00e+00 1.68e-01 2004-02-11T00:28:52.750 A 1.99e+00 1.33e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 5.07e+00 7.07e-01 2004-09-18T16:14:42.183 A 4.30e+00 7.42e-01 2004-09-18T22:34:28.703 B 4.84e+00 5.91e-01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034421.6+321038 056.0901011 5.42e-07 +32.1771253 5.42e-07 A A 03442161+3210376 -5.00 -1.00e+00 4.83e-02 8.99e+00 6 YSOc_star+dust(IR1) 10.00 2.36 9.49 0.3540 0.23 3 1.61e+01 3.11e-01 1998-10-05T21:29:05 A 3.15e+01 5.51e-01 1998-10-05T21:29:05 A 3.77e+01 6.24e-01 1998-10-05T21:29:05 A 5.54e+01 2.70e+00 2004-02-11T00:28:52.750 A 5.08e+01 2.77e+00 2004-02-11T00:28:52.750 A 5.25e+01 2.73e+00 2004-02-11T00:28:52.750 A A 1 3.09 1.5 1.5 -45.0 5.21e+01 2.63e+00 2004-02-11T00:28:52.750 A 4.77e+01 2.40e+00 2004-02-11T00:28:52.750 A 4.95e+01 2.47e+00 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 4.34e+01 2.07e+00 2004-02-11T00:28:52.750 A 3.97e+01 1.96e+00 2004-02-11T00:28:52.750 A 4.15e+01 1.99e+00 2004-02-11T00:28:52.750 A A 1 3.92 1.8 1.8 -45.0 3.76e+01 1.85e+00 2004-02-11T00:28:52.750 A 3.74e+01 1.87e+00 2004-02-11T00:28:52.750 A 3.79e+01 1.82e+00 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 5.99e+01 5.59e+00 2004-09-18T16:14:42.183 A 5.84e+01 5.49e+00 2004-09-18T22:34:28.703 A 5.91e+01 5.51e+00 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034422.3+320543 056.0928682 5.14e-07 +32.0952102 5.14e-07 A A 03442228+3205427 -5.00 -5.70e-01 4.80e-02 3.55e+01 6 YSOc_star+dust(IR3) 12.74 1.22 9.08 0.1240 1.14 5 1.53e+01 2.95e-01 1998-10-05T21:29:14 A 3.16e+01 5.53e-01 1998-10-05T21:29:14 A 3.49e+01 6.43e-01 1998-10-05T21:29:14 A 2.80e+01 1.38e+00 2004-02-11T00:28:52.750 A 2.83e+01 1.43e+00 2004-02-11T00:28:52.750 A 2.90e+01 1.38e+00 2004-02-11T00:28:52.750 A A 1 1.77 1.5 1.5 -45.0 2.28e+01 1.15e+00 2004-02-11T00:28:52.750 A 2.32e+01 1.14e+00 2004-02-11T00:28:52.750 A 2.29e+01 1.15e+00 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 2.08e+01 1.02e+00 2004-02-11T00:28:52.750 A 2.06e+01 1.02e+00 2004-02-11T00:28:52.750 A 2.15e+01 1.01e+00 2004-02-11T00:28:52.750 A A 1 2.54 1.8 1.8 -45.0 3.08e+01 1.59e+00 2004-02-11T00:28:52.750 A 3.08e+01 1.61e+00 2004-02-11T00:28:52.750 A 3.11e+01 1.54e+00 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 1.06e+02 9.92e+00 2004-09-18T16:14:42.183 A 1.08e+02 1.02e+01 2004-09-18T22:34:28.703 A 1.06e+02 9.90e+00 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034422.3+321201 056.0930726 6.38e-07 +32.2001870 6.38e-07 A A 03442232+3212007 -5.00 -8.60e-01 4.97e-02 6.11e+00 6 YSOc_star+dust(IR2) 8.55 1.42 10.06 0.1660 0.26 4 1.51e+01 3.07e-01 1998-10-05T21:29:05 A 2.35e+01 4.55e-01 1998-10-05T21:29:05 A 2.43e+01 4.69e-01 1998-10-05T21:29:05 A 1.62e+01 8.06e-01 2004-02-11T00:28:52.750 A 1.93e+01 9.67e-01 2004-02-11T00:28:52.750 A 1.61e+01 9.49e-01 2004-02-11T00:28:52.750 A B 1 3.09 1.5 1.5 -45.0 1.57e+01 7.75e-01 2004-02-11T00:28:52.750 A 1.87e+01 9.33e-01 2004-02-11T00:28:52.750 A 1.71e+01 8.47e-01 2004-02-11T00:28:52.750 A B 1 2.97 1.7 1.7 -45.0 1.66e+01 8.30e-01 2004-02-11T00:28:52.750 A 1.94e+01 9.54e-01 2004-02-11T00:28:52.750 A 1.80e+01 8.82e-01 2004-02-11T00:28:52.750 A B 1 3.92 1.8 1.8 -45.0 2.07e+01 1.05e+00 2004-02-11T00:28:52.750 A 2.28e+01 1.21e+00 2004-02-11T00:28:52.750 A 2.22e+01 1.09e+00 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 2.30e+01 2.28e+00 2004-09-18T16:14:42.183 A 2.35e+01 2.26e+00 2004-09-18T22:34:28.703 A 2.36e+01 2.24e+00 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034422.6+320154 056.0940913 5.53e-07 +32.0315690 5.53e-07 A A 03442257+3201536 -5.00 -9.20e-01 4.80e-02 1.37e+02 6 YSOc_star+dust(IR4) 6.00 1.13 10.09 0.1050 0.25 6 2.26e+01 4.37e-01 1998-10-05T21:29:14 A 3.55e+01 5.89e-01 1998-10-05T21:29:14 A 3.23e+01 5.36e-01 1998-10-05T21:29:14 A 1.68e+01 8.44e-01 2004-02-11T00:28:52.750 A 1.78e+01 8.92e-01 2004-02-11T00:28:52.750 A 1.72e+01 8.28e-01 2004-02-11T00:28:52.750 A A 1 1.77 1.5 1.5 -45.0 1.17e+01 5.84e-01 2004-02-11T00:28:52.750 A 1.16e+01 5.89e-01 2004-02-11T00:28:52.750 A 1.11e+01 5.92e-01 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 8.94e+00 4.41e-01 2004-02-11T00:28:52.750 A 8.81e+00 4.36e-01 2004-02-11T00:28:52.750 A 8.91e+00 4.21e-01 2004-02-11T00:28:52.750 A A 1 2.54 1.8 1.8 -45.0 8.13e+00 4.44e-01 2004-02-11T00:28:52.750 A 7.61e+00 3.92e-01 2004-02-11T00:28:52.750 A 7.55e+00 3.73e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 6.86e+01 6.40e+00 2004-09-18T16:14:42.183 A 6.66e+01 6.23e+00 2004-09-18T22:34:28.703 A 6.76e+01 6.28e+00 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034422.7+320142 056.0945996 3.26e-08 +32.0284268 3.26e-08 A A 03442270+3201423 -1.87 -1.36e+00 6.64e-02 1.60e+01 6 YSOc_star+dust(IR4) 13.66 1.14 11.41 0.1060 0.84 6 1.39e+00 5.74e-02 1998-10-05T21:29:14 A 3.20e+00 9.72e-02 1998-10-05T21:29:14 A 4.11e+00 1.06e-01 1998-10-05T21:29:14 A 2.89e+00 1.44e-01 2004-02-11T00:28:52.750 A 3.07e+00 1.68e-01 2004-02-11T00:28:52.750 A 3.04e+00 1.47e-01 2004-02-11T00:28:52.750 A A 1 1.77 1.5 1.5 -45.0 2.41e+00 1.20e-01 2004-02-11T00:28:52.750 A 2.36e+00 1.26e-01 2004-02-11T00:28:52.750 A 2.42e+00 1.22e-01 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 1.89e+00 1.18e-01 2004-02-11T00:28:52.750 A 2.02e+00 1.26e-01 2004-02-11T00:28:52.750 A 2.00e+00 1.01e-01 2004-02-11T00:28:52.750 A A 1 2.54 1.8 1.8 -45.0 1.82e+00 1.13e-01 2004-02-11T00:28:52.750 A 2.02e+00 1.25e-01 2004-02-11T00:28:52.750 A 1.92e+00 1.09e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 6.61e+00 1.51e+00 2004-09-18T19:24:35.443 C Q -2 8.04 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034423.6+320934 056.0982514 1.73e-08 +32.1593990 1.73e-08 A A 03442356+3209338 -1.58 -1.51e+00 7.26e-02 4.14e+00 6 YSOc_star+dust(IR2) 6.46 1.40 11.56 0.1620 0.35 4 6.33e+00 1.40e-01 1998-10-05T21:29:05 A 8.19e+00 1.58e-01 1998-10-05T21:29:05 A 7.30e+00 1.41e-01 1998-10-05T21:29:05 A 4.83e+00 2.39e-01 2004-02-11T00:28:52.750 A 4.94e+00 2.53e-01 2004-02-11T00:28:52.750 A 4.60e+00 2.45e-01 2004-02-11T00:28:52.750 A A 1 3.09 1.5 1.5 -45.0 3.96e+00 1.99e-01 2004-02-11T00:28:52.750 A 4.10e+00 2.03e-01 2004-02-11T00:28:52.750 A 4.10e+00 1.98e-01 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 -9.99e+02 -9.99e+02 null U 3.43e+00 2.05e-01 2004-02-11T00:28:52.750 A 3.25e+00 1.77e-01 2004-02-11T00:28:52.750 A Q 1 3.92 1.8 1.8 -45.0 3.27e+00 2.47e-01 2004-02-11T00:28:52.750 A 3.11e+00 2.51e-01 2004-02-11T00:28:52.750 A 3.19e+00 2.17e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 3.80e+00 9.95e-01 2004-09-18T19:24:35.443 C Q -2 8.04 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034423.6+320153 056.0985405 5.85e-07 +32.0313156 5.85e-07 A A 03442364+3201526 -5.00 -1.01e+00 5.16e-02 1.94e+00 6 YSOc_star+dust(IR3) 16.38 1.23 10.23 0.1240 1.73 5 2.16e+00 5.97e-02 1998-10-05T21:29:14 A 6.42e+00 1.71e-01 1998-10-05T21:29:14 A 8.33e+00 1.84e-01 1998-10-05T21:29:14 A 7.62e+00 4.00e-01 2004-02-11T00:28:52.750 A 7.77e+00 4.03e-01 2004-02-11T00:28:52.750 A 7.76e+00 3.71e-01 2004-02-11T00:28:52.750 A A 1 1.77 1.5 1.5 -45.0 6.89e+00 3.35e-01 2004-02-11T00:28:52.750 A 6.90e+00 3.53e-01 2004-02-11T00:28:52.750 A 6.96e+00 3.41e-01 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 6.85e+00 3.35e-01 2004-02-11T00:28:52.750 A 6.87e+00 3.43e-01 2004-02-11T00:28:52.750 A 6.77e+00 3.20e-01 2004-02-11T00:28:52.750 A A 1 2.54 1.8 1.8 -45.0 8.44e+00 4.78e-01 2004-02-11T00:28:52.750 A 7.51e+00 4.10e-01 2004-02-11T00:28:52.750 A 7.42e+00 3.84e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 8.41e+00 1.03e+00 2004-09-18T16:14:42.183 A 8.33e+00 1.09e+00 2004-09-18T22:34:28.703 A 8.37e+00 9.13e-01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034424.5+320144 056.1019253 2.62e-08 +32.0288152 2.62e-08 A A 03442445+3201437 -1.62 -9.70e-01 6.90e-02 2.94e+00 6 YSOc_star+dust(IR2) 13.70 1.42 12.12 0.1600 1.61 4 8.35e-01 4.85e-02 1998-10-05T21:29:14 A 1.50e+00 7.47e-02 1998-10-05T21:29:14 A 2.05e+00 7.16e-02 1998-10-05T21:29:14 A 1.80e+00 9.41e-02 2004-02-11T00:28:52.750 A 1.97e+00 1.07e-01 2004-02-11T00:28:52.750 A 1.79e+00 8.73e-02 2004-02-11T00:28:52.750 A A 1 1.77 1.5 1.5 -45.0 1.92e+00 9.74e-02 2004-02-11T00:28:52.750 A 2.00e+00 1.03e-01 2004-02-11T00:28:52.750 A 1.99e+00 9.92e-02 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 1.79e+00 1.13e-01 2004-02-11T00:28:52.750 A 1.85e+00 1.31e-01 2004-02-11T00:28:52.750 A 1.73e+00 8.85e-02 2004-02-11T00:28:52.750 A A 1 2.54 1.8 1.8 -45.0 1.76e+00 1.18e-01 2004-02-11T00:28:52.750 A 1.87e+00 1.21e-01 2004-02-11T00:28:52.750 A 1.83e+00 1.10e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 3.04e+00 6.48e-01 2004-09-18T16:14:42.183 C -9.99e+02 -9.99e+02 null U 3.59e+00 8.85e-01 2004-09-18T19:24:35.443 C Q -2 8.04 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034424.8+321348 056.1034944 4.22e-09 +32.2300994 4.22e-09 A A null -1.54 1.73e+00 6.18e-02 1.21e+01 5 YSOc_red -999.00 -999.00 -999.00 -999.0000 -999.00 0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 1.73e-01 1.39e-02 2004-02-11T00:28:52.750 A 2.01e-01 1.73e-02 2004-02-11T00:28:52.750 A 1.86e-01 1.28e-02 2004-02-11T00:28:52.750 A A 1 3.09 1.5 1.5 -45.0 2.08e-01 1.60e-02 2004-02-11T00:28:52.750 A 2.69e-01 1.97e-02 2004-02-11T00:28:52.750 A 2.38e-01 1.54e-02 2004-02-11T00:28:52.750 A B 1 2.97 1.7 1.7 -45.0 -9.99e+02 -9.99e+02 null U 4.52e-01 1.23e-01 2004-02-11T00:28:52.750 C 2.64e-01 5.70e-02 2004-02-11T00:28:52.750 C Q 7 3.92 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U 1.16e+00 1.53e-01 2004-02-11T00:28:52.750 A 7.52e-01 1.57e-01 2004-02-11T00:28:52.750 C Q -2 4.98 1.8 1.8 -45.0 3.20e+01 2.99e+00 2004-09-18T16:14:42.183 A 2.91e+01 2.73e+00 2004-09-18T22:34:28.703 A 3.07e+01 2.86e+00 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034425.3+321013 056.1055056 2.58e-08 +32.1702010 2.58e-08 A A 03442530+3210128 -5.00 -1.20e+00 6.95e-02 6.21e+00 6 YSOc_star+dust(IR3) 10.35 1.23 10.15 0.1250 1.72 5 1.13e+01 3.22e-01 1998-10-05T21:29:05 A 1.69e+01 4.06e-01 1998-10-05T21:29:05 A 1.64e+01 3.92e-01 1998-10-05T21:29:05 A 1.17e+01 5.76e-01 2004-02-11T00:28:52.750 A 1.19e+01 6.43e-01 2004-02-11T00:28:52.750 A 1.24e+01 6.32e-01 2004-02-11T00:28:52.750 A A 1 3.09 1.5 1.5 -45.0 1.02e+01 5.26e-01 2004-02-11T00:28:52.750 A 9.97e+00 5.17e-01 2004-02-11T00:28:52.750 A 1.00e+01 4.98e-01 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 9.61e+00 5.03e-01 2004-02-11T00:28:52.750 A 9.39e+00 4.79e-01 2004-02-11T00:28:52.750 A 9.45e+00 4.78e-01 2004-02-11T00:28:52.750 A A 1 3.92 1.8 1.8 -45.0 1.16e+01 6.51e-01 2004-02-11T00:28:52.750 A 1.13e+01 6.57e-01 2004-02-11T00:28:52.750 A 1.17e+01 6.36e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 1.10e+01 3.53e+00 2004-09-18T19:24:35.443 C Q -2 8.04 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034425.5+321131 056.1063298 7.79e-07 +32.1919967 7.79e-07 A A 03442555+3211307 -5.00 -5.30e-01 4.90e-02 1.15e+01 6 YSOc_star+dust(IR1) 9.69 2.36 9.55 0.3540 0.14 3 1.62e+01 3.58e-01 1998-10-05T21:29:05 A 3.17e+01 6.13e-01 1998-10-05T21:29:05 A 3.68e+01 7.13e-01 1998-10-05T21:29:05 A 4.83e+01 2.73e+00 2004-02-11T00:28:52.750 A 4.56e+01 2.56e+00 2004-02-11T00:28:52.750 A 4.46e+01 2.53e+00 2004-02-11T00:28:52.750 A A 1 3.09 1.5 1.5 -45.0 4.78e+01 2.47e+00 2004-02-11T00:28:52.750 A 4.25e+01 2.27e+00 2004-02-11T00:28:52.750 A 4.12e+01 2.33e+00 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 4.27e+01 2.15e+00 2004-02-11T00:28:52.750 A 3.51e+01 1.76e+00 2004-02-11T00:28:52.750 A 3.72e+01 1.85e+00 2004-02-11T00:28:52.750 A B 1 3.92 1.8 1.8 -45.0 5.56e+01 2.74e+00 2004-02-11T00:28:52.750 A 4.80e+01 2.48e+00 2004-02-11T00:28:52.750 A 5.23e+01 2.55e+00 2004-02-11T00:28:52.750 A B 1 4.98 1.8 1.8 -45.0 1.29e+02 1.19e+01 2004-09-18T16:14:42.183 A 1.28e+02 1.18e+01 2004-09-18T22:34:28.703 A 1.30e+02 1.20e+01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034425.5+320617 056.1064507 6.73e-07 +32.1047603 6.73e-07 A A 03442554+3206171 -5.00 -1.03e+00 5.12e-02 3.19e+01 6 YSOc_star+dust(IR4) 11.27 1.13 9.77 0.1050 0.71 6 1.03e+01 2.08e-01 1998-10-05T21:29:14 A 2.15e+01 3.76e-01 1998-10-05T21:29:14 A 2.34e+01 4.53e-01 1998-10-05T21:29:14 A 1.51e+01 7.60e-01 2004-02-11T00:28:52.750 A 1.61e+01 8.67e-01 2004-02-11T00:28:52.750 A 1.60e+01 7.61e-01 2004-02-11T00:28:52.750 A A 1 1.77 1.5 1.5 -45.0 1.21e+01 6.03e-01 2004-02-11T00:28:52.750 A 1.23e+01 6.19e-01 2004-02-11T00:28:52.750 A 1.22e+01 5.99e-01 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 1.01e+01 5.29e-01 2004-02-11T00:28:52.750 A 1.15e+01 1.16e+00 2004-02-11T00:28:52.750 A 9.96e+00 4.81e-01 2004-02-11T00:28:52.750 A A 1 2.54 1.8 1.8 -45.0 1.11e+01 7.57e-01 2004-02-11T00:28:52.750 A 1.08e+01 8.50e-01 2004-02-11T00:28:52.750 A 1.10e+01 7.12e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 2.63e+01 2.80e+00 2004-09-18T16:14:42.183 A 2.61e+01 2.76e+00 2004-09-18T22:34:28.703 A 2.63e+01 2.65e+00 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034425.7+321549 056.1071327 8.94e-07 +32.2636720 8.94e-07 A A 03442570+3215492 -1.66 -8.10e-01 5.39e-02 1.29e+01 6 YSOc_star+dust(IR2) 12.10 1.41 11.91 0.1620 1.89 4 1.57e+00 5.50e-02 1998-10-05T21:28:57 A 2.35e+00 8.02e-02 1998-10-05T21:28:57 A 2.89e+00 8.53e-02 1998-10-05T21:28:57 A 2.48e+00 1.24e-01 2004-02-11T00:28:52.750 A 2.45e+00 1.24e-01 2004-02-11T00:28:52.750 A 2.44e+00 1.28e-01 2004-02-11T00:28:52.750 A A 1 3.09 1.5 1.5 -45.0 2.19e+00 1.13e-01 2004-02-11T00:28:52.750 A 2.15e+00 1.11e-01 2004-02-11T00:28:52.750 A 2.19e+00 1.09e-01 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 2.12e+00 1.41e-01 2004-02-11T00:28:52.750 A 1.90e+00 1.35e-01 2004-02-11T00:28:52.750 A 2.00e+00 1.23e-01 2004-02-11T00:28:52.750 A A 1 3.92 1.8 1.8 -45.0 2.23e+00 1.78e-01 2004-02-11T00:28:52.750 A 2.13e+00 1.68e-01 2004-02-11T00:28:52.750 A 2.16e+00 1.43e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 5.09e+00 6.27e-01 2004-09-18T16:14:42.183 A 5.07e+00 7.64e-01 2004-09-18T22:34:28.703 B 5.29e+00 5.70e-01 2004-09-18T19:24:35.443 A A 7 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034426.0+320430 056.1084831 2.68e-08 +32.0751238 2.68e-08 A A 03442602+3204304 -5.00 -9.40e-01 4.96e-02 1.59e+00 6 YSOc_star+dust(IR1) 10.64 2.37 6.96 0.3560 0.49 3 1.49e+02 2.88e+00 1998-10-05T21:29:14 A 2.91e+02 4.83e+00 1998-10-05T21:29:14 A 3.71e+02 9.23e+00 1998-10-05T21:29:14 A 4.51e+02 2.51e+01 2004-02-11T00:28:52.750 A 3.83e+02 2.63e+01 2004-02-11T00:28:52.750 A 3.89e+02 1.93e+01 2004-02-11T00:28:52.750 A A 7 1.77 1.5 1.5 -45.0 4.10e+02 2.15e+01 2004-02-11T00:28:52.750 A 4.08e+02 2.20e+01 2004-02-11T00:28:52.750 A 3.75e+02 2.17e+01 2004-02-11T00:28:52.750 A A 9 2.97 1.7 1.7 -45.0 3.71e+02 1.82e+01 2004-02-11T00:28:52.750 A 3.60e+02 1.85e+01 2004-02-11T00:28:52.750 A 3.49e+02 1.65e+01 2004-02-11T00:28:52.750 A A 1 2.54 1.8 1.8 -45.0 3.84e+02 1.86e+01 2004-02-11T00:28:52.750 A 3.91e+02 2.10e+01 2004-02-11T00:28:52.750 A 3.69e+02 2.26e+01 2004-02-11T00:28:52.750 A A -2 4.98 1.8 1.8 -45.0 4.78e+02 4.46e+01 2004-09-18T16:14:42.183 A 4.92e+02 4.61e+01 2004-09-18T22:34:28.703 A 4.74e+02 4.42e+01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034426.7+320820 056.1112447 8.18e-07 +32.1389822 8.18e-07 A A 03442668+3208203 -5.00 -9.20e-01 5.03e-02 1.74e+01 6 YSOc_star+dust(IR2) 14.63 1.42 9.02 0.1650 1.35 4 1.01e+01 2.24e-01 1998-10-05T21:29:05 A 2.44e+01 4.27e-01 1998-10-05T21:29:05 A 3.07e+01 5.94e-01 1998-10-05T21:29:05 A 3.22e+01 1.62e+00 2004-02-11T00:28:52.750 A 3.15e+01 1.72e+00 2004-02-11T00:28:52.750 A 2.97e+01 1.71e+00 2004-02-11T00:28:52.750 A A 1 3.09 1.5 1.5 -45.0 2.67e+01 1.37e+00 2004-02-11T00:28:52.750 A 2.62e+01 1.46e+00 2004-02-11T00:28:52.750 A 2.70e+01 1.36e+00 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 2.19e+01 1.11e+00 2004-02-11T00:28:52.750 A 2.02e+01 1.04e+00 2004-02-11T00:28:52.750 A 2.11e+01 1.04e+00 2004-02-11T00:28:52.750 A A 1 3.92 1.8 1.8 -45.0 2.29e+01 1.32e+00 2004-02-11T00:28:52.750 A 2.04e+01 1.21e+00 2004-02-11T00:28:52.750 A 2.21e+01 1.17e+00 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 5.03e+01 5.23e+00 2004-09-18T16:14:42.183 A 4.81e+01 4.75e+00 2004-09-18T22:34:28.703 A 4.98e+01 4.81e+00 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034427.2+322029 056.1134240 7.99e-07 +32.3413310 7.99e-07 A A 03442721+3220288 -1.53 -1.19e+00 5.28e-02 1.03e+01 6 YSOc_star+dust(IR2) 6.98 1.42 11.69 0.1660 0.34 4 5.09e+00 1.03e-01 1998-10-05T21:28:57 A 6.58e+00 1.33e-01 1998-10-05T21:28:57 A 6.27e+00 1.33e-01 1998-10-05T21:28:57 A 4.28e+00 2.18e-01 2004-02-11T00:28:52.750 A 4.32e+00 2.23e-01 2004-02-11T00:28:52.750 A 3.95e+00 2.30e-01 2004-02-11T00:28:52.750 A A 1 3.09 1.5 1.5 -45.0 3.49e+00 1.73e-01 2004-02-11T00:28:52.750 A 3.48e+00 1.75e-01 2004-02-11T00:28:52.750 A 3.52e+00 1.72e-01 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 2.95e+00 1.70e-01 2004-02-11T00:28:52.750 A 2.90e+00 1.63e-01 2004-02-11T00:28:52.750 A 2.94e+00 1.56e-01 2004-02-11T00:28:52.750 A A 1 3.92 1.8 1.8 -45.0 2.94e+00 1.73e-01 2004-02-11T00:28:52.750 A 3.17e+00 1.80e-01 2004-02-11T00:28:52.750 A 3.08e+00 1.61e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 3.87e+00 4.99e-01 2004-09-18T16:14:42.183 A 4.16e+00 5.19e-01 2004-09-18T22:34:28.703 A 4.11e+00 4.45e-01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034427.3+321421 056.1135856 6.65e-07 +32.2391527 6.65e-07 A A 03442724+3214209 -5.00 -9.00e-01 5.03e-02 1.38e+01 6 YSOc_star+dust(IR2) 8.14 1.43 10.51 0.1670 0.13 4 1.06e+01 1.85e-01 1998-10-05T21:29:05 A 1.65e+01 2.89e-01 1998-10-05T21:29:05 A 1.73e+01 2.88e-01 1998-10-05T21:29:05 A 1.25e+01 6.17e-01 2004-02-11T00:28:52.750 A 1.22e+01 6.33e-01 2004-02-11T00:28:52.750 A 1.08e+01 6.48e-01 2004-02-11T00:28:52.750 A A 1 3.09 1.5 1.5 -45.0 1.11e+01 5.51e-01 2004-02-11T00:28:52.750 A 1.11e+01 5.54e-01 2004-02-11T00:28:52.750 A 1.11e+01 5.42e-01 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 9.74e+00 4.76e-01 2004-02-11T00:28:52.750 A 9.93e+00 5.01e-01 2004-02-11T00:28:52.750 A 9.83e+00 4.76e-01 2004-02-11T00:28:52.750 A A 1 3.92 1.8 1.8 -45.0 1.02e+01 5.66e-01 2004-02-11T00:28:52.750 A 1.12e+01 6.19e-01 2004-02-11T00:28:52.750 A 1.08e+01 5.56e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 2.05e+01 2.01e+00 2004-09-18T16:14:42.183 A 2.01e+01 2.15e+00 2004-09-18T22:34:28.703 A 2.06e+01 1.99e+00 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034427.3+321037 056.1135958 1.56e-08 +32.1770364 1.56e-08 A A 03442724+3210373 -5.00 -1.33e+00 7.16e-02 9.74e+00 6 YSOc_star+dust(IR2) 9.70 1.42 10.81 0.1640 0.61 4 6.32e+00 1.40e-01 1998-10-05T21:29:05 A 9.62e+00 2.04e-01 1998-10-05T21:29:05 A 1.07e+01 1.97e-01 1998-10-05T21:29:05 A 8.50e+00 4.35e-01 2004-02-11T00:28:52.750 A 7.71e+00 4.05e-01 2004-02-11T00:28:52.750 A 7.57e+00 4.29e-01 2004-02-11T00:28:52.750 A A 1 3.09 1.5 1.5 -45.0 7.33e+00 3.59e-01 2004-02-11T00:28:52.750 A 6.48e+00 3.25e-01 2004-02-11T00:28:52.750 A 6.75e+00 3.43e-01 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 5.67e+00 4.40e-01 2004-02-11T00:28:52.750 A 4.91e+00 2.86e-01 2004-02-11T00:28:52.750 A 5.16e+00 3.42e-01 2004-02-11T00:28:52.750 A A 1 3.92 1.8 1.8 -45.0 5.49e+00 4.00e-01 2004-02-11T00:28:52.750 A 5.20e+00 3.90e-01 2004-02-11T00:28:52.750 A 5.26e+00 3.45e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 1.31e+01 3.12e+00 2004-09-18T19:24:35.443 C Q -2 8.04 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034427.9+322719 056.1162495 7.09e-07 +32.4552537 7.09e-07 A A 03442789+3227189 -5.00 -9.30e-01 4.87e-02 9.12e+00 6 YSOc_star+dust(IR2) 11.37 1.41 9.73 0.1630 1.31 4 1.24e+01 2.18e-01 1998-10-05T21:28:48 A 2.02e+01 3.54e-01 1998-10-05T21:28:48 A 2.29e+01 3.80e-01 1998-10-05T21:28:48 A -9.99e+02 -9.99e+02 null N 1.85e+01 1.03e+00 2004-02-11T00:28:52.750 A 1.88e+01 1.04e+00 2004-02-11T00:28:52.750 A Q 1 3.09 1.5 1.5 -45.0 1.86e+01 9.06e-01 2004-02-11T00:28:52.750 A 2.01e+01 1.10e+00 2004-02-11T00:28:52.750 A 1.90e+01 9.26e-01 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 -9.99e+02 -9.99e+02 null N 1.45e+01 7.41e-01 2004-02-11T00:28:52.750 A 1.45e+01 7.42e-01 2004-02-11T00:28:52.750 A Q 1 3.92 1.8 1.8 -45.0 1.93e+01 9.40e-01 2004-02-11T00:28:52.750 A 2.03e+01 1.20e+00 2004-02-11T00:28:52.750 A 1.96e+01 9.54e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 2.61e+01 2.43e+00 2004-09-18T16:14:42.183 A 2.60e+01 2.42e+00 2004-09-18T22:34:28.703 A 2.62e+01 2.43e+00 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034428.1+321600 056.1172346 8.01e-07 +32.2667361 8.01e-07 A A 03442812+3216002 -5.00 -2.24e+00 6.38e-02 2.29e+00 6 YSOc_star+dust(MP1) 5.41 1.10 10.40 0.0999 0.94 7 2.07e+01 3.63e-01 1998-10-05T21:28:57 A 2.96e+01 4.64e-01 1998-10-05T21:28:57 A 2.62e+01 4.34e-01 1998-10-05T21:28:57 A 1.30e+01 6.85e-01 2004-02-11T00:28:52.750 A 1.35e+01 6.98e-01 2004-02-11T00:28:52.750 A 1.20e+01 7.35e-01 2004-02-11T00:28:52.750 A A 1 3.09 1.5 1.5 -45.0 9.14e+00 4.51e-01 2004-02-11T00:28:52.750 A 9.43e+00 4.64e-01 2004-02-11T00:28:52.750 A 9.06e+00 4.67e-01 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 6.40e+00 3.38e-01 2004-02-11T00:28:52.750 A 6.78e+00 3.58e-01 2004-02-11T00:28:52.750 A 6.65e+00 3.31e-01 2004-02-11T00:28:52.750 A A 1 3.92 1.8 1.8 -45.0 4.05e+00 2.83e-01 2004-02-11T00:28:52.750 A 4.25e+00 2.79e-01 2004-02-11T00:28:52.750 A 4.20e+00 2.48e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U 1.85e+00 4.29e-01 2004-09-18T22:34:28.703 C 1.70e+00 2.88e-01 2004-09-18T19:24:35.443 B Q 7 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034428.5+315954 056.1187997 4.09e-07 +31.9983281 4.09e-07 A A 03442851+3159539 -5.00 -1.32e+00 4.76e-02 1.12e+01 6 YSOc_star+dust(IR3) 7.90 1.22 9.75 0.1240 1.31 5 2.53e+01 4.66e-01 1998-10-05T21:29:14 A 3.61e+01 6.32e-01 1998-10-05T21:29:14 A 3.28e+01 5.14e-01 1998-10-05T21:29:14 A 2.25e+01 1.14e+00 2004-02-11T00:28:52.750 A 2.11e+01 1.06e+00 2004-02-11T00:28:52.750 A 2.02e+01 9.78e-01 2004-02-11T00:28:52.750 A A 1 1.77 1.5 1.5 -45.0 1.66e+01 7.99e-01 2004-02-11T00:28:52.750 A 1.65e+01 8.05e-01 2004-02-11T00:28:52.750 A 1.65e+01 7.76e-01 2004-02-11T00:28:52.750 A A 1 2.27 1.7 1.7 -45.0 1.37e+01 6.74e-01 2004-02-11T00:28:52.750 A 1.36e+01 6.67e-01 2004-02-11T00:28:52.750 A 1.39e+01 6.54e-01 2004-02-11T00:28:52.750 A A 1 2.54 1.8 1.8 -45.0 1.33e+01 6.40e-01 2004-02-11T00:28:52.750 A 1.30e+01 6.36e-01 2004-02-11T00:28:52.750 A 1.37e+01 6.39e-01 2004-02-11T00:28:52.750 A A 1 2.54 1.8 1.8 -45.0 1.52e+01 1.42e+00 2004-09-18T16:14:42.183 A 1.50e+01 1.43e+00 2004-09-18T22:34:28.703 A 1.52e+01 1.42e+00 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034428.9+320138 056.1206233 5.28e-07 +32.0271834 5.28e-07 A A 03442894+3201378 -5.00 -5.20e-01 4.80e-02 1.11e+01 6 YSOc_star+dust(IR2) 20.00 1.40 9.95 0.1580 1.17 4 1.17e+00 4.94e-02 1998-10-05T21:29:14 A 4.45e+00 1.19e-01 1998-10-05T21:29:14 A 7.99e+00 1.77e-01 1998-10-05T21:29:14 A 9.80e+00 4.77e-01 2004-02-11T00:28:52.750 A 1.07e+01 5.83e-01 2004-02-11T00:28:52.750 A 8.92e+00 4.29e-01 2004-02-11T00:28:52.750 A A 1 1.77 1.5 1.5 -45.0 9.28e+00 4.49e-01 2004-02-11T00:28:52.750 A 1.04e+01 5.27e-01 2004-02-11T00:28:52.750 A 9.63e+00 4.80e-01 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 8.51e+00 4.12e-01 2004-02-11T00:28:52.750 A 9.44e+00 4.67e-01 2004-02-11T00:28:52.750 A 8.80e+00 4.14e-01 2004-02-11T00:28:52.750 A A 1 2.54 1.8 1.8 -45.0 9.81e+00 4.80e-01 2004-02-11T00:28:52.750 A 1.08e+01 5.53e-01 2004-02-11T00:28:52.750 A 1.03e+01 5.05e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 3.10e+01 2.89e+00 2004-09-18T16:14:42.183 A 3.12e+01 2.91e+00 2004-09-18T22:34:28.703 A 3.11e+01 2.89e+00 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034429.2+320116 056.1218003 5.57e-07 +32.0210333 5.57e-07 A A 03442922+3201157 -5.00 -1.00e+00 4.85e-02 4.86e+01 6 YSOc_star+dust(IR4) 13.55 1.13 10.54 0.1050 0.52 6 2.83e+00 7.56e-02 1998-10-05T21:29:14 A 7.26e+00 1.74e-01 1998-10-05T21:29:14 A 9.55e+00 1.85e-01 1998-10-05T21:29:14 A 6.76e+00 3.22e-01 2004-02-11T00:28:52.750 A 7.02e+00 3.43e-01 2004-02-11T00:28:52.750 A 6.84e+00 3.25e-01 2004-02-11T00:28:52.750 A A 1 1.77 1.5 1.5 -45.0 5.42e+00 2.65e-01 2004-02-11T00:28:52.750 A 5.32e+00 2.70e-01 2004-02-11T00:28:52.750 A 5.39e+00 2.66e-01 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 4.21e+00 2.10e-01 2004-02-11T00:28:52.750 A 4.45e+00 2.27e-01 2004-02-11T00:28:52.750 A 4.33e+00 2.06e-01 2004-02-11T00:28:52.750 A A 1 2.54 1.8 1.8 -45.0 3.82e+00 1.96e-01 2004-02-11T00:28:52.750 A 4.11e+00 2.23e-01 2004-02-11T00:28:52.750 A 3.96e+00 2.02e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 1.38e+01 1.34e+00 2004-09-18T16:14:42.183 A 1.44e+01 1.45e+00 2004-09-18T22:34:28.703 A 1.42e+01 1.35e+00 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034429.7+321040 056.1239117 6.83e-07 +32.1777218 6.83e-07 A A 03442972+3210398 -5.00 -6.80e-01 4.98e-02 4.03e+01 6 YSOc_star+dust(IR2) 9.71 1.42 9.03 0.1660 0.10 4 2.69e+01 5.21e-01 1998-10-05T21:29:05 A 5.06e+01 8.39e-01 1998-10-05T21:29:05 A 5.86e+01 1.08e+00 1998-10-05T21:29:05 A 3.94e+01 1.94e+00 2004-02-11T00:28:52.750 A 4.12e+01 2.19e+00 2004-02-11T00:28:52.750 A 3.78e+01 2.21e+00 2004-02-11T00:28:52.750 A A 1 3.09 1.5 1.5 -45.0 3.44e+01 1.75e+00 2004-02-11T00:28:52.750 A 3.80e+01 1.95e+00 2004-02-11T00:28:52.750 A 3.59e+01 1.83e+00 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 2.82e+01 1.41e+00 2004-02-11T00:28:52.750 A 3.13e+01 1.57e+00 2004-02-11T00:28:52.750 A 2.98e+01 1.45e+00 2004-02-11T00:28:52.750 A A 1 3.92 1.8 1.8 -45.0 3.21e+01 1.72e+00 2004-02-11T00:28:52.750 A 3.52e+01 1.91e+00 2004-02-11T00:28:52.750 A 3.45e+01 1.75e+00 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 1.33e+02 1.32e+01 2004-09-18T16:14:42.183 A 1.37e+02 1.33e+01 2004-09-18T22:34:28.703 A 1.38e+02 1.31e+01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034429.8+320055 056.1241750 4.82e-07 +32.0151589 4.82e-07 A A 03442980+3200545 -5.00 -8.80e-01 4.83e-02 4.46e+00 6 YSOc_star+dust(IR2) 10.68 1.39 10.94 0.1580 2.14 4 5.44e+00 1.10e-01 1998-10-05T21:29:14 A 7.30e+00 1.41e-01 1998-10-05T21:29:14 A 7.82e+00 1.66e-01 1998-10-05T21:29:14 A 6.56e+00 3.25e-01 2004-02-11T00:28:52.750 A 6.70e+00 3.42e-01 2004-02-11T00:28:52.750 A 6.54e+00 3.13e-01 2004-02-11T00:28:52.750 A A 1 1.77 1.5 1.5 -45.0 6.12e+00 3.01e-01 2004-02-11T00:28:52.750 A 5.96e+00 3.01e-01 2004-02-11T00:28:52.750 A 5.98e+00 2.84e-01 2004-02-11T00:28:52.750 A A 1 2.27 1.7 1.7 -45.0 6.20e+00 3.13e-01 2004-02-11T00:28:52.750 A 6.07e+00 3.05e-01 2004-02-11T00:28:52.750 A 6.00e+00 2.85e-01 2004-02-11T00:28:52.750 A A 1 2.54 1.8 1.8 -45.0 7.24e+00 3.55e-01 2004-02-11T00:28:52.750 A 7.05e+00 3.59e-01 2004-02-11T00:28:52.750 A 7.23e+00 3.40e-01 2004-02-11T00:28:52.750 A A 1 2.54 1.8 1.8 -45.0 9.25e+00 9.01e-01 2004-09-18T16:14:42.183 A 9.07e+00 9.44e-01 2004-09-18T22:34:28.703 A 9.22e+00 8.86e-01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034430.0+321923 056.1249295 9.75e-07 +32.3229553 9.75e-07 A A 03442997+3219227 -2.07 -2.19e+00 5.86e-02 8.81e+00 6 YSOc_star+dust(MP1) 6.98 1.10 10.33 0.1010 0.68 7 1.68e+01 2.95e-01 1998-10-05T21:28:57 A 2.43e+01 4.26e-01 1998-10-05T21:28:57 A 2.25e+01 3.31e-01 1998-10-05T21:28:57 A -9.99e+02 -9.99e+02 null U 1.22e+01 6.71e-01 2004-02-11T00:28:52.750 A 1.19e+01 7.81e-01 2004-02-11T00:28:52.750 A Q 1 3.09 1.5 1.5 -45.0 9.98e+00 5.02e-01 2004-02-11T00:28:52.750 K 1.01e+01 5.32e-01 2004-02-11T00:28:52.750 K 9.83e+00 4.89e-01 2004-02-11T00:28:52.750 K A 7 2.27 1.7 1.7 -45.0 7.31e+00 3.80e-01 2004-02-11T00:28:52.750 K 8.50e+00 6.62e-01 2004-02-11T00:28:52.750 K 6.35e+00 3.65e-01 2004-02-11T00:28:52.750 A A 1 2.54 1.8 1.8 -45.0 3.57e+00 2.13e-01 2004-02-11T00:28:52.750 A 3.98e+00 2.46e-01 2004-02-11T00:28:52.750 A 3.67e+00 1.96e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 2.31e+00 4.03e-01 2004-09-18T16:14:42.183 B 1.90e+00 3.08e-01 2004-09-18T22:34:28.703 B 2.10e+00 2.84e-01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034430.1+320118 056.1255641 6.27e-07 +32.0217348 6.27e-07 A A 03443013+3201182 -1.65 -1.48e+00 5.50e-02 4.27e+00 6 YSOc_star+dust(IR4) 14.98 1.13 10.62 0.1050 0.57 6 1.69e+00 5.77e-02 1998-10-05T21:29:14 A 5.69e+00 1.26e-01 1998-10-05T21:29:14 A 7.48e+00 1.58e-01 1998-10-05T21:29:14 A 5.79e+00 2.82e-01 2004-02-11T00:28:52.750 A 5.81e+00 2.95e-01 2004-02-11T00:28:52.750 A 5.85e+00 2.79e-01 2004-02-11T00:28:52.750 A A 1 1.77 1.5 1.5 -45.0 4.67e+00 2.31e-01 2004-02-11T00:28:52.750 A 4.41e+00 2.31e-01 2004-02-11T00:28:52.750 A 4.52e+00 2.28e-01 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 3.69e+00 1.97e-01 2004-02-11T00:28:52.750 A 3.75e+00 1.99e-01 2004-02-11T00:28:52.750 A 3.92e+00 1.89e-01 2004-02-11T00:28:52.750 A A 1 2.54 1.8 1.8 -45.0 3.42e+00 1.86e-01 2004-02-11T00:28:52.750 A 3.24e+00 2.13e-01 2004-02-11T00:28:52.750 A 3.40e+00 1.85e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 3.09e+00 3.89e-01 2004-09-18T19:24:35.443 A Q 7 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034430.3+321135 056.1262421 1.10e-06 +32.1931044 1.10e-06 A A 03443030+3211353 -2.24 2.00e-01 6.10e-02 4.37e+00 6 YSOc_star+dust(IR1) 13.69 2.63 13.05 0.3940 0.91 3 2.66e-01 4.11e-02 1998-10-05T21:29:05 C 6.41e-01 6.50e-02 1998-10-05T21:29:05 B 1.01e+00 7.20e-02 1998-10-05T21:29:05 A 1.65e+00 8.78e-02 2004-02-11T00:28:52.750 A 2.90e+00 1.57e-01 2004-02-11T00:28:52.750 A 2.25e+00 1.31e-01 2004-02-11T00:28:52.750 A D 1 3.09 1.5 1.5 -45.0 2.42e+00 1.27e-01 2004-02-11T00:28:52.750 A 4.42e+00 2.25e-01 2004-02-11T00:28:52.750 A 2.98e+00 1.70e-01 2004-02-11T00:28:52.750 A D 1 2.97 1.7 1.7 -45.0 3.26e+00 2.43e-01 2004-02-11T00:28:52.750 A 5.74e+00 3.43e-01 2004-02-11T00:28:52.750 A 4.89e+00 3.04e-01 2004-02-11T00:28:52.750 A C 1 3.92 1.8 1.8 -45.0 5.65e+00 4.91e-01 2004-02-11T00:28:52.750 A 7.61e+00 6.58e-01 2004-02-11T00:28:52.750 A 6.42e+00 4.92e-01 2004-02-11T00:28:52.750 A B 1 4.98 1.8 1.8 -45.0 1.66e+01 2.27e+00 2004-09-18T16:14:42.183 A 1.69e+01 3.35e+00 2004-09-18T22:34:28.703 B 1.70e+01 2.08e+00 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034430.8+320956 056.1284808 1.82e-08 +32.1654746 1.82e-08 A A 03443081+3209558 -5.00 -1.11e+00 6.78e-02 4.30e+01 6 YSOc_star+dust(IR3) 1.60 1.23 9.19 0.1260 0.35 5 1.86e+02 3.42e+00 1998-10-05T21:29:05 A 1.51e+02 2.64e+00 1998-10-05T21:29:05 A 1.12e+02 2.07e+00 1998-10-05T21:29:05 A 5.08e+01 2.55e+00 2004-02-11T00:28:52.750 A 5.17e+01 2.86e+00 2004-02-11T00:28:52.750 A 5.12e+01 2.71e+00 2004-02-11T00:28:52.750 A A 1 3.09 1.5 1.5 -45.0 3.73e+01 1.83e+00 2004-02-11T00:28:52.750 A 3.50e+01 1.83e+00 2004-02-11T00:28:52.750 A 3.53e+01 1.80e+00 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 4.13e+01 2.10e+00 2004-02-11T00:28:52.750 A 4.11e+01 2.06e+00 2004-02-11T00:28:52.750 A 4.12e+01 2.04e+00 2004-02-11T00:28:52.750 A A 1 3.92 1.8 1.8 -45.0 7.30e+01 4.22e+00 2004-02-11T00:28:52.750 A 7.29e+01 4.34e+00 2004-02-11T00:28:52.750 A 7.41e+01 4.04e+00 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 2.58e+01 6.71e+00 2004-09-18T19:24:35.443 C Q -2 8.04 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034431.1+321848 056.1297259 6.81e-07 +32.3134721 6.81e-07 A A 03443112+3218484 -5.00 -5.70e-01 4.89e-02 4.63e+00 6 YSOc_star+dust(IR2) 15.97 1.41 10.78 0.1630 1.95 4 1.64e+00 5.14e-02 1998-10-05T21:28:57 A 3.78e+00 1.01e-01 1998-10-05T21:28:57 A 5.34e+00 1.23e-01 1998-10-05T21:28:57 A 5.24e+00 2.65e-01 2004-02-11T00:28:52.750 A 6.32e+00 3.19e-01 2004-02-11T00:28:52.750 A 5.44e+00 2.95e-01 2004-02-11T00:28:52.750 A B 1 3.09 1.5 1.5 -45.0 5.58e+00 2.74e-01 2004-02-11T00:28:52.750 A 6.09e+00 3.05e-01 2004-02-11T00:28:52.750 A 5.82e+00 2.88e-01 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 5.40e+00 2.81e-01 2004-02-11T00:28:52.750 A 6.04e+00 3.09e-01 2004-02-11T00:28:52.750 A 5.67e+00 2.84e-01 2004-02-11T00:28:52.750 A A 1 3.92 1.8 1.8 -45.0 7.03e+00 3.55e-01 2004-02-11T00:28:52.750 A 6.66e+00 3.54e-01 2004-02-11T00:28:52.750 A 6.95e+00 3.39e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 1.57e+01 1.49e+00 2004-09-18T16:14:42.183 A 1.46e+01 1.38e+00 2004-09-18T22:34:28.703 A 1.52e+01 1.42e+00 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034431.2+320559 056.1299545 5.94e-07 +32.0996962 5.94e-07 A A null -5.00 -6.40e-01 5.35e-02 8.59e+00 5 YSOc_star+dust(IR4) 45.71 6.40 7.79 0.3710 0.00 3 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 1.29e+01 6.48e-01 2004-02-11T00:28:52.750 A 1.13e+01 5.91e-01 2004-02-11T00:28:52.750 A 1.28e+01 6.10e-01 2004-02-11T00:28:52.750 A A 1 1.77 1.5 1.5 -45.0 1.42e+01 7.14e-01 2004-02-11T00:28:52.750 A 1.28e+01 6.57e-01 2004-02-11T00:28:52.750 A 1.32e+01 6.83e-01 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 1.32e+01 6.74e-01 2004-02-11T00:28:52.750 A 1.12e+01 5.86e-01 2004-02-11T00:28:52.750 A 1.34e+01 6.36e-01 2004-02-11T00:28:52.750 A B 1 2.54 1.8 1.8 -45.0 1.37e+01 7.00e-01 2004-02-11T00:28:52.750 A 1.20e+01 6.50e-01 2004-02-11T00:28:52.750 A 1.29e+01 6.42e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 3.04e+01 2.87e+00 2004-09-18T16:14:42.183 A 3.09e+01 2.89e+00 2004-09-18T22:34:28.703 A 3.12e+01 2.90e+00 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034431.4+320014 056.1306911 1.38e-06 +32.0039424 1.38e-06 A A 03443137+3200140 -5.00 -7.90e-01 4.92e-02 2.73e+00 6 YSOc_star+dust(IR2) 17.46 1.42 8.65 0.1650 0.16 4 5.66e+00 1.46e-01 1998-10-05T21:29:14 A 2.25e+01 4.76e-01 1998-10-05T21:29:14 A 3.71e+01 7.86e-01 1998-10-05T21:29:14 A 3.88e+01 2.26e+00 2004-02-11T00:28:52.750 A 3.54e+01 2.41e+00 2004-02-11T00:28:52.750 A 3.36e+01 1.92e+00 2004-02-11T00:28:52.750 A A 1 1.77 1.5 1.5 -45.0 3.28e+01 2.64e+00 2004-02-11T00:28:52.750 A 3.33e+01 2.41e+00 2004-02-11T00:28:52.750 A 3.54e+01 1.98e+00 2004-02-11T00:28:52.750 A A 1 2.27 1.7 1.7 -45.0 3.17e+01 1.85e+00 2004-02-11T00:28:52.750 A 3.24e+01 2.08e+00 2004-02-11T00:28:52.750 A 3.30e+01 1.71e+00 2004-02-11T00:28:52.750 A A 1 2.54 1.8 1.8 -45.0 3.64e+01 2.13e+00 2004-02-11T00:28:52.750 A 3.86e+01 2.16e+00 2004-02-11T00:28:52.750 A 3.93e+01 1.95e+00 2004-02-11T00:28:52.750 A A 1 2.54 1.8 1.8 -45.0 5.77e+01 5.35e+00 2004-09-18T16:14:42.183 A 5.95e+01 5.53e+00 2004-09-18T22:34:28.703 A 5.82e+01 5.39e+00 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034431.5+320845 056.1314511 3.16e-08 +32.1458136 3.16e-08 A A 03443153+3208449 -5.00 -2.36e+00 7.23e-02 7.22e+00 6 YSOc_star+dust(MP1) 3.76 1.10 9.28 0.0994 0.36 7 8.44e+01 1.55e+00 1998-10-05T21:29:05 A 1.05e+02 1.85e+00 1998-10-05T21:29:05 A 8.59e+01 1.42e+00 1998-10-05T21:29:05 A 4.21e+01 2.11e+00 2004-02-11T00:28:52.750 A 4.28e+01 2.15e+00 2004-02-11T00:28:52.750 A 3.95e+01 2.24e+00 2004-02-11T00:28:52.750 A A 1 3.09 1.5 1.5 -45.0 2.77e+01 1.36e+00 2004-02-11T00:28:52.750 A 2.77e+01 1.41e+00 2004-02-11T00:28:52.750 A 2.76e+01 1.39e+00 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 1.93e+01 9.71e-01 2004-02-11T00:28:52.750 A 1.91e+01 9.65e-01 2004-02-11T00:28:52.750 A 1.92e+01 9.39e-01 2004-02-11T00:28:52.750 A A 1 3.92 1.8 1.8 -45.0 1.19e+01 8.16e-01 2004-02-11T00:28:52.750 A 1.20e+01 8.46e-01 2004-02-11T00:28:52.750 A 1.18e+01 7.30e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 1.23e+01 3.63e+00 2004-09-18T19:24:35.443 C Q -2 8.04 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034432.0+321144 056.1334994 2.65e-06 +32.1954765 2.65e-06 A A 03443200+3211439 -5.00 -2.60e-01 5.16e-02 8.83e+01 6 YSOc_PAH-em -999.00 -999.00 -999.00 -999.0000 -999.00 0 1.49e+02 3.03e+00 1998-10-05T21:29:05 A 1.84e+02 3.56e+00 1998-10-05T21:29:05 A 1.80e+02 3.48e+00 1998-10-05T21:29:05 A 1.32e+02 8.87e+00 2004-02-11T00:28:52.750 A 1.08e+02 9.95e+00 2004-02-11T00:28:52.750 A 1.38e+02 8.83e+00 2004-02-11T00:28:52.750 A A 2 5.21 2.3 2.0 -57.3 1.07e+02 7.18e+00 2004-02-11T00:28:52.750 A 1.02e+02 7.60e+00 2004-02-11T00:28:52.750 A 1.08e+02 6.85e+00 2004-02-11T00:28:52.750 A A 2 6.52 2.5 2.3 -88.8 2.54e+02 1.55e+01 2004-02-11T00:28:52.750 A 2.38e+02 1.64e+01 2004-02-11T00:28:52.750 A 2.61e+02 1.52e+01 2004-02-11T00:28:52.750 A A 2 11.97 3.3 3.2 -70.2 7.13e+02 5.26e+01 2004-02-11T00:28:52.750 A 7.79e+02 4.36e+01 2004-02-11T00:28:52.750 A 7.66e+02 4.93e+01 2004-02-11T00:28:52.750 A A 7 4.98 1.8 1.8 -45.0 3.58e+02 3.41e+01 2004-09-18T16:14:42.183 A 3.71e+02 3.58e+01 2004-09-18T22:34:28.703 A 3.68e+02 3.46e+01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034432.6+320856 056.1358022 5.50e-09 +32.1488161 5.50e-09 A A 03443257+3208558 -5.00 -1.68e+00 6.92e-02 7.10e+00 6 YSOc_star+dust(IR4) 7.90 1.13 9.78 0.1060 1.31 6 2.29e+01 4.22e-01 1998-10-05T21:29:05 A 3.62e+01 6.00e-01 1998-10-05T21:29:05 A 3.32e+01 5.50e-01 1998-10-05T21:29:05 A 1.93e+01 9.75e-01 2004-02-11T00:28:52.750 A 2.01e+01 1.03e+00 2004-02-11T00:28:52.750 A 1.85e+01 1.04e+00 2004-02-11T00:28:52.750 A A 1 3.09 1.5 1.5 -45.0 1.43e+01 6.95e-01 2004-02-11T00:28:52.750 A 1.42e+01 7.59e-01 2004-02-11T00:28:52.750 A 1.44e+01 7.07e-01 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 1.16e+01 6.09e-01 2004-02-11T00:28:52.750 A 1.22e+01 6.45e-01 2004-02-11T00:28:52.750 A 1.19e+01 5.99e-01 2004-02-11T00:28:52.750 A A 1 3.92 1.8 1.8 -45.0 1.01e+01 6.55e-01 2004-02-11T00:28:52.750 A 1.07e+01 7.14e-01 2004-02-11T00:28:52.750 A 1.06e+01 6.10e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 1.41e+01 3.62e+00 2004-09-18T19:24:35.443 C Q -2 8.04 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034433.2+321257 056.1384261 9.46e-07 +32.2159515 9.46e-07 A A 03443321+3212574 -1.72 -9.70e-01 5.68e-02 9.64e+00 6 YSOc_star+dust(IR4) 16.57 1.14 10.94 0.1070 1.59 6 1.18e+00 5.33e-02 1998-10-05T21:29:05 A 3.19e+00 1.06e-01 1998-10-05T21:29:05 A 4.42e+00 1.26e-01 1998-10-05T21:29:05 A 3.85e+00 1.96e-01 2004-02-11T00:28:52.750 A 3.76e+00 2.04e-01 2004-02-11T00:28:52.750 A 3.83e+00 2.00e-01 2004-02-11T00:28:52.750 A A 1 3.09 1.5 1.5 -45.0 3.19e+00 1.61e-01 2004-02-11T00:28:52.750 A 3.22e+00 1.60e-01 2004-02-11T00:28:52.750 A 3.21e+00 1.60e-01 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 2.81e+00 1.80e-01 2004-02-11T00:28:52.750 A 3.02e+00 1.88e-01 2004-02-11T00:28:52.750 A 2.88e+00 1.63e-01 2004-02-11T00:28:52.750 A A 1 3.92 1.8 1.8 -45.0 3.08e+00 2.80e-01 2004-02-11T00:28:52.750 A 3.13e+00 2.91e-01 2004-02-11T00:28:52.750 A 3.14e+00 2.37e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 5.68e+00 7.28e-01 2004-09-18T16:14:42.183 A 5.62e+00 1.00e+00 2004-09-18T22:34:28.703 B 5.70e+00 6.67e-01 2004-09-18T19:24:35.443 A A 7 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034433.8+315830 056.1408002 4.88e-07 +31.9750603 4.88e-07 A A 03443379+3158302 -5.00 -9.50e-01 4.78e-02 3.36e+01 6 YSOc_star+dust(IR4) 12.62 1.12 10.10 0.1050 1.93 6 6.42e+00 1.30e-01 1998-10-05T21:29:23 A 1.27e+01 2.45e-01 1998-10-05T21:29:23 A 1.51e+01 2.92e-01 1998-10-05T21:29:23 A 1.08e+01 5.30e-01 2004-02-11T00:28:52.750 A 1.01e+01 5.33e-01 2004-02-11T00:28:52.750 A 1.01e+01 4.98e-01 2004-02-11T00:28:52.750 A A 1 1.77 1.5 1.5 -45.0 9.72e+00 4.78e-01 2004-02-11T00:28:52.750 A 8.88e+00 4.82e-01 2004-02-11T00:28:52.750 A 8.93e+00 4.24e-01 2004-02-11T00:28:52.750 A A 1 2.27 1.7 1.7 -45.0 7.13e+00 3.54e-01 2004-02-11T00:28:52.750 A 6.59e+00 3.30e-01 2004-02-11T00:28:52.750 A 7.27e+00 3.43e-01 2004-02-11T00:28:52.750 A A 1 2.54 1.8 1.8 -45.0 7.64e+00 3.76e-01 2004-02-11T00:28:52.750 A 7.28e+00 3.70e-01 2004-02-11T00:28:52.750 A 7.36e+00 3.46e-01 2004-02-11T00:28:52.750 A A 1 2.54 1.8 1.8 -45.0 2.07e+01 1.93e+00 2004-09-18T16:14:42.183 A 2.12e+01 1.98e+00 2004-09-18T22:34:28.703 A 2.09e+01 1.94e+00 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034434.1+320657 056.1418770 1.79e-08 +32.1158454 1.79e-08 A A null -5.00 -8.10e-01 7.94e-02 2.18e+00 5 YSOc_star+dust(IR4) 41.29 6.44 9.20 0.3740 0.16 3 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 4.70e+00 2.41e-01 2004-02-11T00:28:52.750 A 4.59e+00 2.53e-01 2004-02-11T00:28:52.750 A 4.61e+00 2.24e-01 2004-02-11T00:28:52.750 A A 1 1.77 1.5 1.5 -45.0 4.30e+00 2.18e-01 2004-02-11T00:28:52.750 A 4.35e+00 2.21e-01 2004-02-11T00:28:52.750 A 4.30e+00 2.16e-01 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 4.45e+00 2.81e-01 2004-02-11T00:28:52.750 A 4.64e+00 2.91e-01 2004-02-11T00:28:52.750 A 4.46e+00 2.22e-01 2004-02-11T00:28:52.750 A A 1 2.54 1.8 1.8 -45.0 5.33e+00 3.52e-01 2004-02-11T00:28:52.750 A 5.78e+00 3.95e-01 2004-02-11T00:28:52.750 A 5.54e+00 3.23e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 5.64e+00 1.28e+00 2004-09-18T19:24:35.443 C Q -2 8.04 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034434.1+321636 056.1422457 8.26e-07 +32.2765965 8.26e-07 A A 03443412+3216357 -5.00 -9.60e-01 5.24e-02 9.05e+00 6 YSOc_star+dust(IR2) 11.02 1.41 11.27 0.1640 1.25 4 3.29e+00 7.87e-02 1998-10-05T21:28:57 A 5.19e+00 1.15e-01 1998-10-05T21:28:57 A 5.82e+00 1.18e-01 1998-10-05T21:28:57 A 4.75e+00 2.61e-01 2004-02-11T00:28:52.750 A 4.88e+00 2.45e-01 2004-02-11T00:28:52.750 A 4.68e+00 2.60e-01 2004-02-11T00:28:52.750 A A 1 3.09 1.5 1.5 -45.0 4.28e+00 2.12e-01 2004-02-11T00:28:52.750 A 4.57e+00 2.23e-01 2004-02-11T00:28:52.750 A 4.35e+00 2.16e-01 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 3.70e+00 2.13e-01 2004-02-11T00:28:52.750 A 3.82e+00 2.19e-01 2004-02-11T00:28:52.750 A 3.78e+00 2.00e-01 2004-02-11T00:28:52.750 A A 1 3.92 1.8 1.8 -45.0 3.68e+00 2.24e-01 2004-02-11T00:28:52.750 A 4.60e+00 2.61e-01 2004-02-11T00:28:52.750 A 4.12e+00 2.19e-01 2004-02-11T00:28:52.750 A B 1 4.98 1.8 1.8 -45.0 7.68e+00 9.95e-01 2004-09-18T16:14:42.183 A 7.02e+00 7.96e-01 2004-09-18T22:34:28.703 A 7.24e+00 7.74e-01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034434.2+320946 056.1425455 3.31e-08 +32.1628619 3.31e-08 A A 03443419+3209462 -5.00 -2.03e+00 6.09e-02 4.73e+01 6 YSOc_star+dust(MP1) -0.19 1.10 6.55 0.1020 0.53 7 3.07e+03 5.37e+01 1998-10-05T21:29:05 A 2.26e+03 4.37e+01 1998-10-05T21:29:05 A 1.65e+03 2.59e+01 1998-10-05T21:29:05 A 6.87e+02 3.78e+01 2004-02-11T00:28:52.750 A 6.87e+02 4.26e+01 2004-02-11T00:28:52.750 A 6.21e+02 4.46e+01 2004-02-11T00:28:52.750 A A 9 3.09 1.5 1.5 -45.0 4.28e+02 2.41e+01 2004-02-11T00:28:52.750 A 3.43e+02 2.75e+01 2004-02-11T00:28:52.750 A 3.81e+02 2.40e+01 2004-02-11T00:28:52.750 A B 1 2.97 1.7 1.7 -45.0 2.97e+02 1.44e+01 2004-02-11T00:28:52.750 A 3.04e+02 1.54e+01 2004-02-11T00:28:52.750 A 3.04e+02 1.60e+01 2004-02-11T00:28:52.750 A A 2 9.84 3.1 2.8 -74.5 1.75e+02 8.79e+00 2004-02-11T00:28:52.750 A 1.30e+02 9.75e+00 2004-02-11T00:28:52.750 A 1.67e+02 9.09e+00 2004-02-11T00:28:52.750 A B 2 9.48 3.1 2.7 -45.4 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 3.47e+02 4.96e+01 2004-09-18T19:24:35.443 B Q -2 8.04 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034434.3+321241 056.1428716 7.85e-07 +32.2112970 7.85e-07 A A 03443428+3212407 -5.00 -7.70e-01 5.03e-02 1.14e+00 6 YSOc_star+dust(IR2) 21.86 1.43 9.48 0.1660 1.20 4 1.10e+00 4.58e-02 1998-10-05T21:29:05 A 4.95e+00 1.32e-01 1998-10-05T21:29:05 A 1.15e+01 2.22e-01 1998-10-05T21:29:05 A 1.36e+01 7.32e-01 2004-02-11T00:28:52.750 A 1.23e+01 6.33e-01 2004-02-11T00:28:52.750 A 1.20e+01 7.08e-01 2004-02-11T00:28:52.750 A A 1 3.09 1.5 1.5 -45.0 1.62e+01 8.08e-01 2004-02-11T00:28:52.750 A 1.28e+01 6.64e-01 2004-02-11T00:28:52.750 A 1.40e+01 7.34e-01 2004-02-11T00:28:52.750 A B 1 2.97 1.7 1.7 -45.0 1.38e+01 7.02e-01 2004-02-11T00:28:52.750 A 1.21e+01 6.24e-01 2004-02-11T00:28:52.750 A 1.29e+01 6.39e-01 2004-02-11T00:28:52.750 A A 1 3.92 1.8 1.8 -45.0 1.51e+01 7.66e-01 2004-02-11T00:28:52.750 A 1.38e+01 7.49e-01 2004-02-11T00:28:52.750 A 1.44e+01 7.27e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 2.07e+01 2.08e+00 2004-09-18T16:14:42.183 A 2.10e+01 2.08e+00 2004-09-18T22:34:28.703 A 2.07e+01 2.00e+00 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034434.7+321600 056.1445442 6.43e-07 +32.2666886 6.43e-07 A A 03443468+3216000 -5.00 -9.00e-02 4.89e-02 3.20e+01 6 YSOc_star+dust(IR2) 22.77 1.42 9.44 0.1650 0.92 4 8.81e-01 4.87e-02 1998-10-05T21:28:57 A 4.58e+00 1.10e-01 1998-10-05T21:28:57 A 1.06e+01 2.06e-01 1998-10-05T21:28:57 A 1.21e+01 6.00e-01 2004-02-11T00:28:52.750 A 1.31e+01 6.92e-01 2004-02-11T00:28:52.750 A 1.18e+01 6.70e-01 2004-02-11T00:28:52.750 A A 1 3.09 1.5 1.5 -45.0 1.30e+01 6.59e-01 2004-02-11T00:28:52.750 A 1.50e+01 7.39e-01 2004-02-11T00:28:52.750 A 1.38e+01 6.96e-01 2004-02-11T00:28:52.750 A B 1 2.97 1.7 1.7 -45.0 1.31e+01 6.45e-01 2004-02-11T00:28:52.750 A 1.38e+01 7.01e-01 2004-02-11T00:28:52.750 A 1.36e+01 6.62e-01 2004-02-11T00:28:52.750 A A 1 3.92 1.8 1.8 -45.0 1.47e+01 7.34e-01 2004-02-11T00:28:52.750 A 1.69e+01 8.41e-01 2004-02-11T00:28:52.750 A 1.59e+01 7.72e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 1.27e+02 1.18e+01 2004-09-18T16:14:42.183 A 1.21e+02 1.13e+01 2004-09-18T22:34:28.703 A 1.25e+02 1.16e+01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034434.7+321554 056.1446409 3.08e-08 +32.2651046 3.08e-08 A A 03443470+3215544 -5.00 -1.89e+00 6.59e-02 2.57e+01 6 YSOc_star+dust(MP1) 9.31 1.10 10.50 0.0998 0.37 7 7.44e+00 1.58e-01 1998-10-05T21:28:57 A 1.47e+01 3.53e-01 1998-10-05T21:28:57 A 1.56e+01 4.31e-01 1998-10-05T21:28:57 A 9.73e+00 5.26e-01 2004-02-11T00:28:52.750 A 9.64e+00 5.43e-01 2004-02-11T00:28:52.750 A 9.06e+00 5.40e-01 2004-02-11T00:28:52.750 A A 1 3.09 1.5 1.5 -45.0 6.91e+00 3.49e-01 2004-02-11T00:28:52.750 A 7.35e+00 3.64e-01 2004-02-11T00:28:52.750 A 7.15e+00 3.49e-01 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 4.90e+00 2.70e-01 2004-02-11T00:28:52.750 A 5.15e+00 2.78e-01 2004-02-11T00:28:52.750 A 5.06e+00 2.57e-01 2004-02-11T00:28:52.750 A A 1 3.92 1.8 1.8 -45.0 2.77e+00 2.09e-01 2004-02-11T00:28:52.750 A 2.82e+00 2.09e-01 2004-02-11T00:28:52.750 A 2.82e+00 1.79e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 6.36e+00 1.13e+00 2004-09-18T19:24:35.443 B Q -2 8.04 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034434.8+315655 056.1450304 5.20e-07 +31.9486637 5.20e-07 A A 03443481+3156552 -5.00 -1.11e+00 4.77e-02 6.77e+01 6 YSOc_star+dust(IR4) 10.86 1.12 10.21 0.1050 1.34 6 9.83e+00 1.63e-01 1998-10-05T21:29:23 A 1.49e+01 2.61e-01 1998-10-05T21:29:23 A 1.49e+01 2.89e-01 1998-10-05T21:29:23 A 1.14e+01 5.57e-01 2004-02-11T00:28:52.750 A 1.18e+01 6.07e-01 2004-02-11T00:28:52.750 A 1.11e+01 5.28e-01 2004-02-11T00:28:52.750 A A 1 1.77 1.5 1.5 -45.0 -9.99e+02 -9.99e+02 null N 8.55e+00 4.49e-01 2004-02-11T00:28:52.750 A 8.91e+00 4.23e-01 2004-02-11T00:28:52.750 A Q 1 2.27 1.7 1.7 -45.0 6.88e+00 3.33e-01 2004-02-11T00:28:52.750 A 7.01e+00 3.54e-01 2004-02-11T00:28:52.750 A 6.50e+00 3.12e-01 2004-02-11T00:28:52.750 A A 1 2.54 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null N 5.63e+00 3.90e-01 2004-02-11T00:28:52.750 A 5.07e+00 2.44e-01 2004-02-11T00:28:52.750 A Q 1 2.54 1.8 1.8 -45.0 2.11e+01 1.97e+00 2004-09-18T16:14:42.183 A 2.07e+01 1.94e+00 2004-09-18T22:34:28.703 A 2.14e+01 1.99e+00 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034435.0+321531 056.1458034 7.12e-07 +32.2586483 7.12e-07 A A 03443498+3215311 -5.00 -1.02e+00 5.00e-02 5.81e+00 6 YSOc_star+dust(IR2) 8.85 1.41 10.77 0.1640 0.55 4 7.43e+00 1.44e-01 1998-10-05T21:28:57 A 1.19e+01 2.08e-01 1998-10-05T21:28:57 A 1.16e+01 1.92e-01 1998-10-05T21:28:57 A 7.88e+00 4.15e-01 2004-02-11T00:28:52.750 A 9.18e+00 4.95e-01 2004-02-11T00:28:52.750 A 8.31e+00 4.67e-01 2004-02-11T00:28:52.750 A B 1 3.09 1.5 1.5 -45.0 7.65e+00 3.74e-01 2004-02-11T00:28:52.750 A 8.87e+00 4.41e-01 2004-02-11T00:28:52.750 A 8.30e+00 4.06e-01 2004-02-11T00:28:52.750 A B 1 2.97 1.7 1.7 -45.0 7.27e+00 3.68e-01 2004-02-11T00:28:52.750 A 8.73e+00 4.51e-01 2004-02-11T00:28:52.750 A 7.94e+00 4.03e-01 2004-02-11T00:28:52.750 A B 1 3.92 1.8 1.8 -45.0 9.72e+00 5.07e-01 2004-02-11T00:28:52.750 A 1.08e+01 5.52e-01 2004-02-11T00:28:52.750 A 1.03e+01 5.19e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 7.98e+00 8.92e-01 2004-09-18T16:14:42.183 A 7.75e+00 7.74e-01 2004-09-18T22:34:28.703 A 7.94e+00 7.75e-01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034435.3+322837 056.1472706 1.88e-06 +32.4769952 1.88e-06 A A null -1.48 1.00e-02 6.77e-02 4.46e+01 5 YSOc_red -999.00 -999.00 -999.00 -999.0000 -999.00 0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 2.80e-01 2.09e-02 2004-02-11T00:28:52.750 A 2.87e-01 2.13e-02 2004-02-11T00:28:52.750 A Q 1 3.09 1.5 1.5 -45.0 9.52e-01 4.99e-02 2004-02-11T00:28:52.750 A 1.19e+00 3.54e-01 2004-02-11T00:28:52.750 C 9.71e-01 5.09e-02 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 -9.99e+02 -9.99e+02 null N 5.43e-01 7.55e-02 2004-02-11T00:28:52.750 A 5.79e-01 6.09e-02 2004-02-11T00:28:52.750 A Q 1 3.92 1.8 1.8 -45.0 8.20e-01 7.72e-02 2004-02-11T00:28:52.750 A -9.99e+02 -9.99e+02 null U 8.37e-01 7.84e-02 2004-02-11T00:28:52.750 A Q 1 4.98 1.8 1.8 -45.0 3.33e+00 4.45e-01 2004-09-18T16:14:42.183 A 3.37e+00 4.05e-01 2004-09-18T22:34:28.703 A 3.39e+00 3.75e-01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034435.4+320736 056.1473995 5.90e-07 +32.1267241 5.90e-07 A A 03443537+3207362 -5.00 -8.40e-01 4.81e-02 1.46e+01 6 YSOc_star+dust(IR2) 8.55 1.39 9.42 0.1580 0.02 4 2.35e+01 5.41e-01 1998-10-05T21:29:05 A 4.30e+01 9.11e-01 1998-10-05T21:29:05 A 4.64e+01 9.83e-01 1998-10-05T21:29:05 A 3.57e+01 1.81e+00 2004-02-11T00:28:52.750 A 3.40e+01 1.96e+00 2004-02-11T00:28:52.750 A 2.83e+01 1.36e+00 2004-02-11T00:28:52.750 A A 1 1.77 1.5 1.5 -45.0 3.23e+01 1.61e+00 2004-02-11T00:28:52.750 A 2.83e+01 1.59e+00 2004-02-11T00:28:52.750 A 2.95e+01 1.49e+00 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 2.92e+01 1.43e+00 2004-02-11T00:28:52.750 A 2.64e+01 1.67e+00 2004-02-11T00:28:52.750 A 2.53e+01 1.19e+00 2004-02-11T00:28:52.750 A A 1 2.54 1.8 1.8 -45.0 3.55e+01 1.74e+00 2004-02-11T00:28:52.750 A 3.69e+01 2.03e+00 2004-02-11T00:28:52.750 A 3.73e+01 1.78e+00 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 4.88e+01 4.69e+00 2004-09-18T16:14:42.183 A 4.81e+01 4.62e+00 2004-09-18T22:34:28.703 A 4.87e+01 4.59e+00 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034435.4+321005 056.1474164 9.74e-07 +32.1679345 9.74e-07 A A 03443536+3210045 -5.00 -1.43e+00 5.35e-02 1.10e+01 6 YSOc_star+dust(IR3) 4.72 1.27 6.62 0.1350 1.39 5 1.05e+03 2.23e+01 1998-10-05T21:29:05 A 9.92e+02 1.83e+01 1998-10-05T21:29:05 A 8.42e+02 1.71e+01 1998-10-05T21:29:05 A 4.54e+02 2.33e+01 2004-02-11T00:28:52.750 A 3.69e+02 3.52e+01 2004-02-11T00:28:52.750 A 4.25e+02 2.99e+01 2004-02-11T00:28:52.750 A B 9 3.09 1.5 1.5 -45.0 3.26e+02 1.81e+01 2004-02-11T00:28:52.750 A 2.06e+02 1.95e+01 2004-02-11T00:28:52.750 A 3.51e+02 2.41e+01 2004-02-11T00:28:52.750 K C 7 2.27 1.7 1.7 -45.0 3.04e+02 1.50e+01 2004-02-11T00:28:52.750 A 2.72e+02 1.58e+01 2004-02-11T00:28:52.750 A 2.89e+02 1.51e+01 2004-02-11T00:28:52.750 A A 1 3.92 1.8 1.8 -45.0 3.26e+02 1.66e+01 2004-02-11T00:28:52.750 A 2.77e+02 1.56e+01 2004-02-11T00:28:52.750 A 2.98e+02 1.64e+01 2004-02-11T00:28:52.750 A B 7 4.98 1.8 1.8 -45.0 2.67e+02 2.78e+01 2004-09-18T16:14:42.183 A 2.68e+02 3.10e+01 2004-09-18T22:34:28.703 A 2.65e+02 2.70e+01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034435.5+320856 056.1477869 1.52e-08 +32.1489720 1.52e-08 A A 03443545+3208563 -5.00 -8.90e-01 7.64e-02 1.58e+01 6 YSOc_star+dust(IR2) 10.04 1.41 10.52 0.1630 1.27 4 7.99e+00 1.77e-01 1998-10-05T21:29:05 A 1.22e+01 2.70e-01 1998-10-05T21:29:05 A 1.24e+01 2.74e-01 1998-10-05T21:29:05 A 9.45e+00 5.48e-01 2004-02-11T00:28:52.750 A 9.78e+00 5.43e-01 2004-02-11T00:28:52.750 A 9.87e+00 5.40e-01 2004-02-11T00:28:52.750 A A 1 3.09 1.5 1.5 -45.0 8.55e+00 4.58e-01 2004-02-11T00:28:52.750 A 8.58e+00 4.49e-01 2004-02-11T00:28:52.750 A 8.70e+00 4.39e-01 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 8.03e+00 5.31e-01 2004-02-11T00:28:52.750 A 8.24e+00 5.45e-01 2004-02-11T00:28:52.750 A 8.14e+00 4.86e-01 2004-02-11T00:28:52.750 A A 1 3.92 1.8 1.8 -45.0 9.44e+00 9.29e-01 2004-02-11T00:28:52.750 A -9.99e+02 -9.99e+02 null U 9.27e+00 9.07e-01 2004-02-11T00:28:52.750 A Q 1 4.98 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 4.60e+01 1.02e+01 2004-09-18T19:24:35.443 C Q -2 8.04 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034435.7+320304 056.1486978 5.73e-07 +32.0509733 5.73e-07 A A 03443568+3203035 -5.00 -7.70e-01 5.07e-02 1.22e+00 6 YSOc_star+dust(IR1) 13.25 2.36 10.40 0.3540 1.34 3 3.56e+00 8.52e-02 1998-10-05T21:29:14 A 7.93e+00 1.68e-01 1998-10-05T21:29:14 A 1.22e+01 2.26e-01 1998-10-05T21:29:14 A 1.19e+01 6.02e-01 2004-02-11T00:28:52.750 A 1.35e+01 7.65e-01 2004-02-11T00:28:52.750 A 1.32e+01 6.33e-01 2004-02-11T00:28:52.750 A A 1 1.77 1.5 1.5 -45.0 1.19e+01 5.89e-01 2004-02-11T00:28:52.750 A 1.34e+01 6.86e-01 2004-02-11T00:28:52.750 A 1.26e+01 6.40e-01 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 1.17e+01 5.84e-01 2004-02-11T00:28:52.750 A 1.33e+01 6.68e-01 2004-02-11T00:28:52.750 A 1.33e+01 6.28e-01 2004-02-11T00:28:52.750 A A 1 2.54 1.8 1.8 -45.0 1.49e+01 7.41e-01 2004-02-11T00:28:52.750 A 1.57e+01 8.01e-01 2004-02-11T00:28:52.750 A 1.50e+01 7.47e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 2.18e+01 2.82e+00 2004-09-18T16:14:42.183 A 2.15e+01 2.43e+00 2004-09-18T22:34:28.703 A 2.20e+01 2.33e+00 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034436.0+320924 056.1498481 6.60e-09 +32.1567564 6.60e-09 A A 03443595+3209243 -5.00 -1.00e-02 8.08e-02 8.66e+01 6 YSOc_star+dust(IR2) 9.67 1.46 11.36 0.1710 1.76 4 4.26e+00 1.22e-01 1998-10-05T21:29:05 A 5.96e+00 2.36e-01 1998-10-05T21:29:05 A 5.81e+00 1.50e-01 1998-10-05T21:29:05 A 4.60e+00 2.97e-01 2004-02-11T00:28:52.750 A 4.48e+00 3.09e-01 2004-02-11T00:28:52.750 A 4.77e+00 3.07e-01 2004-02-11T00:28:52.750 A A 1 3.09 1.5 1.5 -45.0 4.05e+00 2.70e-01 2004-02-11T00:28:52.750 A 3.67e+00 2.70e-01 2004-02-11T00:28:52.750 A 3.86e+00 2.45e-01 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 3.37e+00 3.23e-01 2004-02-11T00:28:52.750 A 3.12e+00 2.68e-01 2004-02-11T00:28:52.750 A 3.13e+00 2.65e-01 2004-02-11T00:28:52.750 A A 1 3.92 1.8 1.8 -45.0 4.41e+00 6.40e-01 2004-02-11T00:28:52.750 B -9.99e+02 -9.99e+02 null U 4.13e+00 6.91e-01 2004-02-11T00:28:52.750 B Q -2 4.98 1.8 1.8 -45.0 2.60e+02 3.05e+01 2004-09-18T16:14:42.183 A -9.99e+02 -9.99e+02 null U 3.28e+02 6.35e+01 2004-09-18T19:24:35.443 B Q -2 8.04 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034437.0+320645 056.1539887 5.31e-07 +32.1125567 5.31e-07 A A 03443694+3206453 -5.00 -1.87e+00 4.93e-02 3.74e+00 6 YSOc_star+dust(IR4) 6.69 1.12 7.30 0.1050 0.75 6 3.31e+02 6.39e+00 1998-10-05T21:29:14 A 4.13e+02 6.47e+00 1998-10-05T21:29:14 A 3.52e+02 7.13e+00 1998-10-05T21:29:14 A 2.09e+02 1.02e+01 2004-02-11T00:28:52.750 A 1.83e+02 1.14e+01 2004-02-11T00:28:52.750 A 2.08e+02 9.97e+00 2004-02-11T00:28:52.750 A A 1 1.77 1.5 1.5 -45.0 1.48e+02 7.40e+00 2004-02-11T00:28:52.750 A 1.44e+02 7.92e+00 2004-02-11T00:28:52.750 A 1.48e+02 7.43e+00 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 1.17e+02 5.70e+00 2004-02-11T00:28:52.750 A 1.13e+02 5.53e+00 2004-02-11T00:28:52.750 A 1.16e+02 5.47e+00 2004-02-11T00:28:52.750 A A 1 2.54 1.8 1.8 -45.0 9.47e+01 4.56e+00 2004-02-11T00:28:52.750 A 9.06e+01 4.53e+00 2004-02-11T00:28:52.750 A 9.26e+01 4.51e+00 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 4.64e+01 5.04e+00 2004-09-18T16:14:42.183 A 4.88e+01 5.23e+00 2004-09-18T22:34:28.703 A 4.61e+01 4.60e+00 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034437.4+321224 056.1558386 7.25e-07 +32.2067135 7.25e-07 A A 03443739+3212241 -5.00 -6.00e-01 4.93e-02 6.62e+01 6 YSOc_star+dust(IR4) 11.98 1.14 9.79 0.1060 0.75 6 9.44e+00 3.04e-01 1998-10-05T21:29:05 A 1.87e+01 6.04e-01 1998-10-05T21:29:05 A 2.05e+01 5.84e-01 1998-10-05T21:29:05 A 1.51e+01 7.56e-01 2004-02-11T00:28:52.750 A 1.54e+01 7.68e-01 2004-02-11T00:28:52.750 A 1.54e+01 7.75e-01 2004-02-11T00:28:52.750 A A 1 3.09 1.5 1.5 -45.0 1.17e+01 5.75e-01 2004-02-11T00:28:52.750 A 1.15e+01 5.68e-01 2004-02-11T00:28:52.750 A 1.16e+01 5.71e-01 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 9.85e+00 5.20e-01 2004-02-11T00:28:52.750 A 8.93e+00 4.96e-01 2004-02-11T00:28:52.750 A 9.40e+00 4.82e-01 2004-02-11T00:28:52.750 A A 1 3.92 1.8 1.8 -45.0 1.13e+01 7.62e-01 2004-02-11T00:28:52.750 A 1.19e+01 7.25e-01 2004-02-11T00:28:52.750 A 1.19e+01 6.75e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 6.68e+01 6.39e+00 2004-09-18T16:14:42.183 A 6.73e+01 6.42e+00 2004-09-18T22:34:28.703 A 6.76e+01 6.33e+00 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034437.9+320804 056.1578533 5.53e-07 +32.1344842 5.53e-07 A A 03443788+3208041 -5.00 -9.70e-01 4.83e-02 7.30e+00 6 YSOc_star+dust(IR2) 12.90 1.39 8.22 0.1580 1.35 4 3.37e+01 6.83e-01 1998-10-05T21:29:05 A 6.59e+01 1.15e+00 1998-10-05T21:29:05 A 7.79e+01 1.29e+00 1998-10-05T21:29:05 A 7.80e+01 4.03e+00 2004-02-11T00:28:52.750 A 6.88e+01 4.37e+00 2004-02-11T00:28:52.750 A 6.91e+01 3.34e+00 2004-02-11T00:28:52.750 A A 1 1.77 1.5 1.5 -45.0 7.06e+01 3.58e+00 2004-02-11T00:28:52.750 A 6.31e+01 3.56e+00 2004-02-11T00:28:52.750 A 6.96e+01 3.43e+00 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 6.02e+01 2.92e+00 2004-02-11T00:28:52.750 A 6.01e+01 2.95e+00 2004-02-11T00:28:52.750 A 5.41e+01 2.54e+00 2004-02-11T00:28:52.750 A A 1 2.54 1.8 1.8 -45.0 6.57e+01 3.53e+00 2004-02-11T00:28:52.750 A 6.42e+01 3.66e+00 2004-02-11T00:28:52.750 A 6.70e+01 3.38e+00 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 8.77e+01 8.39e+00 2004-09-18T16:14:42.183 A 8.95e+01 8.65e+00 2004-09-18T22:34:28.703 A 8.94e+01 8.43e+00 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034438.0+320330 056.1582471 4.55e-07 +32.0582452 4.55e-07 A A 03443798+3203296 -5.00 -9.60e-01 4.80e-02 1.04e+01 6 YSOc_star+dust(IR2) 9.90 1.39 8.66 0.1570 0.51 4 4.20e+01 8.12e-01 1998-10-05T21:29:14 A 6.85e+01 1.20e+00 1998-10-05T21:29:14 A 7.51e+01 1.38e+00 1998-10-05T21:29:14 A 6.05e+01 3.07e+00 2004-02-11T00:28:52.750 A 5.60e+01 3.06e+00 2004-02-11T00:28:52.750 A 5.38e+01 2.53e+00 2004-02-11T00:28:52.750 A A 1 1.77 1.5 1.5 -45.0 5.23e+01 2.66e+00 2004-02-11T00:28:52.750 A 4.82e+01 2.55e+00 2004-02-11T00:28:52.750 A 4.93e+01 2.58e+00 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 5.04e+01 2.43e+00 2004-02-11T00:28:52.750 A 4.36e+01 2.17e+00 2004-02-11T00:28:52.750 A 4.29e+01 2.00e+00 2004-02-11T00:28:52.750 A B 1 2.54 1.8 1.8 -45.0 6.42e+01 3.10e+00 2004-02-11T00:28:52.750 A 5.49e+01 2.82e+00 2004-02-11T00:28:52.750 A 5.86e+01 2.87e+00 2004-02-11T00:28:52.750 A B 1 4.98 1.8 1.8 -45.0 6.94e+01 6.71e+00 2004-09-18T16:14:42.183 A 6.63e+01 6.41e+00 2004-09-18T22:34:28.703 A 6.83e+01 6.42e+00 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034438.0+321137 056.1583770 3.85e-08 +32.1936109 3.85e-08 A A 03443800+3211370 -5.00 -1.24e+00 7.28e-02 6.83e+00 6 YSOc_star+dust(IR3) 9.52 1.23 10.81 0.1260 1.61 5 7.36e+00 1.63e-01 1998-10-05T21:29:05 A 1.04e+01 2.21e-01 1998-10-05T21:29:05 A 1.00e+01 2.13e-01 1998-10-05T21:29:05 A 7.55e+00 3.76e-01 2004-02-11T00:28:52.750 A 7.26e+00 3.56e-01 2004-02-11T00:28:52.750 A 7.01e+00 3.76e-01 2004-02-11T00:28:52.750 A A 1 3.09 1.5 1.5 -45.0 5.71e+00 2.93e-01 2004-02-11T00:28:52.750 A 5.82e+00 2.93e-01 2004-02-11T00:28:52.750 A 5.72e+00 2.88e-01 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 5.48e+00 3.00e-01 2004-02-11T00:28:52.750 A 5.36e+00 2.92e-01 2004-02-11T00:28:52.750 A 5.41e+00 2.81e-01 2004-02-11T00:28:52.750 A A 1 3.92 1.8 1.8 -45.0 5.95e+00 3.95e-01 2004-02-11T00:28:52.750 A 5.95e+00 4.12e-01 2004-02-11T00:28:52.750 A 6.24e+00 4.16e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 9.74e+00 2.68e+00 2004-09-18T19:24:35.443 C Q -2 8.04 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034438.5+320736 056.1602428 5.40e-07 +32.1265873 5.40e-07 A A 03443845+3207356 -5.00 -7.90e-01 4.84e-02 2.37e+01 6 YSOc_star+dust(IR3) 7.98 1.23 8.84 0.1240 1.35 5 5.39e+01 1.04e+00 1998-10-05T21:29:05 A 8.32e+01 1.61e+00 1998-10-05T21:29:05 A 7.65e+01 1.48e+00 1998-10-05T21:29:05 A 4.57e+01 2.34e+00 2004-02-11T00:28:52.750 A 4.80e+01 2.86e+00 2004-02-11T00:28:52.750 A 4.59e+01 2.20e+00 2004-02-11T00:28:52.750 A A 1 1.77 1.5 1.5 -45.0 3.97e+01 1.94e+00 2004-02-11T00:28:52.750 A 3.88e+01 1.93e+00 2004-02-11T00:28:52.750 A 3.84e+01 1.97e+00 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 4.22e+01 2.05e+00 2004-02-11T00:28:52.750 A 3.91e+01 1.92e+00 2004-02-11T00:28:52.750 A 3.91e+01 1.83e+00 2004-02-11T00:28:52.750 A A 1 2.54 1.8 1.8 -45.0 6.69e+01 3.52e+00 2004-02-11T00:28:52.750 A 5.91e+01 3.19e+00 2004-02-11T00:28:52.750 A 6.43e+01 3.26e+00 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 8.04e+01 7.71e+00 2004-09-18T16:14:42.183 A 8.22e+01 8.01e+00 2004-09-18T22:34:28.703 A 8.22e+01 7.76e+00 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034438.5+320801 056.1606005 6.94e-07 +32.1335133 6.94e-07 A A 03443854+3208006 -5.00 -6.20e-01 4.88e-02 4.81e+01 6 YSOc_star+dust(IR3) 9.48 1.23 9.26 0.1250 1.16 5 2.67e+01 5.16e-01 1998-10-05T21:29:05 A 4.47e+01 7.82e-01 1998-10-05T21:29:05 A 4.33e+01 7.98e-01 1998-10-05T21:29:05 A 3.41e+01 1.72e+00 2004-02-11T00:28:52.750 A 2.70e+01 1.47e+00 2004-02-11T00:28:52.750 A 2.91e+01 1.42e+00 2004-02-11T00:28:52.750 A B 1 1.77 1.5 1.5 -45.0 2.79e+01 1.37e+00 2004-02-11T00:28:52.750 A 2.03e+01 1.10e+00 2004-02-11T00:28:52.750 A 2.37e+01 1.23e+00 2004-02-11T00:28:52.750 A C 1 2.97 1.7 1.7 -45.0 2.37e+01 1.20e+00 2004-02-11T00:28:52.750 A 1.76e+01 9.23e-01 2004-02-11T00:28:52.750 A 2.01e+01 9.56e-01 2004-02-11T00:28:52.750 A C 1 2.54 1.8 1.8 -45.0 2.93e+01 2.07e+00 2004-02-11T00:28:52.750 A 2.77e+01 1.68e+00 2004-02-11T00:28:52.750 A 2.95e+01 1.61e+00 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 1.08e+02 1.02e+01 2004-09-18T16:14:42.183 A 1.09e+02 1.03e+01 2004-09-18T22:34:28.703 A 1.10e+02 1.03e+01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034439.0+320320 056.1623752 8.28e-09 +32.0554724 8.28e-09 A A 03443896+3203196 -1.68 -1.16e+00 7.41e-02 7.59e+00 6 YSOc_star+dust(IR1) 5.17 2.37 12.42 0.3550 0.09 3 3.56e+00 8.20e-02 1998-10-05T21:29:14 A 4.46e+00 9.86e-02 1998-10-05T21:29:14 A 4.16e+00 9.19e-02 1998-10-05T21:29:14 A 3.71e+00 1.98e-01 2004-02-11T00:28:52.750 A 3.30e+00 1.77e-01 2004-02-11T00:28:52.750 A 3.74e+00 1.80e-01 2004-02-11T00:28:52.750 A A 1 1.77 1.5 1.5 -45.0 3.21e+00 1.64e-01 2004-02-11T00:28:52.750 A 2.91e+00 1.45e-01 2004-02-11T00:28:52.750 A 3.09e+00 1.54e-01 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 2.78e+00 1.65e-01 2004-02-11T00:28:52.750 A 2.58e+00 1.59e-01 2004-02-11T00:28:52.750 A 2.66e+00 1.32e-01 2004-02-11T00:28:52.750 A A 1 2.54 1.8 1.8 -45.0 3.31e+00 2.57e-01 2004-02-11T00:28:52.750 A 3.09e+00 2.47e-01 2004-02-11T00:28:52.750 A 3.51e+00 2.78e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 5.71e+00 1.50e+00 2004-09-18T19:24:35.443 C Q -2 8.04 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034439.2+322009 056.1633064 1.47e-08 +32.3358042 1.47e-08 A A 03443918+3220089 -5.00 -1.57e+00 7.10e-02 2.44e+00 6 YSOc_star+dust(IR3) 8.16 1.23 10.37 0.1260 1.37 5 1.39e+01 2.44e-01 1998-10-05T21:28:57 A 1.94e+01 3.76e-01 1998-10-05T21:28:57 A 1.77e+01 2.60e-01 1998-10-05T21:28:57 A 1.20e+01 5.79e-01 2004-02-11T00:28:52.750 A 1.13e+01 5.94e-01 2004-02-11T00:28:52.750 A 1.12e+01 6.11e-01 2004-02-11T00:28:52.750 A A 1 3.09 1.5 1.5 -45.0 9.07e+00 4.49e-01 2004-02-11T00:28:52.750 A 9.27e+00 4.73e-01 2004-02-11T00:28:52.750 A 9.15e+00 4.54e-01 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 7.90e+00 4.05e-01 2004-02-11T00:28:52.750 A 7.95e+00 4.15e-01 2004-02-11T00:28:52.750 A 7.93e+00 3.96e-01 2004-02-11T00:28:52.750 A A 1 3.92 1.8 1.8 -45.0 7.29e+00 4.16e-01 2004-02-11T00:28:52.750 A 8.08e+00 5.65e-01 2004-02-11T00:28:52.750 A 7.54e+00 4.62e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 5.86e+00 1.63e+00 2004-09-18T19:24:35.443 C Q -2 8.04 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034439.2+320945 056.1633674 3.59e-08 +32.1624551 3.59e-08 A A 03443919+3209448 -5.00 -1.15e+00 7.30e-02 9.29e+00 6 YSOc_star+dust(IR2) 8.56 1.41 9.96 0.1620 0.72 4 1.65e+01 3.20e-01 1998-10-05T21:29:05 A 2.67e+01 4.68e-01 1998-10-05T21:29:05 A 2.45e+01 4.73e-01 1998-10-05T21:29:05 A 1.65e+01 8.33e-01 2004-02-11T00:28:52.750 A 1.98e+01 1.06e+00 2004-02-11T00:28:52.750 A 1.79e+01 9.71e-01 2004-02-11T00:28:52.750 A B 1 3.09 1.5 1.5 -45.0 1.41e+01 7.01e-01 2004-02-11T00:28:52.750 A 1.73e+01 9.05e-01 2004-02-11T00:28:52.750 A 1.58e+01 7.87e-01 2004-02-11T00:28:52.750 A B 1 2.97 1.7 1.7 -45.0 1.23e+01 7.00e-01 2004-02-11T00:28:52.750 A 1.51e+01 8.13e-01 2004-02-11T00:28:52.750 A 1.40e+01 7.52e-01 2004-02-11T00:28:52.750 A B 1 3.92 1.8 1.8 -45.0 1.48e+01 1.13e+00 2004-02-11T00:28:52.750 A 1.67e+01 1.28e+00 2004-02-11T00:28:52.750 A 1.60e+01 1.06e+00 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 4.84e+01 1.35e+01 2004-09-18T19:24:35.443 C Q -2 8.04 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034439.8+321804 056.1658298 6.20e-07 +32.3011195 6.20e-07 A A 03443979+3218041 -5.00 -1.19e+00 4.91e-02 1.44e+01 6 YSOc_star+dust(IR3) 9.88 1.23 9.54 0.1270 1.42 5 2.05e+01 3.58e-01 1998-10-05T21:28:57 A 3.15e+01 4.94e-01 1998-10-05T21:28:57 A 3.24e+01 4.78e-01 1998-10-05T21:28:57 A 2.32e+01 1.15e+00 2004-02-11T00:28:52.750 A 2.30e+01 1.16e+00 2004-02-11T00:28:52.750 A 2.16e+01 1.24e+00 2004-02-11T00:28:52.750 A A 1 3.09 1.5 1.5 -45.0 1.92e+01 9.37e-01 2004-02-11T00:28:52.750 A 1.79e+01 8.94e-01 2004-02-11T00:28:52.750 A 1.81e+01 9.15e-01 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 1.52e+01 7.44e-01 2004-02-11T00:28:52.750 A 1.46e+01 7.17e-01 2004-02-11T00:28:52.750 A 1.49e+01 7.25e-01 2004-02-11T00:28:52.750 A A 1 3.92 1.8 1.8 -45.0 1.52e+01 7.58e-01 2004-02-11T00:28:52.750 A 1.48e+01 7.45e-01 2004-02-11T00:28:52.750 A 1.51e+01 7.29e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 2.22e+01 2.10e+00 2004-09-18T16:14:42.183 A 2.08e+01 1.99e+00 2004-09-18T22:34:28.703 A 2.17e+01 2.03e+00 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034441.7+321202 056.1739209 6.68e-07 +32.2006218 6.68e-07 A A 03444173+3212022 -5.00 -6.80e-01 4.91e-02 8.48e+01 6 YSOc_star+dust(IR4) 10.32 1.13 10.23 0.1060 0.85 6 7.95e+00 1.83e-01 1998-11-21T20:13:38 A 1.60e+01 3.09e-01 1998-11-21T20:13:38 A 1.80e+01 2.99e-01 1998-11-21T20:13:38 A 1.14e+01 5.52e-01 2004-02-11T00:28:52.750 A 1.12e+01 5.62e-01 2004-02-11T00:28:52.750 A 1.08e+01 5.63e-01 2004-02-11T00:28:52.750 A A 1 3.09 1.5 1.5 -45.0 8.49e+00 4.08e-01 2004-02-11T00:28:52.750 A 8.42e+00 4.12e-01 2004-02-11T00:28:52.750 A 8.14e+00 4.03e-01 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 7.02e+00 3.63e-01 2004-02-11T00:28:52.750 A 2.28e+00 7.75e-01 2004-02-11T00:28:52.750 D 6.95e+00 3.57e-01 2004-02-11T00:28:52.750 A C 1 3.92 1.8 1.8 -45.0 6.90e+00 4.47e-01 2004-02-11T00:28:52.750 A 7.37e+00 5.00e-01 2004-02-11T00:28:52.750 A 6.91e+00 3.88e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 4.85e+01 4.51e+00 2004-09-18T16:14:42.183 A 4.81e+01 4.52e+00 2004-09-18T22:34:28.703 A 4.91e+01 4.57e+00 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034442.0+320860 056.1751584 3.16e-08 +32.1499828 3.16e-08 A A 03444207+3209009 -5.00 -1.65e+00 7.24e-02 4.20e+00 6 YSOc_star+dust(IR4) 9.20 1.14 9.08 0.1090 1.65 6 3.07e+01 6.49e-01 1998-11-21T20:13:38 E 5.53e+01 1.17e+00 1998-11-21T20:13:38 E 5.89e+01 1.03e+00 1998-11-21T20:13:38 E 2.68e+01 1.42e+00 2004-02-11T00:28:52.750 A 3.15e+01 1.73e+00 2004-02-11T00:28:52.750 A 3.11e+01 1.81e+00 2004-02-11T00:28:52.750 A B 7 1.77 1.5 1.5 -45.0 2.45e+01 1.30e+00 2004-02-11T00:28:52.750 A 2.51e+01 1.34e+00 2004-02-11T00:28:52.750 A 2.72e+01 1.46e+00 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 2.03e+01 1.22e+00 2004-02-11T00:28:52.750 A 2.05e+01 1.43e+00 2004-02-11T00:28:52.750 A 2.11e+01 1.32e+00 2004-02-11T00:28:52.750 A A 3 3.92 1.8 1.8 -45.0 2.20e+01 1.40e+00 2004-02-11T00:28:52.750 A 1.86e+01 1.69e+00 2004-02-11T00:28:52.750 A 2.10e+01 1.47e+00 2004-02-11T00:28:52.750 A A 3 4.98 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 1.51e+01 3.49e+00 2004-09-18T19:24:35.443 C Q -2 8.04 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034442.1+320902 056.1756061 8.47e-07 +32.1505711 8.47e-07 A A null -5.00 -5.20e-01 5.73e-02 1.85e+00 5 YSOc -999.00 -999.00 -999.00 -999.0000 -999.00 0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N 4.12e+01 2.18e+00 2004-02-11T00:28:52.750 A 3.63e+01 2.01e+00 2004-02-11T00:28:52.750 A 2.64e+01 1.71e+00 2004-02-11T00:28:52.750 A A 1 3.09 1.5 1.5 -45.0 4.30e+01 2.35e+00 2004-02-11T00:28:52.750 A 4.02e+01 2.05e+00 2004-02-11T00:28:52.750 A 3.47e+01 1.77e+00 2004-02-11T00:28:52.750 A A 7 2.97 1.7 1.7 -45.0 3.70e+01 1.85e+00 2004-02-11T00:28:52.750 A 3.64e+01 1.85e+00 2004-02-11T00:28:52.750 A 3.56e+01 1.76e+00 2004-02-11T00:28:52.750 A A 3 3.92 1.8 1.8 -45.0 3.87e+01 2.03e+00 2004-02-11T00:28:52.750 A 3.94e+01 2.30e+00 2004-02-11T00:28:52.750 A 3.89e+01 2.05e+00 2004-02-11T00:28:52.750 A A 3 4.98 1.8 1.8 -45.0 7.25e+01 6.77e+00 2004-09-18T16:14:42.183 A 7.41e+01 6.92e+00 2004-09-18T22:34:28.703 A 7.38e+01 6.86e+00 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034442.6+321002 056.1774053 8.55e-07 +32.1673528 8.55e-07 A A 03444256+3210025 -5.00 -9.60e-01 5.97e-02 2.00e+00 6 YSOc_star+dust(IR2) 11.43 1.41 10.38 0.1640 0.57 4 5.63e+00 1.30e-01 1998-11-21T20:13:38 A 1.16e+01 2.46e-01 1998-11-21T20:13:38 A 1.28e+01 2.24e-01 1998-11-21T20:13:38 A 1.12e+01 5.72e-01 2004-02-11T00:28:52.750 A 1.18e+01 6.03e-01 2004-02-11T00:28:52.750 A 1.02e+01 5.70e-01 2004-02-11T00:28:52.750 A A 1 3.09 1.5 1.5 -45.0 1.13e+01 5.50e-01 2004-02-11T00:28:52.750 A 1.10e+01 5.70e-01 2004-02-11T00:28:52.750 A 1.10e+01 5.48e-01 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 1.04e+01 5.39e-01 2004-02-11T00:28:52.750 A 1.03e+01 5.42e-01 2004-02-11T00:28:52.750 A 1.02e+01 5.25e-01 2004-02-11T00:28:52.750 A A 1 3.92 1.8 1.8 -45.0 1.09e+01 6.49e-01 2004-02-11T00:28:52.750 A 1.14e+01 7.10e-01 2004-02-11T00:28:52.750 A 1.13e+01 6.11e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 1.37e+01 2.57e+00 2004-09-18T16:14:42.183 B 1.44e+01 2.78e+00 2004-09-18T22:34:28.703 B 1.41e+01 2.12e+00 2004-09-18T19:24:35.443 B A 7 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034442.8+320834 056.1781800 3.46e-08 +32.1427011 3.46e-08 A A 03444276+3208337 -5.00 -1.10e+00 6.93e-02 2.50e+00 6 YSOc_star+dust(IR2) 10.74 1.39 10.32 0.1580 1.40 4 8.18e+00 1.88e-01 1998-11-21T20:13:29 A 1.33e+01 2.58e-01 1998-11-21T20:13:29 A 1.38e+01 2.42e-01 1998-11-21T20:13:29 A 1.10e+01 5.55e-01 2004-02-11T00:28:52.750 A 1.14e+01 5.77e-01 2004-02-11T00:28:52.750 A 1.14e+01 5.50e-01 2004-02-11T00:28:52.750 A A 1 1.77 1.5 1.5 -45.0 9.45e+00 4.73e-01 2004-02-11T00:28:52.750 A 1.01e+01 5.16e-01 2004-02-11T00:28:52.750 A 9.84e+00 4.86e-01 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 9.18e+00 5.13e-01 2004-02-11T00:28:52.750 A 9.79e+00 5.46e-01 2004-02-11T00:28:52.750 A 1.03e+01 5.06e-01 2004-02-11T00:28:52.750 A A 1 2.54 1.8 1.8 -45.0 9.61e+00 8.61e-01 2004-02-11T00:28:52.750 A 1.09e+01 8.72e-01 2004-02-11T00:28:52.750 A 1.05e+01 7.53e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 1.36e+01 2.99e+00 2004-09-18T19:24:35.443 C Q -2 8.04 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034443.0+321560 056.1792926 8.24e-07 +32.2665591 8.24e-07 A A 03444301+3215596 -2.54 -3.60e-01 5.01e-02 1.06e+01 6 YSOc_star+dust(IR1) 8.72 2.38 12.01 0.3570 0.48 3 2.29e+00 8.03e-02 1998-11-21T20:13:38 A 3.68e+00 1.05e-01 1998-11-21T20:13:38 A 4.29e+00 1.15e-01 1998-11-21T20:13:38 A 4.53e+00 2.30e-01 2004-02-11T00:28:52.750 A 3.96e+00 2.09e-01 2004-02-11T00:28:52.750 A 4.06e+00 2.21e-01 2004-02-11T00:28:52.750 A A 1 3.09 1.5 1.5 -45.0 4.47e+00 2.20e-01 2004-02-11T00:28:52.750 A 3.88e+00 1.94e-01 2004-02-11T00:28:52.750 A 4.14e+00 2.07e-01 2004-02-11T00:28:52.750 A B 1 2.97 1.7 1.7 -45.0 4.74e+00 2.50e-01 2004-02-11T00:28:52.750 A 3.88e+00 2.13e-01 2004-02-11T00:28:52.750 A 4.22e+00 2.30e-01 2004-02-11T00:28:52.750 A B 1 3.92 1.8 1.8 -45.0 7.08e+00 3.92e-01 2004-02-11T00:28:52.750 A 5.84e+00 3.48e-01 2004-02-11T00:28:52.750 A 6.42e+00 3.46e-01 2004-02-11T00:28:52.750 A B 1 4.98 1.8 1.8 -45.0 1.75e+01 1.75e+00 2004-09-18T16:14:42.183 A 1.71e+01 1.70e+00 2004-09-18T22:34:28.703 A 1.75e+01 1.68e+00 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034443.1+313734 056.1794247 8.55e-07 +31.6260305 8.55e-07 A A 03444306+3137338 -1.91 -9.50e-01 5.09e-02 1.56e+01 6 YSOc_star+dust(IR4) 14.25 1.15 10.77 0.1100 1.88 6 2.46e+00 7.94e-02 1998-11-21T20:13:03 A 5.31e+00 1.42e-01 1998-11-21T20:13:03 A 6.65e+00 1.53e-01 1998-11-21T20:13:03 A 5.66e+00 2.87e-01 2004-09-07T07:03:48.646 A 5.47e+00 3.16e-01 2004-09-07T07:03:48.646 A 5.04e+00 3.03e-01 2004-09-07T07:03:48.646 A A 1 3.09 1.5 1.5 -45.0 4.93e+00 2.45e-01 2004-09-07T07:03:48.646 A 4.39e+00 2.57e-01 2004-09-07T07:03:48.646 A 4.19e+00 2.67e-01 2004-09-07T07:03:48.646 A A 1 2.97 1.7 1.7 -45.0 4.01e+00 2.13e-01 2004-09-07T07:03:48.646 A 3.85e+00 2.20e-01 2004-09-07T07:03:48.646 A 3.81e+00 2.13e-01 2004-09-07T07:03:48.646 A A 1 3.92 1.8 1.8 -45.0 4.37e+00 2.23e-01 2004-09-07T07:03:48.646 A 4.19e+00 2.32e-01 2004-09-07T07:03:48.646 A 3.99e+00 2.20e-01 2004-09-07T07:03:48.646 A A 1 4.98 1.8 1.8 -45.0 7.89e+00 7.66e-01 2004-09-18T16:14:42.183 A 8.11e+00 8.05e-01 2004-09-18T22:34:28.703 A 8.06e+00 7.67e-01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034443.3+320132 056.1804919 7.30e-07 +32.0254264 7.30e-07 A A 03444330+3201315 -5.00 5.00e-01 4.90e-02 2.52e+01 6 YSOc_red -999.00 -999.00 -999.00 -999.0000 -999.00 0 3.96e-01 -9.99e+02 1998-11-21T20:13:29 U 3.64e+00 2.15e-01 1998-11-21T20:13:29 A 3.44e+01 7.30e-01 1998-11-21T20:13:29 A 2.24e+02 1.99e+01 2004-02-11T00:28:52.750 K 2.04e+02 1.24e+01 2004-02-11T00:28:52.750 A 1.81e+02 9.19e+00 2004-02-11T00:28:52.750 A A 7 1.77 1.5 1.5 -45.0 3.94e+02 2.15e+01 2004-02-11T00:28:52.750 A 3.22e+02 2.07e+01 2004-02-11T00:28:52.750 A 3.02e+02 1.76e+01 2004-02-11T00:28:52.750 K B 7 2.27 1.7 1.7 -45.0 5.63e+02 3.03e+01 2004-02-11T00:28:52.750 A 4.11e+02 2.41e+01 2004-02-11T00:28:52.750 A 3.78e+02 1.81e+01 2004-02-11T00:28:52.750 A B 7 2.54 1.8 1.8 -45.0 7.65e+02 3.80e+01 2004-02-11T00:28:52.750 A 6.25e+02 3.28e+01 2004-02-11T00:28:52.750 A 5.55e+02 2.77e+01 2004-02-11T00:28:52.750 A B 7 2.54 1.8 1.8 -45.0 2.04e+03 1.96e+02 2004-09-18T16:14:42.183 A 1.98e+03 1.99e+02 2004-09-18T22:34:28.703 A 2.04e+03 1.93e+02 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034443.5+320743 056.1813585 8.55e-07 +32.1285414 8.55e-07 A A 03444351+3207427 -5.00 -1.88e+00 5.59e-02 2.15e+01 6 YSOc_star+dust(MP1) 8.18 1.10 9.47 0.0987 0.35 7 2.27e+01 4.39e-01 1998-11-21T20:13:29 A 4.53e+01 7.51e-01 1998-11-21T20:13:29 A 4.54e+01 6.69e-01 1998-11-21T20:13:29 A 2.70e+01 1.47e+00 2004-02-11T00:28:52.750 A 2.66e+01 1.46e+00 2004-02-11T00:28:52.750 A 2.68e+01 1.30e+00 2004-02-11T00:28:52.750 A A 1 1.77 1.5 1.5 -45.0 1.80e+01 9.16e-01 2004-02-11T00:28:52.750 A 1.86e+01 9.58e-01 2004-02-11T00:28:52.750 A 1.83e+01 9.36e-01 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 1.28e+01 6.91e-01 2004-02-11T00:28:52.750 A 1.23e+01 6.98e-01 2004-02-11T00:28:52.750 A 1.26e+01 6.15e-01 2004-02-11T00:28:52.750 A A 1 2.54 1.8 1.8 -45.0 8.88e+00 6.76e-01 2004-02-11T00:28:52.750 A 8.53e+00 6.29e-01 2004-02-11T00:28:52.750 A 8.65e+00 5.56e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 9.18e+00 1.55e+00 2004-09-18T16:14:42.183 B 9.05e+00 1.76e+00 2004-09-18T22:34:28.703 B 9.77e+00 1.20e+00 2004-09-18T19:24:35.443 A A 7 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034443.8+321030 056.1824052 5.51e-07 +32.1751020 5.51e-07 A A 03444376+3210304 -5.00 -4.50e-01 4.84e-02 2.36e+01 6 YSOc_star+dust(IR2) 7.32 1.40 9.95 0.1610 0.02 4 1.93e+01 3.73e-01 1998-11-21T20:13:38 A 3.23e+01 5.66e-01 1998-11-21T20:13:38 A 3.11e+01 4.58e-01 1998-11-21T20:13:38 A 1.96e+01 9.71e-01 2004-02-11T00:28:52.750 A 2.10e+01 1.04e+00 2004-02-11T00:28:52.750 A 1.88e+01 9.93e-01 2004-02-11T00:28:52.750 A A 1 3.09 1.5 1.5 -45.0 1.99e+01 9.47e-01 2004-02-11T00:28:52.750 A 1.84e+01 9.08e-01 2004-02-11T00:28:52.750 A 1.87e+01 9.12e-01 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 2.17e+01 1.05e+00 2004-02-11T00:28:52.750 A 1.98e+01 9.69e-01 2004-02-11T00:28:52.750 A 2.09e+01 1.00e+00 2004-02-11T00:28:52.750 A A 1 3.92 1.8 1.8 -45.0 3.82e+01 1.88e+00 2004-02-11T00:28:52.750 A 3.50e+01 1.92e+00 2004-02-11T00:28:52.750 A 3.55e+01 1.73e+00 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 7.06e+01 6.63e+00 2004-09-18T16:14:42.183 A 6.54e+01 6.16e+00 2004-09-18T22:34:28.703 A 6.81e+01 6.34e+00 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034444.6+320813 056.1857888 5.47e-07 +32.1368114 5.47e-07 A A 03444458+3208125 -5.00 -8.40e-01 5.38e-02 5.17e+00 6 YSOc_star+dust(IR1) 7.81 2.36 10.45 0.3530 0.09 3 1.12e+01 2.17e-01 1998-11-21T20:13:29 A 1.84e+01 3.21e-01 1998-11-21T20:13:29 A 1.94e+01 2.86e-01 1998-11-21T20:13:29 A 2.81e+01 1.41e+00 2004-02-11T00:28:52.750 A 2.63e+01 1.39e+00 2004-02-11T00:28:52.750 A 2.59e+01 1.25e+00 2004-02-11T00:28:52.750 A A 1 1.77 1.5 1.5 -45.0 2.67e+01 1.28e+00 2004-02-11T00:28:52.750 A 2.56e+01 1.31e+00 2004-02-11T00:28:52.750 A 2.56e+01 1.29e+00 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 2.60e+01 1.26e+00 2004-02-11T00:28:52.750 A 2.31e+01 1.14e+00 2004-02-11T00:28:52.750 A 2.30e+01 1.09e+00 2004-02-11T00:28:52.750 A A 1 2.54 1.8 1.8 -45.0 3.34e+01 1.62e+00 2004-02-11T00:28:52.750 A 2.90e+01 1.60e+00 2004-02-11T00:28:52.750 A 3.17e+01 1.55e+00 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 2.43e+01 3.90e+00 2004-09-18T16:14:42.183 B 2.58e+01 4.38e+00 2004-09-18T22:34:28.703 B 2.67e+01 3.34e+00 2004-09-18T19:24:35.443 A A 7 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034444.7+320402 056.1863257 4.93e-07 +32.0673561 4.93e-07 A A 03444472+3204024 -5.00 -9.20e-01 4.78e-02 8.07e+00 6 YSOc_star+dust(IR2) 11.26 1.39 7.94 0.1580 0.89 4 6.20e+01 1.14e+00 1998-11-21T20:13:29 A 1.09e+02 1.80e+00 1998-11-21T20:13:29 A 1.23e+02 1.59e+00 1998-11-21T20:13:29 A 1.06e+02 5.73e+00 2004-02-11T00:28:52.750 A 9.93e+01 5.91e+00 2004-02-11T00:28:52.750 A 9.77e+01 4.72e+00 2004-02-11T00:28:52.750 A A 2 1.81 1.7 1.4 -51.5 9.75e+01 4.91e+00 2004-02-11T00:28:52.750 A 9.64e+01 4.93e+00 2004-02-11T00:28:52.750 A 9.77e+01 4.84e+00 2004-02-11T00:28:52.750 A A 2 6.25 2.4 2.3 -83.3 8.78e+01 4.17e+00 2004-02-11T00:28:52.750 A 8.50e+01 4.22e+00 2004-02-11T00:28:52.750 A 8.43e+01 3.93e+00 2004-02-11T00:28:52.750 A A 1 2.54 1.8 1.8 -45.0 9.31e+01 4.62e+00 2004-02-11T00:28:52.750 A 8.93e+01 4.54e+00 2004-02-11T00:28:52.750 A 9.28e+01 4.57e+00 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 1.49e+02 1.39e+01 2004-09-18T16:14:42.183 A 1.56e+02 1.44e+01 2004-09-18T22:34:28.703 A 1.53e+02 1.42e+01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034445.2+320120 056.1883145 5.12e-07 +32.0221208 5.12e-07 A A 03444520+3201197 -5.00 -3.40e-01 4.82e-02 4.33e+00 6 YSOc_star+dust(IR1) 16.49 2.41 11.19 0.3610 0.99 3 7.37e-01 5.84e-02 1998-11-21T20:13:29 A 2.39e+00 1.04e-01 1998-11-21T20:13:29 A 4.19e+00 1.20e-01 1998-11-21T20:13:29 A 6.86e+00 3.59e-01 2004-02-11T00:28:52.750 A 9.05e+00 4.65e-01 2004-02-11T00:28:52.750 A 9.11e+00 4.38e-01 2004-02-11T00:28:52.750 A B 1 1.77 1.5 1.5 -45.0 7.95e+00 4.03e-01 2004-02-11T00:28:52.750 A 1.05e+01 5.35e-01 2004-02-11T00:28:52.750 A 1.04e+01 4.98e-01 2004-02-11T00:28:52.750 A B 1 2.27 1.7 1.7 -45.0 8.89e+00 4.51e-01 2004-02-11T00:28:52.750 A 1.19e+01 5.92e-01 2004-02-11T00:28:52.750 A 1.19e+01 5.66e-01 2004-02-11T00:28:52.750 A C 1 2.54 1.8 1.8 -45.0 1.17e+01 5.70e-01 2004-02-11T00:28:52.750 A 1.41e+01 6.99e-01 2004-02-11T00:28:52.750 A 1.41e+01 6.61e-01 2004-02-11T00:28:52.750 A B 1 2.54 1.8 1.8 -45.0 2.58e+01 2.49e+00 2004-09-18T16:14:42.183 A 2.33e+01 2.77e+00 2004-09-18T22:34:28.703 A 2.71e+01 2.57e+00 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034450.4+315236 056.2097928 5.14e-07 +31.8766693 5.14e-07 A A 03445036+3152359 -1.76 -9.80e-01 4.94e-02 1.01e+01 6 YSOc_star+dust(IR2) 11.98 1.40 11.65 0.1580 2.27 4 1.94e+00 6.43e-02 1998-11-21T20:13:21 A 3.19e+00 1.12e-01 1998-11-21T20:13:21 A 3.40e+00 1.13e-01 1998-11-21T20:13:21 A 2.57e+00 1.27e-01 2004-02-11T00:28:52.750 A 2.48e+00 1.42e-01 2004-02-11T00:28:52.750 A 3.13e+00 1.48e-01 2004-02-11T00:28:52.750 A A 1 1.77 1.5 1.5 -45.0 -9.99e+02 -9.99e+02 null N 2.36e+00 1.28e-01 2004-02-11T00:28:52.750 A 3.02e+00 1.42e-01 2004-02-11T00:28:52.750 A Q 1 2.27 1.7 1.7 -45.0 2.18e+00 1.15e-01 2004-02-11T00:28:52.750 A 2.18e+00 1.31e-01 2004-09-07T07:03:48.646 A 2.50e+00 1.20e-01 2004-02-11T00:28:52.750 A A 1 2.54 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null N 2.38e+00 1.35e-01 2004-02-11T00:28:52.750 A 2.54e+00 1.23e-01 2004-02-11T00:28:52.750 A Q 1 2.54 1.8 1.8 -45.0 5.10e+00 5.69e-01 2004-09-18T16:14:42.183 A 4.46e+00 4.62e-01 2004-09-18T22:34:28.703 A 4.67e+00 4.67e-01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034452.0+322625 056.2166773 1.00e-06 +32.4403998 1.00e-06 A A 03445200+3226253 -5.00 -1.43e+00 5.24e-02 3.42e+00 6 YSOc_star+dust(IR2) 12.38 1.50 10.39 0.1820 1.20 4 5.20e+00 1.39e-01 1998-11-21T20:13:55 A 9.41e+00 2.08e-01 1998-11-21T20:13:55 A 1.15e+01 2.53e-01 1998-11-21T20:13:55 A -9.99e+02 -9.99e+02 null N 9.56e+00 7.53e-01 2004-02-11T00:28:52.750 A 9.78e+00 7.69e-01 2004-02-11T00:28:52.750 A Q 1 3.09 1.5 1.5 -45.0 8.76e+00 4.36e-01 2004-02-11T00:28:52.750 A 8.82e+00 5.34e-01 2004-02-11T00:28:52.750 A 8.98e+00 4.49e-01 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 -9.99e+02 -9.99e+02 null N 6.84e+00 3.76e-01 2004-02-11T00:28:52.750 A 6.87e+00 3.78e-01 2004-02-11T00:28:52.750 A Q 1 3.92 1.8 1.8 -45.0 5.93e+00 3.44e-01 2004-02-11T00:28:52.750 K 5.73e+00 4.07e-01 2004-02-11T00:28:52.750 A 5.85e+00 2.85e-01 2004-02-11T00:28:52.750 A A 1 2.54 1.8 1.8 -45.0 5.05e+00 5.24e-01 2004-09-18T16:14:42.183 A 4.83e+00 5.52e-01 2004-09-18T22:34:28.703 A 5.02e+00 5.02e-01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034452.1+315825 056.2168919 4.21e-07 +31.9736661 4.21e-07 A A 03445205+3158252 -5.00 -1.11e+00 4.76e-02 1.08e+01 6 YSOc_star+dust(IR2) 13.25 1.39 10.18 0.1570 1.64 4 4.83e+00 1.11e-01 1998-11-21T20:13:21 A 1.07e+01 2.27e-01 1998-11-21T20:13:21 A 1.18e+01 2.07e-01 1998-11-21T20:13:21 A 9.76e+00 4.65e-01 2004-02-11T00:28:52.750 A 1.23e+01 6.16e-01 2004-02-11T00:28:52.750 A 1.12e+01 5.30e-01 2004-02-11T00:28:52.750 A B 1 1.77 1.5 1.5 -45.0 6.94e+00 3.37e-01 2004-02-11T00:28:52.750 A 1.20e+01 6.16e-01 2004-02-11T00:28:52.750 A 9.97e+00 4.73e-01 2004-02-11T00:28:52.750 A D 1 2.27 1.7 1.7 -45.0 7.31e+00 3.49e-01 2004-02-11T00:28:52.750 A 1.04e+01 5.17e-01 2004-02-11T00:28:52.750 A 8.99e+00 4.26e-01 2004-02-11T00:28:52.750 A C 1 2.54 1.8 1.8 -45.0 5.55e+00 2.74e-01 2004-02-11T00:28:52.750 A 8.38e+00 4.22e-01 2004-02-11T00:28:52.750 A 7.49e+00 3.53e-01 2004-02-11T00:28:52.750 A C 1 2.54 1.8 1.8 -45.0 1.22e+01 1.16e+00 2004-09-18T16:14:42.183 A 1.29e+01 1.23e+00 2004-09-18T22:34:28.703 A 1.26e+01 1.18e+00 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034455.6+320920 056.2317441 1.83e-08 +32.1555053 1.83e-08 A A 03445561+3209198 -5.00 -2.22e+00 6.31e-02 1.43e+01 6 YSOc_star+dust(MP1) 6.66 1.09 9.59 0.0981 0.20 7 2.91e+01 6.17e-01 1998-11-21T20:13:38 A 5.13e+01 8.98e-01 1998-11-21T20:13:38 A 4.79e+01 7.06e-01 1998-11-21T20:13:38 A 2.55e+01 1.28e+00 2004-02-11T00:28:52.750 A 2.66e+01 1.40e+00 2004-02-11T00:28:52.750 A 2.58e+01 1.25e+00 2004-02-11T00:28:52.750 A A 1 1.77 1.5 1.5 -45.0 1.77e+01 8.78e-01 2004-02-11T00:28:52.750 A 1.79e+01 9.24e-01 2004-02-11T00:28:52.750 A 1.81e+01 9.32e-01 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 1.26e+01 6.31e-01 2004-02-11T00:28:52.750 A 1.26e+01 6.29e-01 2004-02-11T00:28:52.750 A 1.24e+01 5.85e-01 2004-02-11T00:28:52.750 A A 1 2.54 1.8 1.8 -45.0 7.51e+00 4.25e-01 2004-02-11T00:28:52.750 A 7.52e+00 4.30e-01 2004-02-11T00:28:52.750 A 7.65e+00 3.92e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 9.48e+00 2.01e+00 2004-09-18T19:24:35.443 C Q -2 8.04 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034456.1+320915 056.2339352 5.02e-07 +32.1542246 5.02e-07 A A 03445614+3209152 -5.00 -9.60e-01 4.77e-02 6.70e+01 6 YSOc_star+dust(IR4) 12.16 1.13 7.90 0.1050 1.54 6 6.21e+01 1.14e+00 1998-11-21T20:13:38 A 1.03e+02 1.71e+00 1998-11-21T20:13:38 A 1.08e+02 1.60e+00 1998-11-21T20:13:38 A 6.99e+01 3.37e+00 2004-02-11T00:28:52.750 A 7.48e+01 4.20e+00 2004-02-11T00:28:52.750 A 8.69e+01 4.23e+00 2004-02-11T00:28:52.750 A A 1 1.77 1.5 1.5 -45.0 5.32e+01 2.68e+00 2004-02-11T00:28:52.750 A 6.48e+01 3.37e+00 2004-02-11T00:28:52.750 A 6.53e+01 3.40e+00 2004-02-11T00:28:52.750 A B 1 2.97 1.7 1.7 -45.0 4.35e+01 2.11e+00 2004-02-11T00:28:52.750 A 5.32e+01 2.57e+00 2004-02-11T00:28:52.750 A 5.53e+01 2.60e+00 2004-02-11T00:28:52.750 A B 1 2.54 1.8 1.8 -45.0 4.17e+01 2.01e+00 2004-02-11T00:28:52.750 A 4.37e+01 2.28e+00 2004-02-11T00:28:52.750 A 4.38e+01 2.09e+00 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 2.14e+02 1.98e+01 2004-09-18T16:14:42.183 A 2.14e+02 1.98e+01 2004-09-18T22:34:28.703 A 2.14e+02 1.98e+01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034456.8+315411 056.2368369 6.07e-07 +31.9031668 6.07e-07 A A 03445683+3154114 -1.49 -1.33e+00 5.22e-02 3.88e+01 6 YSOc_star+dust(IR4) 9.57 1.13 11.86 0.1050 1.44 6 3.09e+00 8.83e-02 1998-11-21T20:13:21 A 3.88e+00 1.11e-01 1998-11-21T20:13:21 A 3.85e+00 1.03e-01 1998-11-21T20:13:21 A 2.63e+00 1.25e-01 2004-02-11T00:28:52.750 A 2.32e+00 1.26e-01 2004-02-11T00:28:52.750 A 2.61e+00 1.24e-01 2004-02-11T00:28:52.750 A A 1 1.77 1.5 1.5 -45.0 -9.99e+02 -9.99e+02 null N 1.94e+00 1.04e-01 2004-02-11T00:28:52.750 A 1.96e+00 9.39e-02 2004-02-11T00:28:52.750 A Q 1 2.27 1.7 1.7 -45.0 1.81e+00 9.28e-02 2004-02-11T00:28:52.750 A 1.45e+00 9.92e-02 2004-02-11T00:28:52.750 A 1.61e+00 7.97e-02 2004-02-11T00:28:52.750 A B 1 2.54 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null N 1.12e+00 8.74e-02 2004-02-11T00:28:52.750 A 1.13e+00 6.34e-02 2004-02-11T00:28:52.750 A Q 1 2.54 1.8 1.8 -45.0 3.14e+00 3.59e-01 2004-09-18T16:14:42.183 A 2.87e+00 4.18e-01 2004-09-18T22:34:28.703 B 3.12e+00 3.40e-01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034456.9+322036 056.2371028 9.39e-07 +32.3432085 9.39e-07 A A 03445688+3220355 -1.96 -4.10e-01 6.23e-02 8.42e+00 6 YSOc_red -999.00 -999.00 -999.00 -999.0000 -999.00 0 5.80e-02 -9.99e+02 1998-11-21T20:13:47 U 2.03e-01 -9.99e+02 1998-11-21T20:13:47 U 6.01e-01 8.86e-02 1998-11-21T20:13:47 C 1.26e+00 6.50e-02 2004-02-11T00:28:52.750 A 1.22e+00 6.89e-02 2004-02-11T00:28:52.750 A 1.16e+00 6.71e-02 2004-02-11T00:28:52.750 A A 1 3.09 1.5 1.5 -45.0 1.48e+00 7.30e-02 2004-02-11T00:28:52.750 A 1.43e+00 7.38e-02 2004-02-11T00:28:52.750 A 1.47e+00 7.19e-02 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 1.54e+00 1.12e-01 2004-02-11T00:28:52.750 A 1.45e+00 9.34e-02 2004-02-11T00:28:52.750 A 1.48e+00 8.28e-02 2004-02-11T00:28:52.750 A A 1 3.92 1.8 1.8 -45.0 1.21e+00 1.23e-01 2004-02-11T00:28:52.750 A 1.30e+00 1.70e-01 2004-02-11T00:28:52.750 A 1.28e+00 9.68e-02 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 4.64e+00 5.75e-01 2004-09-18T16:14:42.183 A 3.76e+00 7.54e-01 2004-09-18T22:34:28.703 C 4.46e+00 5.23e-01 2004-09-18T19:24:35.443 A A 9 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034457.7+320742 056.2404938 8.44e-07 +32.1282515 8.44e-07 A A 03445771+3207416 -4.46 -9.00e-01 5.46e-02 5.59e+01 6 YSOc_star+dust(IR4) 9.26 1.14 11.90 0.1060 0.83 6 2.85e+00 7.87e-02 1998-11-21T20:13:29 A 3.94e+00 1.09e-01 1998-11-21T20:13:29 A 3.88e+00 1.11e-01 1998-11-21T20:13:29 A 2.69e+00 1.47e-01 2004-02-11T00:28:52.750 A 2.66e+00 1.39e-01 2004-02-11T00:28:52.750 A 2.61e+00 1.27e-01 2004-02-11T00:28:52.750 A A 1 1.77 1.5 1.5 -45.0 1.97e+00 1.00e-01 2004-02-11T00:28:52.750 A 1.96e+00 9.87e-02 2004-02-11T00:28:52.750 A 1.99e+00 9.81e-02 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 1.47e+00 1.06e-01 2004-02-11T00:28:52.750 A 1.38e+00 1.02e-01 2004-02-11T00:28:52.750 A 1.45e+00 8.01e-02 2004-02-11T00:28:52.750 A A 1 2.54 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U 1.15e+00 1.52e-01 2004-02-11T00:28:52.750 A 1.16e+00 1.26e-01 2004-02-11T00:28:52.750 A Q 7 4.98 1.8 1.8 -45.0 6.28e+00 6.98e-01 2004-09-18T16:14:42.183 A 6.48e+00 8.05e-01 2004-09-18T22:34:28.703 A 6.50e+00 6.73e-01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034457.9+320402 056.2410587 6.40e-07 +32.0670951 6.40e-07 A A 03445785+3204016 -1.59 -1.62e+00 6.32e-02 5.71e+00 6 YSOc_star+dust(IR4) 7.84 1.13 11.36 0.1050 1.51 6 6.36e+00 1.23e-01 1998-11-21T20:13:29 A 8.40e+00 1.78e-01 1998-11-21T20:13:29 A 7.31e+00 1.41e-01 1998-11-21T20:13:29 A 4.43e+00 2.30e-01 2004-02-11T00:28:52.750 A 4.24e+00 2.25e-01 2004-02-11T00:28:52.750 A 4.42e+00 2.09e-01 2004-02-11T00:28:52.750 A A 1 1.77 1.5 1.5 -45.0 3.45e+00 1.73e-01 2004-02-11T00:28:52.750 A 3.35e+00 1.73e-01 2004-02-11T00:28:52.750 A 3.45e+00 1.69e-01 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 2.84e+00 1.59e-01 2004-02-11T00:28:52.750 A 2.73e+00 1.49e-01 2004-02-11T00:28:52.750 A 2.75e+00 1.32e-01 2004-02-11T00:28:52.750 A A 1 2.54 1.8 1.8 -45.0 3.06e+00 1.67e-01 2004-02-11T00:28:52.750 A 2.92e+00 1.65e-01 2004-02-11T00:28:52.750 A 3.00e+00 1.54e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 1.98e+00 4.63e-01 2004-09-18T16:14:42.183 C 1.27e+00 4.08e-01 2004-09-18T22:34:28.703 C 1.54e+00 3.31e-01 2004-09-18T19:24:35.443 C A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034458.5+315827 056.2439413 7.34e-07 +31.9741807 7.34e-07 A A 03445853+3158270 -1.67 -1.02e+00 6.15e-02 2.07e+00 6 YSOc_star+dust(IR1) 9.14 2.45 13.02 0.3680 1.02 3 8.63e-01 5.41e-02 1998-11-21T20:13:21 A 1.31e+00 9.15e-02 1998-11-21T20:13:21 A 1.66e+00 8.27e-02 1998-11-21T20:13:21 A 1.44e+00 7.23e-02 2004-02-11T00:28:52.750 A 1.41e+00 8.16e-02 2004-02-11T00:28:52.750 A 1.71e+00 8.21e-02 2004-02-11T00:28:52.750 A A 1 1.77 1.5 1.5 -45.0 1.39e+00 8.74e-02 2004-02-11T00:28:52.750 A 1.32e+00 7.18e-02 2004-02-11T00:28:52.750 A 1.65e+00 7.93e-02 2004-02-11T00:28:52.750 A A 1 2.27 1.7 1.7 -45.0 1.34e+00 7.61e-02 2004-02-11T00:28:52.750 A 1.21e+00 7.97e-02 2004-02-11T00:28:52.750 A 1.45e+00 7.51e-02 2004-02-11T00:28:52.750 A A 1 2.54 1.8 1.8 -45.0 1.66e+00 1.24e-01 2004-02-11T00:28:52.750 A 1.33e+00 8.67e-02 2004-02-11T00:28:52.750 A 1.77e+00 8.92e-02 2004-02-11T00:28:52.750 A B 1 2.54 1.8 1.8 -45.0 1.22e+00 3.17e-01 2004-09-18T16:14:42.183 C 1.74e+00 3.51e-01 2004-09-18T22:34:28.703 C 1.47e+00 2.58e-01 2004-09-18T19:24:35.443 B A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034459.8+321332 056.2493602 8.38e-07 +32.2255371 8.38e-07 A A 03445983+3213319 -1.73 -1.51e+00 6.08e-02 1.14e+00 6 YSOc_star+dust(IR3) 10.18 1.23 11.05 0.1250 1.62 5 5.00e+00 1.29e-01 1998-11-21T20:13:38 A 7.50e+00 1.87e-01 1998-11-21T20:13:38 A 7.54e+00 1.60e-01 1998-11-21T20:13:38 A 4.78e+00 2.39e-01 2004-02-11T00:28:52.750 A 5.57e+00 2.82e-01 2004-02-11T00:28:52.750 A 5.37e+00 2.68e-01 2004-02-11T00:28:52.750 A B 1 3.09 1.5 1.5 -45.0 4.08e+00 2.06e-01 2004-02-11T00:28:52.750 A 4.81e+00 2.46e-01 2004-02-11T00:28:52.750 A 4.44e+00 2.24e-01 2004-02-11T00:28:52.750 A B 1 2.97 1.7 1.7 -45.0 3.54e+00 1.98e-01 2004-02-11T00:28:52.750 A 4.10e+00 2.26e-01 2004-02-11T00:28:52.750 A 3.89e+00 2.06e-01 2004-02-11T00:28:52.750 A A 1 3.92 1.8 1.8 -45.0 3.43e+00 2.34e-01 2004-02-11T00:28:52.750 A 4.04e+00 2.57e-01 2004-02-11T00:28:52.750 A 3.75e+00 2.18e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 2.16e+00 4.74e-01 2004-09-18T16:14:42.183 C 1.89e+00 3.70e-01 2004-09-18T22:34:28.703 B 2.07e+00 3.28e-01 2004-09-18T19:24:35.443 B A 7 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034500.5+320320 056.2518920 6.67e-07 +32.0555931 6.67e-07 A A 03450045+3203201 -1.62 -1.53e+00 6.11e-02 3.62e+00 6 YSOc_star+dust(IR4) 16.77 1.15 11.13 0.1060 1.40 6 9.64e-01 5.95e-02 1998-11-21T20:13:29 A 2.58e+00 1.07e-01 1998-11-21T20:13:29 A 3.57e+00 1.02e-01 1998-11-21T20:13:29 A 3.38e+00 1.69e-01 2004-02-11T00:28:52.750 A 3.19e+00 1.62e-01 2004-02-11T00:28:52.750 A 3.23e+00 1.54e-01 2004-02-11T00:28:52.750 A A 1 1.77 1.5 1.5 -45.0 2.73e+00 1.38e-01 2004-02-11T00:28:52.750 A 2.65e+00 1.31e-01 2004-02-11T00:28:52.750 A 2.71e+00 1.36e-01 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 2.41e+00 1.39e-01 2004-02-11T00:28:52.750 A 2.14e+00 1.30e-01 2004-02-11T00:28:52.750 A 2.30e+00 1.15e-01 2004-02-11T00:28:52.750 A A 1 2.54 1.8 1.8 -45.0 1.90e+00 1.18e-01 2004-02-11T00:28:52.750 A 1.72e+00 1.03e-01 2004-02-11T00:28:52.750 A 1.74e+00 9.40e-02 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 1.87e+00 3.77e-01 2004-09-18T16:14:42.183 C 1.74e+00 3.80e-01 2004-09-18T22:34:28.703 C 1.71e+00 2.94e-01 2004-09-18T19:24:35.443 B A 9 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034501.4+320502 056.2559488 4.36e-07 +32.0838160 4.36e-07 A A 03450142+3205017 -5.00 -2.31e+00 4.96e-02 3.49e+00 6 YSOc_star+dust(MP1) 3.47 1.09 9.21 0.0976 0.57 7 1.19e+02 2.31e+00 1998-11-21T20:13:29 A 1.13e+02 1.88e+00 1998-11-21T20:13:29 A 9.24e+01 1.53e+00 1998-11-21T20:13:29 A 4.41e+01 2.15e+00 2004-02-11T00:28:52.750 A 4.32e+01 2.30e+00 2004-02-11T00:28:52.750 A 4.36e+01 2.07e+00 2004-02-11T00:28:52.750 A A 1 1.77 1.5 1.5 -45.0 3.00e+01 1.46e+00 2004-02-11T00:28:52.750 A 2.97e+01 1.51e+00 2004-02-11T00:28:52.750 A 3.01e+01 1.46e+00 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 2.01e+01 9.75e-01 2004-02-11T00:28:52.750 A 2.07e+01 1.00e+00 2004-02-11T00:28:52.750 A 2.04e+01 9.53e-01 2004-02-11T00:28:52.750 A A 1 2.54 1.8 1.8 -45.0 1.35e+01 6.44e-01 2004-02-11T00:28:52.750 A 1.35e+01 6.58e-01 2004-02-11T00:28:52.750 A 1.36e+01 6.44e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 4.41e+00 4.74e-01 2004-09-18T16:14:42.183 A 4.39e+00 5.38e-01 2004-09-18T22:34:28.703 A 4.44e+00 4.58e-01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034504.7+321501 056.2694031 2.79e-08 +32.2502981 2.79e-08 A A 03450464+3215010 -1.66 -1.93e+00 6.88e-02 1.40e+00 6 YSOc_star+dust(IR4) 9.51 1.13 10.67 0.1060 0.55 6 7.05e+00 1.56e-01 1998-11-21T20:13:38 A 1.20e+01 2.55e-01 1998-11-21T20:13:38 A 1.24e+01 2.05e-01 1998-11-21T20:13:38 A 8.15e+00 4.05e-01 2004-02-11T00:28:52.750 A 8.14e+00 4.19e-01 2004-02-11T00:28:52.750 A 7.79e+00 4.31e-01 2004-02-11T00:28:52.750 A A 1 3.09 1.5 1.5 -45.0 5.91e+00 2.97e-01 2004-02-11T00:28:52.750 A 5.97e+00 3.06e-01 2004-02-11T00:28:52.750 A 5.95e+00 2.97e-01 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 4.43e+00 2.43e-01 2004-02-11T00:28:52.750 A 4.50e+00 2.36e-01 2004-02-11T00:28:52.750 A 4.50e+00 2.29e-01 2004-02-11T00:28:52.750 A A 1 3.92 1.8 1.8 -45.0 3.56e+00 1.96e-01 2004-02-11T00:28:52.750 A 3.64e+00 2.04e-01 2004-02-11T00:28:52.750 A 3.61e+00 1.85e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null U -9.99e+02 -9.99e+02 null U 2.30e+00 7.35e-01 2004-09-18T19:24:35.443 C Q -2 8.04 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034507.6+321028 056.2818205 5.75e-07 +32.1744089 5.75e-07 A A 03450762+3210279 -5.00 -2.38e+00 5.49e-02 3.11e+00 6 YSOc_star+dust(IR2) -0.56 1.48 9.59 0.1780 1.20 4 1.43e+02 2.63e+00 1998-11-21T20:13:38 A 1.49e+02 2.60e+00 1998-11-21T20:13:38 A 1.15e+02 1.48e+00 1998-11-21T20:13:38 A 5.23e+01 2.58e+00 2004-02-11T00:28:52.750 A 5.15e+01 2.86e+00 2004-02-11T00:28:52.750 A 3.96e+01 2.93e+00 2004-02-11T00:28:52.750 A A 1 3.09 1.5 1.5 -45.0 3.43e+01 1.70e+00 2004-02-11T00:28:52.750 A 3.38e+01 1.67e+00 2004-02-11T00:28:52.750 A 3.44e+01 1.66e+00 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 2.34e+01 1.15e+00 2004-02-11T00:28:52.750 A 2.34e+01 1.13e+00 2004-02-11T00:28:52.750 A 2.35e+01 1.12e+00 2004-02-11T00:28:52.750 A A 1 3.92 1.8 1.8 -45.0 1.50e+01 7.49e-01 2004-02-11T00:28:52.750 A 1.47e+01 7.83e-01 2004-02-11T00:28:52.750 A 1.47e+01 7.34e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 3.60e+00 4.77e-01 2004-09-18T16:14:42.183 A 3.43e+00 6.16e-01 2004-09-18T22:34:28.703 B 3.93e+00 4.50e-01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034513.1+322005 056.3044948 9.57e-07 +32.3348045 9.57e-07 A A 03451307+3220053 -1.54 -1.26e+00 6.30e-02 1.71e+00 6 YSOc_star+dust(IR2) 10.85 1.88 12.17 0.2000 0.14 3 1.48e+00 -9.99e+02 1998-11-21T20:13:47 U 2.51e+00 1.25e-01 1998-11-21T20:13:47 A 2.73e+00 1.08e-01 1998-11-21T20:13:47 A 2.21e+00 1.14e-01 2004-02-11T00:28:52.750 A 2.13e+00 1.13e-01 2004-02-11T00:28:52.750 A 1.96e+00 1.18e-01 2004-02-11T00:28:52.750 A A 1 3.09 1.5 1.5 -45.0 1.86e+00 9.59e-02 2004-02-11T00:28:52.750 A 1.92e+00 9.66e-02 2004-02-11T00:28:52.750 A 1.90e+00 9.50e-02 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 1.73e+00 1.06e-01 2004-02-11T00:28:52.750 A 1.48e+00 9.84e-02 2004-02-11T00:28:52.750 A 1.61e+00 9.25e-02 2004-02-11T00:28:52.750 A A 1 3.92 1.8 1.8 -45.0 1.74e+00 1.14e-01 2004-02-11T00:28:52.750 A 1.60e+00 1.03e-01 2004-02-11T00:28:52.750 A 1.67e+00 9.46e-02 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 1.60e+00 3.56e-01 2004-09-18T16:14:42.183 C 1.24e+00 2.51e-01 2004-09-18T22:34:28.703 C 1.46e+00 2.32e-01 2004-09-18T19:24:35.443 B A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034513.5+322435 056.3062750 6.41e-07 +32.4096568 6.41e-07 A A 03451349+3224347 -1.89 -1.10e+00 5.06e-02 1.26e+01 6 YSOc_star+dust(IR1) 7.58 2.39 12.20 0.3590 0.18 3 2.42e+00 8.46e-02 1998-11-21T20:13:47 A 3.74e+00 1.24e-01 1998-11-21T20:13:47 A 3.98e+00 1.36e-01 1998-11-21T20:13:47 A 4.31e+00 2.32e-01 2004-02-11T00:28:52.750 A 4.70e+00 2.33e-01 2004-02-11T00:28:52.750 A 4.72e+00 2.31e-01 2004-02-11T00:28:52.750 A A 1 3.09 1.5 1.5 -45.0 3.75e+00 1.87e-01 2004-02-11T00:28:52.750 A 3.96e+00 1.93e-01 2004-02-11T00:28:52.750 A 3.91e+00 1.87e-01 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 3.10e+00 1.59e-01 2004-02-11T00:28:52.750 A 3.04e+00 1.58e-01 2004-02-11T00:28:52.750 A 3.07e+00 1.53e-01 2004-02-11T00:28:52.750 A A 1 3.92 1.8 1.8 -45.0 3.15e+00 1.60e-01 2004-02-11T00:28:52.750 A 3.13e+00 1.58e-01 2004-02-11T00:28:52.750 A 3.16e+00 1.54e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 4.92e+00 5.52e-01 2004-09-18T16:14:42.183 A 5.11e+00 5.93e-01 2004-09-18T22:34:28.703 A 5.04e+00 5.25e-01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034517.8+321206 056.3242629 7.79e-07 +32.2016191 7.79e-07 A B 03451782+3212058 -1.69 -1.35e+00 5.34e-02 7.71e+00 6 YSOc_star+dust(IR2) 8.43 1.41 11.30 0.1630 0.35 4 5.06e+00 1.17e-01 1998-01-25T13:52:11 A 7.65e+00 1.62e-01 1998-01-25T13:52:11 A 7.71e+00 1.49e-01 1998-01-25T13:52:11 A 4.49e+00 2.34e-01 2004-02-11T00:28:52.750 A 6.48e+00 3.33e-01 2004-02-11T00:28:52.750 A 5.19e+00 2.88e-01 2004-02-11T00:28:52.750 A C 1 3.09 1.5 1.5 -45.0 3.81e+00 1.83e-01 2004-02-11T00:28:52.750 A 5.57e+00 2.77e-01 2004-02-11T00:28:52.750 A 4.66e+00 2.40e-01 2004-02-11T00:28:52.750 A C 1 2.97 1.7 1.7 -45.0 2.97e+00 1.60e-01 2004-02-11T00:28:52.750 A 4.64e+00 2.38e-01 2004-02-11T00:28:52.750 A 3.65e+00 2.05e-01 2004-02-11T00:28:52.750 A C 1 3.92 1.8 1.8 -45.0 2.78e+00 1.52e-01 2004-02-11T00:28:52.750 A 3.82e+00 2.02e-01 2004-02-11T00:28:52.750 A 3.38e+00 1.78e-01 2004-02-11T00:28:52.750 A C 1 4.98 1.8 1.8 -45.0 3.84e+00 5.10e-01 2004-09-18T16:14:42.183 A 4.19e+00 5.01e-01 2004-09-18T22:34:28.703 A 3.96e+00 4.45e-01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034529.7+315920 056.3738160 6.12e-07 +31.9888077 6.12e-07 A A 03452971+3159197 -1.57 -1.05e+00 4.93e-02 1.24e+01 6 YSOc_star+dust(IR3) 11.53 1.23 11.44 0.1240 1.57 5 2.49e+00 7.12e-02 1998-01-25T13:52:28 A 4.32e+00 9.56e-02 1998-01-25T13:52:28 A 4.56e+00 1.09e-01 1998-01-25T13:52:28 A 3.56e+00 1.71e-01 2004-02-11T00:28:52.750 A 3.55e+00 1.95e-01 2004-02-11T00:28:52.750 A 3.49e+00 1.66e-01 2004-02-11T00:28:52.750 A A 1 1.77 1.5 1.5 -45.0 -9.99e+02 -9.99e+02 null N 3.07e+00 1.65e-01 2004-02-11T00:28:52.750 A 2.89e+00 1.39e-01 2004-02-11T00:28:52.750 A Q 1 2.27 1.7 1.7 -45.0 2.76e+00 1.42e-01 2004-02-11T00:28:52.750 A 2.78e+00 1.55e-01 2004-02-11T00:28:52.750 A 2.66e+00 1.31e-01 2004-02-11T00:28:52.750 A A 1 2.54 1.8 1.8 -45.0 -9.99e+02 -9.99e+02 null N 2.85e+00 1.54e-01 2004-02-11T00:28:52.750 A 2.73e+00 1.31e-01 2004-02-11T00:28:52.750 A Q 1 2.54 1.8 1.8 -45.0 4.45e+00 4.76e-01 2004-09-18T16:14:42.183 A 4.69e+00 5.11e-01 2004-09-18T22:34:28.703 A 4.63e+00 4.65e-01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034533.5+314555 056.3894541 7.87e-07 +31.7653612 7.87e-07 A A 03453345+3145553 -1.67 -9.70e-01 5.20e-02 7.07e+00 6 YSOc_star+dust(IR2) 13.29 1.43 11.65 0.1640 0.99 4 1.26e+00 5.79e-02 1998-01-25T13:52:36 A 2.58e+00 9.27e-02 1998-01-25T13:52:36 A 3.29e+00 9.40e-02 1998-01-25T13:52:36 A 3.05e+00 1.59e-01 2004-09-07T07:03:48.646 A 2.98e+00 1.56e-01 2004-09-07T07:03:48.646 A 2.83e+00 1.58e-01 2004-09-07T07:03:48.646 A A 1 3.09 1.5 1.5 -45.0 2.85e+00 1.43e-01 2004-09-07T07:03:48.646 A 2.69e+00 1.40e-01 2004-09-07T07:03:48.646 A 2.73e+00 1.38e-01 2005-09-16T09:10:33.865 A A 1 2.27 1.7 1.7 -45.0 2.57e+00 1.41e-01 2004-09-07T07:03:48.646 A 2.39e+00 1.38e-01 2004-09-07T07:03:48.646 A 2.40e+00 1.36e-01 2004-09-07T07:03:48.646 A A 1 3.92 1.8 1.8 -45.0 2.33e+00 1.29e-01 2004-09-07T07:03:48.646 A 2.33e+00 1.34e-01 2004-09-07T07:03:48.646 A 2.39e+00 1.22e-01 2005-09-16T09:10:33.865 A A 1 2.54 1.8 1.8 -45.0 4.16e+00 5.02e-01 2004-09-18T16:14:42.183 A 4.12e+00 4.60e-01 2004-09-18T22:34:28.703 A 4.17e+00 4.38e-01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034535.6+315954 056.3984925 4.41e-07 +31.9984539 4.41e-07 A A 03453563+3159544 -5.00 -9.70e-01 4.77e-02 1.34e+01 6 YSOc_star+dust(IR3) 11.85 1.22 10.44 0.1230 1.32 5 5.26e+00 1.16e-01 1998-01-25T13:52:19 A 1.03e+01 2.08e-01 1998-01-25T13:52:19 A 1.17e+01 1.94e-01 1998-01-25T13:52:19 A 8.69e+00 4.19e-01 2004-02-11T00:28:52.750 A 8.87e+00 4.88e-01 2004-02-11T00:28:52.750 A 8.39e+00 3.99e-01 2004-02-11T00:28:52.750 A A 1 1.77 1.5 1.5 -45.0 7.22e+00 3.63e-01 2004-02-11T00:28:52.750 A 7.43e+00 3.66e-01 2004-02-11T00:28:52.750 A 7.15e+00 3.36e-01 2004-02-11T00:28:52.750 A A 1 2.27 1.7 1.7 -45.0 6.63e+00 3.21e-01 2004-02-11T00:28:52.750 A 6.73e+00 3.61e-01 2004-02-11T00:28:52.750 A 6.49e+00 3.08e-01 2004-02-11T00:28:52.750 A A 1 2.54 1.8 1.8 -45.0 8.25e+00 4.05e-01 2004-02-11T00:28:52.750 A 8.03e+00 4.00e-01 2004-02-11T00:28:52.750 A 7.65e+00 3.58e-01 2004-02-11T00:28:52.750 A A 1 2.54 1.8 1.8 -45.0 1.23e+01 1.26e+00 2004-09-18T16:14:42.183 A 1.20e+01 1.15e+00 2004-09-18T22:34:28.703 A 1.22e+01 1.15e+00 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034536.8+322557 056.4034686 6.39e-07 +32.4325087 6.39e-07 A A 03453685+3225567 -5.00 -1.02e+00 4.99e-02 2.84e+01 6 YSOc_star+dust(IR2) 7.97 1.46 8.02 0.1760 0.07 4 1.04e+02 1.91e+00 1998-01-25T13:51:53 A 1.68e+02 3.10e+00 1998-01-25T13:51:53 A 1.79e+02 2.80e+00 1998-01-25T13:51:53 A 1.31e+02 7.15e+00 2004-02-11T00:28:52.750 A 1.05e+02 8.50e+00 2004-02-11T00:28:52.750 A 1.07e+02 7.57e+00 2004-02-11T00:28:52.750 A B 7 3.09 1.5 1.5 -45.0 1.01e+02 5.27e+00 2004-02-11T00:28:52.750 A 1.04e+02 5.33e+00 2004-02-11T00:28:52.750 A 1.04e+02 5.16e+00 2004-02-11T00:28:52.750 A A 7 2.97 1.7 1.7 -45.0 7.97e+01 3.85e+00 2004-02-11T00:28:52.750 A 6.78e+01 4.58e+00 2004-02-11T00:28:52.750 A 7.20e+01 4.11e+00 2004-02-11T00:28:52.750 A A 1 3.92 1.8 1.8 -45.0 8.01e+01 3.97e+00 2004-02-11T00:28:52.750 A 8.15e+01 3.99e+00 2004-02-11T00:28:52.750 A 8.14e+01 3.93e+00 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 1.79e+02 1.65e+01 2004-09-18T16:14:42.183 A 1.71e+02 1.58e+01 2004-09-18T22:34:28.703 A 1.77e+02 1.63e+01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034547.4+314311 056.4473566 8.09e-07 +31.7198486 8.09e-07 Q C 03454735+3143113 -1.80 -1.81e+00 6.49e-02 1.46e+00 6 YSOc_star+dust(IR4) 12.14 1.14 10.93 0.1080 1.31 6 4.04e+00 1.15e-01 1998-01-25T13:52:45 A 6.10e+00 1.68e-01 1998-01-25T13:52:45 A 6.75e+00 1.74e-01 1998-01-25T13:52:45 A 5.54e+00 2.77e-01 2004-09-07T07:03:48.646 K 5.66e+00 2.95e-01 2004-09-07T07:03:48.646 K 5.51e+00 2.87e-01 2004-09-07T07:03:48.646 K A 3 1.77 1.5 1.5 -45.0 4.38e+00 2.29e-01 2004-09-07T07:03:48.646 K 4.37e+00 2.39e-01 2004-09-07T07:03:48.646 K 3.99e+00 2.41e-01 2004-09-07T07:03:48.646 K A 1 2.27 1.7 1.7 -45.0 3.31e+00 1.84e-01 2004-09-07T07:03:48.646 K 3.36e+00 1.84e-01 2004-09-07T07:03:48.646 K 3.30e+00 1.75e-01 2004-09-07T07:03:48.646 K A 7 2.54 1.8 1.8 -45.0 2.56e+00 1.49e-01 2004-09-07T07:03:48.646 K 2.61e+00 1.53e-01 2004-09-07T07:03:48.646 K 2.55e+00 1.41e-01 2004-09-07T07:03:48.646 K A 1 2.54 1.8 1.8 -45.0 1.55e+00 3.33e-01 2004-09-18T16:14:42.183 C 1.05e+00 3.32e-01 2004-09-18T22:34:28.703 C 1.28e+00 2.55e-01 2004-09-18T19:24:35.443 B A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034558.2+322647 056.4926923 9.45e-07 +32.4465252 9.45e-07 A A 03455824+3226475 -5.00 -1.26e+00 5.34e-02 2.30e-01 6 YSOc_star+dust(IR2) 11.33 1.44 10.83 0.1710 2.14 4 4.76e+00 1.05e-01 1998-01-25T13:58:57 A 7.59e+00 1.47e-01 1998-01-25T13:58:57 A 7.88e+00 1.38e-01 1998-01-25T13:58:57 A 8.49e+00 4.42e-01 2004-02-11T00:28:52.750 A 7.15e+00 4.92e-01 2004-02-11T00:28:52.750 A 7.09e+00 4.57e-01 2004-02-11T00:28:52.750 A B 1 3.09 1.5 1.5 -45.0 8.01e+00 3.94e-01 2004-02-11T00:28:52.750 A 7.32e+00 4.27e-01 2004-02-11T00:28:52.750 A 6.54e+00 4.18e-01 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 6.58e+00 3.45e-01 2004-02-11T00:28:52.750 A 6.08e+00 3.80e-01 2004-02-11T00:28:52.750 A 6.04e+00 3.56e-01 2004-02-11T00:28:52.750 A A 1 3.92 1.8 1.8 -45.0 5.80e+00 2.90e-01 2004-02-11T00:28:52.750 A 5.69e+00 3.03e-01 2004-02-11T00:28:52.750 A 5.44e+00 2.91e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 4.58e+00 5.27e-01 2004-09-18T16:14:42.183 A 4.39e+00 5.24e-01 2004-09-18T22:34:28.703 A 4.52e+00 4.77e-01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034657.4+324917 056.7390993 6.98e-07 +32.8214969 6.98e-07 A A 03465739+3249173 -1.90 -1.27e+00 4.94e-02 1.18e+01 6 YSOc_star+dust(IR2) 9.12 1.39 11.10 0.1590 1.39 4 5.97e+00 1.37e-01 1998-01-25T14:13:12 A 8.27e+00 1.83e-01 1998-01-25T14:13:12 A 7.93e+00 1.75e-01 1998-01-25T14:13:12 A 5.83e+00 3.07e-01 2004-02-11T00:28:52.750 A 6.13e+00 3.10e-01 2004-02-11T00:28:52.750 A 6.14e+00 3.04e-01 2004-02-11T00:28:52.750 A A 1 3.09 1.5 1.5 -45.0 5.14e+00 2.54e-01 2004-02-11T00:28:52.750 A 5.13e+00 2.59e-01 2004-02-11T00:28:52.750 A 5.21e+00 2.53e-01 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 4.17e+00 2.24e-01 2004-02-11T00:28:52.750 A 4.27e+00 2.27e-01 2004-02-11T00:28:52.750 A 4.24e+00 2.14e-01 2004-02-11T00:28:52.750 A A 1 3.92 1.8 1.8 -45.0 3.68e+00 1.87e-01 2004-02-11T00:28:52.750 A 3.82e+00 1.96e-01 2004-02-11T00:28:52.750 A 3.78e+00 1.84e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 5.40e+00 5.79e-01 2004-09-18T16:14:42.183 A 5.29e+00 5.44e-01 2004-09-18T22:34:28.703 A 5.39e+00 5.33e-01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 - SSTc2d J034658.5+324659 056.7437927 6.12e-07 +32.7830271 6.12e-07 A A 03465851+3246588 -5.00 -1.36e+00 5.06e-02 5.52e+00 6 YSOc_star+dust(IR4) 11.14 1.13 10.70 0.1050 1.86 6 5.88e+00 1.35e-01 1998-01-25T14:13:12 A 9.27e+00 2.13e-01 1998-01-25T14:13:12 A 9.11e+00 1.85e-01 1998-01-25T14:13:12 A 6.52e+00 3.30e-01 2004-02-11T00:28:52.750 A 6.74e+00 3.33e-01 2004-02-11T00:28:52.750 A 6.80e+00 3.31e-01 2004-02-11T00:28:52.750 A A 1 3.09 1.5 1.5 -45.0 5.16e+00 2.55e-01 2004-02-11T00:28:52.750 A 5.15e+00 2.57e-01 2004-02-11T00:28:52.750 A 5.22e+00 2.53e-01 2004-02-11T00:28:52.750 A A 1 2.97 1.7 1.7 -45.0 4.57e+00 2.38e-01 2004-02-11T00:28:52.750 A 4.42e+00 2.36e-01 2004-02-11T00:28:52.750 A 4.53e+00 2.27e-01 2004-02-11T00:28:52.750 A A 1 3.92 1.8 1.8 -45.0 4.57e+00 2.30e-01 2004-02-11T00:28:52.750 A 4.77e+00 2.39e-01 2004-02-11T00:28:52.750 A 4.70e+00 2.27e-01 2004-02-11T00:28:52.750 A A 1 4.98 1.8 1.8 -45.0 3.87e+00 4.79e-01 2004-09-18T16:14:42.183 A 3.91e+00 4.47e-01 2004-09-18T22:34:28.703 A 3.96e+00 4.21e-01 2004-09-18T19:24:35.443 A A 1 8.16 2.7 2.7 -45.0 -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null N -9.99e+02 -9.99e+02 null U Q 0 -999.00 -999.0 -999.0 -999.0 diff --git a/doc/videos.rst b/doc/videos.rst deleted file mode 100644 index dbdf65379..000000000 --- a/doc/videos.rst +++ /dev/null @@ -1,58 +0,0 @@ -.. _demo_videos: - -Demo Videos -=========== - -Quick Introduction to Glue (2 minutes) --------------------------------------- - -.. raw:: html - -
- -
- -2013 SciPy Conference Talk (20 minutes) ---------------------------------------- -.. raw:: html - -
- -The Perseus Shell (2 Minutes) ------------------------------ -*Featured in Alyssa Goodman's plenary talk at the 223rd meeting of the American Astronomical Society* - -.. raw:: html - -
- -
- -Glue, data cleaning, and civic hacking (5 minutes) --------------------------------------------------- - -.. raw:: html - -
- -
- -Glue, FBI Crime Data, and Plotly (5 minutes) --------------------------------------------- - -.. raw:: html - -
- < -
- -See also the `IPython notebook `_ that accompanies this video. - -Extracting slices from cubes ----------------------------- - -.. raw:: html - -
- -
\ No newline at end of file diff --git a/doc/whatsnew/0.7_code_reorganization.rst b/doc/whatsnew/0.7_code_reorganization.rst deleted file mode 100644 index 5c4f05f9c..000000000 --- a/doc/whatsnew/0.7_code_reorganization.rst +++ /dev/null @@ -1,109 +0,0 @@ -:orphan: - -Code reorganization in Glue v0.7 -================================ - -In the Glue v0.7 release, a large number of functions and classes have been -reorganized to help make the code base more approachable to new developers. As -a result, users importing classes/functions from Glue may need to update -imports in e.g. config scripts. Most of the files affected are likely only used -internally in Glue, but all the moves are nevertheless documented below for -completeness. - -For a few common cases, backward-compatibility is provided for now. These -special cases are: - -==================================================== =============================================================== - Old location in Glue v0.6 New location in Glue v0.7 -==================================================== =============================================================== - ``glue.qt.get_qapp`` ``glue.external.qt.get_qapp`` - ``glue.qt.qtutil.load_ui`` ``glue.utils.qt.helpers.load_ui`` - ``glue.qt.widget_properties`` ``glue.utils.qt.widget_properties`` - ``glue.qt.widgets.data_viewer`` ``glue.viewers.common.qt.data_viewer`` -==================================================== =============================================================== - -The old imports will continue to work for now, but will be removed in future, -after a couple of major releases. - -On the other hand, the following imports will need to be updated now, as no -backward-compatibility is provided for these: - -==================================================== =============================================================== - Old location in Glue v0.6 New location in Glue v0.7 -==================================================== =============================================================== - ``glue.clients.ds9norm`` ``glue.viewers.image.ds9norm`` - ``glue.clients.histogram_client`` ``glue.viewers.histogram.client`` - ``glue.clients.image_client`` ``glue.viewers.image.client`` - ``glue.clients.layer_artist.ChangedTrigger`` ``glue.clients.layer_artist.ChangedTrigger`` - ``glue.clients.layer_artist.LayerArtistContainer`` ``glue.clients.layer_artist.LayerArtistContainer`` - ``glue.clients.layer_artist.LayerArtist`` ``glue.clients.layer_artist.MatplotlibLayerArtist`` - ``glue.clients.profile_viewer`` ``glue.plugins.tools.spectrum_viewer.profile_viewer`` - ``glue.clients.scatter_client`` ``glue.viewers.scatter.client`` - ``glue.clients.tests.util.renderless_figure`` ``glue.utils.matplotlib.renderless_figure`` - ``glue.clients.util.small_view_array`` ``glue.core.util.small_view_array`` - ``glue.clients.util.small_view`` ``glue.core.util.small_view`` - ``glue.clients.util.tick_linker`` ``glue.core.util.tick_linker`` - ``glue.clients.util.update_ticks`` ``glue.core.util.update_ticks`` - ``glue.clients.util.visible_limits`` ``glue.core.util.visible_limits`` - ``glue.core.qt.simpleforms`` ``glue.core.qt.simpleforms`` - ``glue.core.util.CallbackMixin`` ``glue.utils.misc.CallbackMixin`` - ``glue.core.util.defer`` ``glue.utils.misc.defer`` - ``glue.core.util.Pointer`` ``glue.utils.misc.Pointer`` - ``glue.core.util.PropertySetMixin`` ``glue.utils.misc.PropertySetMixin`` - ``glue.qt.custom_viewer`` ``glue.viewers.custom.qt`` - ``glue.qt.data_slice_widget`` ``glue.viewers.common.qt.data_slice_widget`` - ``glue.qt.decorators`` ``glue.utils.qt.decorators`` - ``glue.qt.feedback.FeedbackWidget`` ``glue.app.qt.feedback.FeedbackWidget`` - ``glue.qt.glue_application.GlueApplication`` ``glue.app.qt.application.GlueApplication`` - ``glue.qt.glue_toolbar`` ``glue.viewers.common.qt.toolbar`` - ``glue.qt.layer_artist_model`` ``glue.core.qt.layer_artist_model`` - ``glue.qt.link_editor`` ``glue.dialogs.link_editor.qt`` - ``glue.qt.mime.PyMimeData`` ``glue.utils.qt.mime.PyMimeData`` - ``glue.qt.mime`` ``glue.core.qt.mime`` - ``glue.qt.mouse_mode`` ``glue.viewers.common.qt.mouse_mode`` - ``glue.qt.plugin_manager.QtPluginManager`` ``glue.app.qt.plugin_manager.QtPluginManager`` - ``glue.qt.qtutil.action`` ``glue.app.qt.actions.action`` - ``glue.qt.qtutil.cache_axes`` ``glue.utils.matplotlib.cache_axes`` - ``glue.qt.qtutil.cmap2pixmap`` ``glue.utils.qt.colors.cmap2pixmap`` - ``glue.qt.qtutil.ComponentIDCombo`` ``glue.core.qt.component_id_combo`` - ``glue.qt.qtutil.data_wizard`` ``glue.dialogs.data_wizard.qt.data_wizard`` - ``glue.qt.qtutil.get_text`` ``glue.utils.qt.dialogs.get_text`` - ``glue.qt.qtutil.GlueActionButton`` ``glue.app.qt.actions.GlueActionButton`` - ``glue.qt.qtutil.GlueItemWidget`` ``glue.utils.qt.mixins.GlueItemWidget`` - ``glue.qt.qtutil.GlueListWidget`` ``glue.core.qt.mime.GlueMimeListWidget`` - ``glue.qt.qtutil.GlueTabBar`` ``glue.utils.qt.helpers.GlueTabBar`` - ``glue.qt.qtutil.icon_path`` ``glue.icons.icon_path`` - ``glue.qt.qtutil.layer_artist_icon`` ``glue.icons.qt.layer_artist_icon`` - ``glue.qt.qtutil.layer_icon`` ``glue.icons.qt.layer_icon`` - ``glue.qt.qtutil.load_icon`` ``glue.icons.qt.load_icon`` - ``glue.qt.qtutil.mpl_to_qt4_color`` ``glue.utils.qt.colors.mpl_to_qt4_color`` - ``glue.qt.qtutil.pick_class`` ``glue.utils.qt.dialogs.pick_class`` - ``glue.qt.qtutil.pick_item`` ``glue.utils.qt.dialogs.pick_item`` - ``glue.qt.qtutil.pretty_number`` ``glue.utils.qt.PropertySetMixin`` - ``glue.qt.qtutil.PythonListModel`` ``glue.utils.qt.python_list_model.PythonListModel`` - ``glue.qt.qtutil.qt4_to_mpl_color`` ``glue.utils.qt.colors.qt4_to_mpl_color`` - ``glue.qt.qtutil.RGBEdit`` ``glue.viewers.image.qt.rgb_edit.RGBEdit`` - ``glue.qt.qtutil.symbol_icon`` ``glue.icons.qt.symbol_icon`` - ``glue.qt.qtutil.tint_pixmap`` ``glue.utils.qt.colors.tint_pixmap`` - ``glue.qt.qtutil.update_combobox`` ``glue.utils.qt.helpers.update_combobox`` - ``glue.qt.qtutil.Worker`` ``glue.utils.qt.threading.Worker`` - ``glue.qt.qt_backend.Timer`` ``glue.backends.QtTimer`` - ``glue.qt.qt_roi`` ``glue.core.qt.roi`` - ``glue.qt.widgets.custom_component_widget`` ``glue.dialogs.custom_component.qt`` - ``glue.qt.widgets.glue_mdi_area`` ``glue.app.qt.mdi_area`` - ``glue.qt.widgets.histogram_widget`` ``glue.viewers.histogram.qt`` - ``glue.qt.widgets.histogram_widget`` ``glue.viewers.image.qt`` - ``glue.qt.widgets.layer_tree_widget`` ``glue.app.qt.layer_tree_widget`` - ``glue.qt.widgets.message_widget`` ``glue.core.qt.message_widget`` - ``glue.qt.widgets.MplWidget`` ``glue.viewers.common.qt.mpl_widget.MplWidget`` - ``glue.qt.widgets.mpl_widget`` ``glue.viewers.common.qt.mpl_widget`` - ``glue.qt.widgets.scatter_widget`` ``glue.viewers.scatter.qt`` - ``glue.qt.widgets.settings_editor`` ``glue.app.qt.settings_editor`` - ``glue.qt.widgets.style_dialog`` ``glue.core.qt.style_dialog`` - ``glue.qt.widgets.subset_facet`` ``glue.dialogs.subset_facet.qt`` - ``glue.qt.widgets.table_widget`` ``glue.viewers.table.qt`` - ``glue.qt.widgets.terminal`` ``glue.app.qt.terminal`` -==================================================== =============================================================== - -If it would be helpful for us to add back backward-compatibility for any of -these, please let us know! \ No newline at end of file diff --git a/doc/whatsnew/aspect_auto.png b/doc/whatsnew/aspect_auto.png deleted file mode 100644 index 453b8beaf..000000000 Binary files a/doc/whatsnew/aspect_auto.png and /dev/null differ diff --git a/doc/whatsnew/aspect_combo.png b/doc/whatsnew/aspect_combo.png deleted file mode 100644 index d121a76f0..000000000 Binary files a/doc/whatsnew/aspect_combo.png and /dev/null differ diff --git a/doc/whatsnew/aspect_square.png b/doc/whatsnew/aspect_square.png deleted file mode 100644 index 597a267fe..000000000 Binary files a/doc/whatsnew/aspect_square.png and /dev/null differ diff --git a/doc/whatsnew/cube_playback_controls.png b/doc/whatsnew/cube_playback_controls.png deleted file mode 100644 index 550746b38..000000000 Binary files a/doc/whatsnew/cube_playback_controls.png and /dev/null differ diff --git a/doc/whatsnew/cube_slider.png b/doc/whatsnew/cube_slider.png deleted file mode 100644 index 648960ab7..000000000 Binary files a/doc/whatsnew/cube_slider.png and /dev/null differ diff --git a/doc/whatsnew/feedback.png b/doc/whatsnew/feedback.png deleted file mode 100644 index f4a0bc22e..000000000 Binary files a/doc/whatsnew/feedback.png and /dev/null differ diff --git a/doc/whatsnew/images/v.132/arithmetic_button.png b/doc/whatsnew/images/v.132/arithmetic_button.png deleted file mode 100644 index 241d63493..000000000 Binary files a/doc/whatsnew/images/v.132/arithmetic_button.png and /dev/null differ diff --git a/doc/whatsnew/images/v.132/arithmetic_equation.png b/doc/whatsnew/images/v.132/arithmetic_equation.png deleted file mode 100644 index c47e685bf..000000000 Binary files a/doc/whatsnew/images/v.132/arithmetic_equation.png and /dev/null differ diff --git a/doc/whatsnew/images/v.132/arithmetic_main.png b/doc/whatsnew/images/v.132/arithmetic_main.png deleted file mode 100644 index 9edac7335..000000000 Binary files a/doc/whatsnew/images/v.132/arithmetic_main.png and /dev/null differ diff --git a/doc/whatsnew/images/v.132/export_data.png b/doc/whatsnew/images/v.132/export_data.png deleted file mode 100644 index dbc28f548..000000000 Binary files a/doc/whatsnew/images/v.132/export_data.png and /dev/null differ diff --git a/doc/whatsnew/images/v.132/link_editor.png b/doc/whatsnew/images/v.132/link_editor.png deleted file mode 100644 index bae1b218c..000000000 Binary files a/doc/whatsnew/images/v.132/link_editor.png and /dev/null differ diff --git a/doc/whatsnew/images/v.132/organize_components.png b/doc/whatsnew/images/v.132/organize_components.png deleted file mode 100644 index de635d2b5..000000000 Binary files a/doc/whatsnew/images/v.132/organize_components.png and /dev/null differ diff --git a/doc/whatsnew/images/v.132/profile_single.png b/doc/whatsnew/images/v.132/profile_single.png deleted file mode 100644 index 38b01e302..000000000 Binary files a/doc/whatsnew/images/v.132/profile_single.png and /dev/null differ diff --git a/doc/whatsnew/images/v.132/profile_single_pixel.png b/doc/whatsnew/images/v.132/profile_single_pixel.png deleted file mode 100644 index c178a1727..000000000 Binary files a/doc/whatsnew/images/v.132/profile_single_pixel.png and /dev/null differ diff --git a/doc/whatsnew/images/v.132/profile_with_image.png b/doc/whatsnew/images/v.132/profile_with_image.png deleted file mode 100644 index d2005be85..000000000 Binary files a/doc/whatsnew/images/v.132/profile_with_image.png and /dev/null differ diff --git a/doc/whatsnew/images/v.132/scatter_density.png b/doc/whatsnew/images/v.132/scatter_density.png deleted file mode 100644 index 5cfa7c727..000000000 Binary files a/doc/whatsnew/images/v.132/scatter_density.png and /dev/null differ diff --git a/doc/whatsnew/images/v.132/scatter_density_color.png b/doc/whatsnew/images/v.132/scatter_density_color.png deleted file mode 100644 index d52260001..000000000 Binary files a/doc/whatsnew/images/v.132/scatter_density_color.png and /dev/null differ diff --git a/doc/whatsnew/images/v.132/scatter_density_color2.png b/doc/whatsnew/images/v.132/scatter_density_color2.png deleted file mode 100644 index a6031dea2..000000000 Binary files a/doc/whatsnew/images/v.132/scatter_density_color2.png and /dev/null differ diff --git a/doc/whatsnew/images/v0.10/link_window.png b/doc/whatsnew/images/v0.10/link_window.png deleted file mode 100644 index 14f9aa432..000000000 Binary files a/doc/whatsnew/images/v0.10/link_window.png and /dev/null differ diff --git a/doc/whatsnew/images/v0.11/application_ui.jpg b/doc/whatsnew/images/v0.11/application_ui.jpg deleted file mode 100644 index 07631a990..000000000 Binary files a/doc/whatsnew/images/v0.11/application_ui.jpg and /dev/null differ diff --git a/doc/whatsnew/images/v0.11/image_rgb.jpg b/doc/whatsnew/images/v0.11/image_rgb.jpg deleted file mode 100644 index 8f4178545..000000000 Binary files a/doc/whatsnew/images/v0.11/image_rgb.jpg and /dev/null differ diff --git a/doc/whatsnew/images/v0.11/plugin_geospatial.jpg b/doc/whatsnew/images/v0.11/plugin_geospatial.jpg deleted file mode 100644 index a59e888e7..000000000 Binary files a/doc/whatsnew/images/v0.11/plugin_geospatial.jpg and /dev/null differ diff --git a/doc/whatsnew/images/v0.11/plugin_wwt.jpg b/doc/whatsnew/images/v0.11/plugin_wwt.jpg deleted file mode 100644 index a14f00dfc..000000000 Binary files a/doc/whatsnew/images/v0.11/plugin_wwt.jpg and /dev/null differ diff --git a/doc/whatsnew/images/v0.11/scatter_color_size.jpg b/doc/whatsnew/images/v0.11/scatter_color_size.jpg deleted file mode 100644 index 7f9db3a1d..000000000 Binary files a/doc/whatsnew/images/v0.11/scatter_color_size.jpg and /dev/null differ diff --git a/doc/whatsnew/images/v0.11/scatter_error.jpg b/doc/whatsnew/images/v0.11/scatter_error.jpg deleted file mode 100644 index c086b361c..000000000 Binary files a/doc/whatsnew/images/v0.11/scatter_error.jpg and /dev/null differ diff --git a/doc/whatsnew/images/v0.12/smooth.png b/doc/whatsnew/images/v0.12/smooth.png deleted file mode 100644 index 70ba91ecc..000000000 Binary files a/doc/whatsnew/images/v0.12/smooth.png and /dev/null differ diff --git a/doc/whatsnew/images/v0.12/vectors.png b/doc/whatsnew/images/v0.12/vectors.png deleted file mode 100644 index 1fdeaa900..000000000 Binary files a/doc/whatsnew/images/v0.12/vectors.png and /dev/null differ diff --git a/doc/whatsnew/images/v0.13/arithmetic_button.png b/doc/whatsnew/images/v0.13/arithmetic_button.png deleted file mode 100644 index c5e06c987..000000000 Binary files a/doc/whatsnew/images/v0.13/arithmetic_button.png and /dev/null differ diff --git a/doc/whatsnew/images/v0.13/arithmetic_equation.png b/doc/whatsnew/images/v0.13/arithmetic_equation.png deleted file mode 100644 index 26326188e..000000000 Binary files a/doc/whatsnew/images/v0.13/arithmetic_equation.png and /dev/null differ diff --git a/doc/whatsnew/images/v0.13/arithmetic_main.png b/doc/whatsnew/images/v0.13/arithmetic_main.png deleted file mode 100644 index 249d5d5fe..000000000 Binary files a/doc/whatsnew/images/v0.13/arithmetic_main.png and /dev/null differ diff --git a/doc/whatsnew/images/v0.13/export_data.png b/doc/whatsnew/images/v0.13/export_data.png deleted file mode 100644 index e0a9289a4..000000000 Binary files a/doc/whatsnew/images/v0.13/export_data.png and /dev/null differ diff --git a/doc/whatsnew/images/v0.13/export_data_button.png b/doc/whatsnew/images/v0.13/export_data_button.png deleted file mode 100644 index 7d09e5d55..000000000 Binary files a/doc/whatsnew/images/v0.13/export_data_button.png and /dev/null differ diff --git a/doc/whatsnew/images/v0.13/link_editor.png b/doc/whatsnew/images/v0.13/link_editor.png deleted file mode 100644 index b103d6a94..000000000 Binary files a/doc/whatsnew/images/v0.13/link_editor.png and /dev/null differ diff --git a/doc/whatsnew/images/v0.13/metadata.png b/doc/whatsnew/images/v0.13/metadata.png deleted file mode 100644 index 506789b2d..000000000 Binary files a/doc/whatsnew/images/v0.13/metadata.png and /dev/null differ diff --git a/doc/whatsnew/images/v0.13/organize_components.png b/doc/whatsnew/images/v0.13/organize_components.png deleted file mode 100644 index 03ca16eda..000000000 Binary files a/doc/whatsnew/images/v0.13/organize_components.png and /dev/null differ diff --git a/doc/whatsnew/images/v0.13/profile_icon.png b/doc/whatsnew/images/v0.13/profile_icon.png deleted file mode 100644 index f042bc029..000000000 Binary files a/doc/whatsnew/images/v0.13/profile_icon.png and /dev/null differ diff --git a/doc/whatsnew/images/v0.13/profile_single_pixel.png b/doc/whatsnew/images/v0.13/profile_single_pixel.png deleted file mode 100644 index b73d1d44c..000000000 Binary files a/doc/whatsnew/images/v0.13/profile_single_pixel.png and /dev/null differ diff --git a/doc/whatsnew/images/v0.13/profile_with_image.png b/doc/whatsnew/images/v0.13/profile_with_image.png deleted file mode 100644 index 9471ab83a..000000000 Binary files a/doc/whatsnew/images/v0.13/profile_with_image.png and /dev/null differ diff --git a/doc/whatsnew/images/v0.13/reprojection.jpg b/doc/whatsnew/images/v0.13/reprojection.jpg deleted file mode 100644 index 4a00e87f9..000000000 Binary files a/doc/whatsnew/images/v0.13/reprojection.jpg and /dev/null differ diff --git a/doc/whatsnew/images/v0.13/scatter_density.png b/doc/whatsnew/images/v0.13/scatter_density.png deleted file mode 100644 index 590a65491..000000000 Binary files a/doc/whatsnew/images/v0.13/scatter_density.png and /dev/null differ diff --git a/doc/whatsnew/images/v0.13/scatter_density_color.png b/doc/whatsnew/images/v0.13/scatter_density_color.png deleted file mode 100644 index ca6e19e5c..000000000 Binary files a/doc/whatsnew/images/v0.13/scatter_density_color.png and /dev/null differ diff --git a/doc/whatsnew/images/v0.14/active_subset.png b/doc/whatsnew/images/v0.14/active_subset.png deleted file mode 100644 index 52a4ccf72..000000000 Binary files a/doc/whatsnew/images/v0.14/active_subset.png and /dev/null differ diff --git a/doc/whatsnew/images/v0.14/computing.png b/doc/whatsnew/images/v0.14/computing.png deleted file mode 100644 index dc76ef74c..000000000 Binary files a/doc/whatsnew/images/v0.14/computing.png and /dev/null differ diff --git a/doc/whatsnew/images/v0.15/autolink.png b/doc/whatsnew/images/v0.15/autolink.png deleted file mode 100644 index 79ea87ae0..000000000 Binary files a/doc/whatsnew/images/v0.15/autolink.png and /dev/null differ diff --git a/doc/whatsnew/images/v0.15/autolink_details.png b/doc/whatsnew/images/v0.15/autolink_details.png deleted file mode 100644 index 39129cc2f..000000000 Binary files a/doc/whatsnew/images/v0.15/autolink_details.png and /dev/null differ diff --git a/doc/whatsnew/images/v0.15/complex_links.png b/doc/whatsnew/images/v0.15/complex_links.png deleted file mode 100644 index f47295903..000000000 Binary files a/doc/whatsnew/images/v0.15/complex_links.png and /dev/null differ diff --git a/doc/whatsnew/images/v0.9/3d_viewers.png b/doc/whatsnew/images/v0.9/3d_viewers.png deleted file mode 100644 index 9b12ac920..000000000 Binary files a/doc/whatsnew/images/v0.9/3d_viewers.png and /dev/null differ diff --git a/doc/whatsnew/images/v0.9/plotly_exporter.png b/doc/whatsnew/images/v0.9/plotly_exporter.png deleted file mode 100644 index d5a761977..000000000 Binary files a/doc/whatsnew/images/v0.9/plotly_exporter.png and /dev/null differ diff --git a/doc/whatsnew/images/v0.9/spectral_cube_import.png b/doc/whatsnew/images/v0.9/spectral_cube_import.png deleted file mode 100644 index 39b579679..000000000 Binary files a/doc/whatsnew/images/v0.9/spectral_cube_import.png and /dev/null differ diff --git a/doc/whatsnew/images/v0.9/table_viewer.png b/doc/whatsnew/images/v0.9/table_viewer.png deleted file mode 100644 index be17c40db..000000000 Binary files a/doc/whatsnew/images/v0.9/table_viewer.png and /dev/null differ diff --git a/doc/whatsnew/images/v0.9/world_slicing.png b/doc/whatsnew/images/v0.9/world_slicing.png deleted file mode 100644 index 35edf467f..000000000 Binary files a/doc/whatsnew/images/v0.9/world_slicing.png and /dev/null differ diff --git a/doc/whatsnew/images/v1.0.0/legend_control.png b/doc/whatsnew/images/v1.0.0/legend_control.png deleted file mode 100644 index c4c61f265..000000000 Binary files a/doc/whatsnew/images/v1.0.0/legend_control.png and /dev/null differ diff --git a/doc/whatsnew/images/v1.0.0/legend_example.png b/doc/whatsnew/images/v1.0.0/legend_example.png deleted file mode 100644 index b38c4ab82..000000000 Binary files a/doc/whatsnew/images/v1.0.0/legend_example.png and /dev/null differ diff --git a/doc/whatsnew/images/v1.0.0/polar.png b/doc/whatsnew/images/v1.0.0/polar.png deleted file mode 100644 index d9c96c740..000000000 Binary files a/doc/whatsnew/images/v1.0.0/polar.png and /dev/null differ diff --git a/doc/whatsnew/layer_options.png b/doc/whatsnew/layer_options.png deleted file mode 100644 index b2fff1efb..000000000 Binary files a/doc/whatsnew/layer_options.png and /dev/null differ diff --git a/doc/whatsnew/new_component.png b/doc/whatsnew/new_component.png deleted file mode 100644 index bdeebbdc0..000000000 Binary files a/doc/whatsnew/new_component.png and /dev/null differ diff --git a/doc/whatsnew/plugin_manager.png b/doc/whatsnew/plugin_manager.png deleted file mode 100644 index 4e52c9cb0..000000000 Binary files a/doc/whatsnew/plugin_manager.png and /dev/null differ diff --git a/doc/whatsnew/preferences.png b/doc/whatsnew/preferences.png deleted file mode 100644 index 99b67ced2..000000000 Binary files a/doc/whatsnew/preferences.png and /dev/null differ diff --git a/doc/whatsnew/whatsnew.rst b/doc/whatsnew/whatsnew.rst deleted file mode 100644 index e55b8de2e..000000000 --- a/doc/whatsnew/whatsnew.rst +++ /dev/null @@ -1,1418 +0,0 @@ -:orphan: - -.. _whatsnew: - -******************* -What's new in glue? -******************* - -On this page you will find information about significant changes in each -release. Many smaller improvements and bug fixes are not mentioned here but can be -found in the full list of `CHANGES.md -`_ file if you are -interested. - -Before we get started, here's a reminder on how to install/update glue. You can -easily update glue if you are using Anaconda/Miniconda by doing:: - - conda install -c glueviz glueviz=1.2 - -If instead you installed glue with pip, you can update with:: - - pip install glueviz[all] --upgrade - -If you have any questions about any of the functionality described below or -about glue in general, you can find information :ref:`here -` about contacting us and/or -reporting issues. - -.. _whatsnew_120: - -What's new in glue v1.2.0? -========================== - -Glue v1.2.0 is a maintenance release that provides performance improvements and -incremental improvements in the programmatic API. - -.. _whatsnew_110: - -What's new in glue v1.1.0? -========================== - -Glue v1.1.0 is a maintenance release that fixes support with recent versions of -dependencies and drops support for Python 3.6. Python 3.7 or later is now required -to run glue. - -.. _whatsnew_100: - -What's new in glue v1.0.x? -========================== - -Semantic versioning -------------------- - -Glue has been developed since 2011, and in the last few years we have focused on -keeping the application as stable as possible for existing users while adding -new functionality. The use of version numbers such as **0.15.6** is therefore no -longer appropriate since the **0** major version is typically used for early -development when many things can still change significantly. - -To better reflect the current development stage, and because the programmatic -interface for glue is used by a number of other packages and user scripts, we -therefore have chosen to call the present release **1.0.0**. We are adopting `semantic -versioning `_ (with version numbers of the form -major.minor.bugfix) to better distinguish between releases that break the -programmatic interface or have significant changes to the user interface (for -which we would increase the major number), releases that add new functionality -without changing existing functionality (for which we would increase the minor -number), and releases that just fix bugs (for which we would increase the bugfix -number). - -To put it another way, the 1.0.0 version number does not mean that there will no -longer be significant enhancements or updates to glue, but rather that the -version numbers will now give a better indication of the scope of changes -between versions in future. - -Updated minimum Python requirement ----------------------------------- - -With this release, we are focusing development on supporting Python 3.6 and -above, and glue will no longer work for Python 2.7 and 3.5. If you are using -these Python versions and are unable to upgrade, you will need to continue using -the 0.15.x releases of glue. - -Legends -------- - -Legends can now be displayed for the built-in Matplotlib viewers: - -.. image:: images/v1.0.0/legend_example.png - :align: center - :width: 500 - -The legend in a viewer can be customized using a new tab in the plot options widget: - -.. image:: images/v1.0.0/legend_control.png - :align: center - :width: 200 - -The export scripts for the default Matplotlib viewers have also been updated to -draw the legend if it is activated in the user interface. Otherwise, it is -commented out so the user can easily activate the legend if needed. - -Support for polar and other non-rectilinear projections in 2D Scatter Viewer ----------------------------------------------------------------------------- - -The 2-d scatter plot now includes experimental support for non-rectilinear -projections, such as polar plots or Aitoff projections. The following shows -the 2D scattter plot in polar plot mode: - -.. image:: images/v1.0.0/polar.png - :align: center - :width: 500 - -Replace mode when creating a subset ------------------------------------ - -When creating a new subset, the selection mode is now automatically changed back -to 'replace' if it was changed previously, since there is no reason for the mode -to be one of the logical combinations when creating a new subset from scratch. - -Export Python scripts (1D Profile viewer) ------------------------------------------ - -The 1D Profile viewer now allows exporting of Python scripts, in line with the other -built-in Matplotlib viewers - -Performance improvements ------------------------- - -The performance of the image viewer has been impoved in the case where some of the -coordinate axes are uncorrelated. In addition, the 1-d profile viewer should now -also be signficantly faster when showing the collapsed profiles for selections made in -the image viewer. - -Changes to coordinate classes [advanced] ----------------------------------------- - -The infrastructure to handle world coordinates has been refactored - while this -should make no difference for users using exclusively the user interface, if you -have written custom code that makes use of ``Data.coords`` you may need to -update your code. The biggest change is that the ``pixel2world`` and -``world2pixel`` methods have now been renamed to ``pixel_to_world_values`` and -``world_to_pixel_values``. The latest changes were to bring the glue API in line -with the recommended API for world coordinate systems described in `A shared -Python interface for World Coordinate Systems -`_. Any object -conforming to that API can now be used directly as a ``Data.coords`` object. - -Initial support for dask arrays [advanced] ------------------------------------------- - -When adding `dask arrays `_ to glue -:class:`~glue.core.data.Data` objects, glue will no longer cause the whole dask -array to be loaded into memory. The image viewer in particular will now only -access the required and relevant parts of the data. - -.. _whatsnew_015: - -What's new in glue v0.15? -========================= - -New and improved link editor ----------------------------- - -The bottom panel of the link editor has now been re-written to make it easier to -edit existing links, and significantly improves the interface for links more -complex than identity links. - -.. image:: images/v0.15/complex_links.png - :align: center - :width: 600 - -Reprojection of raster data in 3-d volume viewer ------------------------------------------------- - -The 3-d volume viewer now supports on-the-fly reprojection of raster data in -the same way as the 2-d image viewer. This means that if two 3-d datasets are -linked, they can be shown at the same time in the 3-d volume viewer even if they -are not defined on the same pixel grid. - -No more merging suggestions ---------------------------- - -Glue will no longer prompt you to merge multiple datasets into a single one -automatically - instead this is something that you should now do manually by -selecting the datasets to merge, then right-clicking (or control-clicking) and -selecting **Merge datasets**. We decided to not suggest this by default anymore -since links are the preferred way of using multiple datasets together. - -Improvements to Table viewer ----------------------------- - -When using the table viewer, you can now choose to show only one or more -subsets of the data in the viewer without having to show the full table. You -can control this by using the checkboxes in the layer list as for other -viewers. - -Improvements when showing multiple images in the image viewer -------------------------------------------------------------- - -When showing multiple datasets in the image viewer, the images are now no -longer downsampled to the resolution of the reference data and clipped to its -field of view. The reference data is still used to define the coordinate system -in which to show the data, but all datasets are now resampled to the resolution -of the screen. - -New coordinate class for affine transformations ------------------------------------------------ - -We have now written a page on :ref:`coordinates` and have added a new class -to deal with common affine transformations:: - - >>> import numpy as np - >>> from glue.core import Data - >>> from glue.core.coordinates import AffineCoordinates - >>> matrix = np.array([[2, 0, 0], [0, 2, 0], [0, 0, 1]]) - >>> affine_coords = AffineCoordinates(matrix, units=['m', 'm'], labels=['xw', 'yw']) - >>> data = Data(x=[1, 2, 3], coords=affine_coords) - -You can read more about this in :ref:`affine-coordinates`. - -New auto-linking functionality ------------------------------- - -We have now set up an infrastructure to make it possible to define plugins that -can automatically suggest links, and have included such a plugin for linking -astronomical images and higher dimensional datasets (note that this -functionality requires astropy 3.1 or later). To write such a plugin, you will -need to define a Python function that takes a data collection and then returns a -list of links, e.g.:: - - from glue.config import autolinker - - @autolinker('Auto-linker name') - def my_autolinker(data_collection): - ... - return links - -When adding datasets, glue will then automatically check whether links can be -added between datasets and show a dialog to ask whether to apply the -suggestions: - -.. image:: images/v0.15/autolink.png - :align: center - :width: 600 - -with the option to see the details: - -.. image:: images/v0.15/autolink_details.png - :align: center - :width: 600 - -For more information, see :ref:`custom-auto-link`. - -.. _whatsnew_014: - -What's new in glue v0.14? -========================= - -Active subset in menu bar -------------------------- - -The active subset, which is the subset that would be modified during the next -selection, is now shown in the toolbar: - -.. image:: images/v0.14/active_subset.png - :align: center - :width: 500 - -Prior to this, the only way to set the active subset was to select a subset -in the data collection list on the left, or deselect all subsets to create a new -one. The drop-down menu in the toolbar should make the process of choosing the -subset to modify (or choosing to create a new subset) more straightforward. - -Documentation about plugins ---------------------------- - -We have now added a page to the documentation which provides a :ref:`list of -available plugins `, as well a page describing how to -:ref:`write your own plugin package ` - -Changes in behavior -------------------- - -There are a couple of changes in behavior in glue that we want to mention here: - -* The first is that when saving session files, the default has now changed to - be to include relative paths to the data files (as opposed to absolute paths). - -* The second is that when accessing string components in datasets, you will - now get back a specialized Numpy array class that behaves like an array of - strings, but also has a ``.codes`` attribute to get the numerical category for - each value, as well as a ``.categories`` attribute to get the unique - categories:: - - >>> from glue.core import Data - >>> d = Data(x=['a', 'b', 'c', 'a', 'a']) - >>> d['x'] - categorical_ndarray(['a', 'b', 'c', 'a', 'a'], dtype='>> d['x'].codes - array([0., 1., 2., 0., 0.]) - >>> d['x'].categories - array(['a', 'b', 'c'], dtype=object) - - In previous versions, ``d['x']`` returns the numerical ``.codes`` directly - but it was not easy to access the original labels. - -Performance ------------ - -This release includes a number of significant performance enhancements (both -in terms of speed and memory usage). In particular, the histogram and profile -viewers have been updated to be able to compute histograms and profiles in the -background without causing the rest of the application to hang - in this case -the viewers will look like this while they are updated: - -.. image:: images/v0.14/computing.png - :align: center - :width: 500 - -Documentation about creating custom viewers [advanced] ------------------------------------------------------- - -We have now written extensive documentation on writing fully customized -viewers for glue. The new pages available are: - -* :ref:`state-viewer` -* :ref:`state-qt-viewer` -* :ref:`matplotlib-qt-viewer` - -This is intended for developers working on plugin packages that define new -viewers. If you are a user and want to make Matplotlib-based custom viewers, -we recommend that you start off with the existing :ref:`simple-custom-viewer` -page. - -Abstract data classes [advanced] --------------------------------- - -By default, data objects in glue are instances of the Data class, and this class -assumes that the data are stored in one or more local n-dimensional arrays. -However, glue now includes a way of defining a wider variety of data objects, -which may rely for example on large remote datasets, or datasets that are not -inherently stored as regular n-dimensional arrays. We have written up -documentation on :ref:`basedata`. - -Better isolation of Qt code [advanced] --------------------------------------- - -The code related to the data viewers has been significantly re-organized to -split out as much as possible of the code to make it non-Qt-specific. This will -enable other interfaces, such as Jupyter widgets, to re-use as much of the code -as possible. - -.. _whatsnew_013: - -What's new in glue v0.13? -========================= - -Scatter density maps --------------------- - -The 2D scatter viewer can now show data using a density map rather than individual -markers - this makes it possible to now plot tens to hundreds of millions of -points efficiently. - -.. image:: images/v0.13/scatter_density.png - :align: center - :width: 500 - -Whether data is shown as a density map or markers can be controlled for each -layer, and by default glue will automatically decide which one to use depending -on the size of the data. It is also possible to color-code points by a third -variable as for markers: - -.. image:: images/v0.13/scatter_density_color.png - :align: center - :width: 500 - -On-the-fly reprojection ------------------------ - -Previously, the 2D image viewer only allowed multiple datasets to be shown at -the same time if all pixel coordinates were linked between the different -datasets. The image viewer will now automatically overlay data even if it is -linked by world coordinates instead of pixel coordinates: - -.. image:: images/v0.13/reprojection.jpg - :align: center - :width: 500 - -For astronomers: this means being able to overplot images and cubes with -different WCS transformations, provided that you have set up links between the -world coordinates - and this includes being able to show 2D images and 3D -spectral cubes at the same time (if you do this, note that the reference data -needs to be the dataset you want to slice over, i.e. the cube, not the 2D image). - -Arithmetic attribute editor ---------------------------- - -Glue has long had the ability to define so-called 'arithmetic' -attributes/components, which are data attributes that depend on other data -attributes via an arithmetic expression. However, editing or removing existing -arithmetic attributes was not possible. We have now re-written the dialog for -arithmetic attributes to make it possible to go back and edit existing -expressions or remove unneeded arithmetic attributes: - -.. image:: images/v0.13/arithmetic_main.png - :align: center - :width: 400 - -and the equation editor itself has also been improved: - -.. image:: images/v0.13/arithmetic_equation.png - :align: center - :width: 400 - -The arithmetic attribute editor is accessible via a new toolbar button: - -.. image:: images/v0.13/arithmetic_button.png - :align: center - :width: 300 - -Organize data attributes ------------------------- - -We have also added a new dialog that can be used to rename or reorder existing -attributes in the data. In future, this dialog will be used to also change the -types of attributes (for example to indicate that an attribute should be -interpreted as a time): - -.. image:: images/v0.13/organize_components.png - :align: center - :width: 400 - -This dialog can be found in the **Data Manager** menu as **Reorder/rename data -attributes**. - -Export data/subset dialog -------------------------- - -While exporting datasets and subsets has been possible in the past by -control-clicking on datasets/subsets in the data collection view in the top left -of the glue window, we have now added a new **Export Data/Subsets** dialog which -provides a more intuitive interface for exporting data and subsets: - -.. image:: images/v0.13/export_data.png - :align: center - :width: 350 - -This dialog is accessible via the **Export Data/Subsets** button in the toolbar: - -.. image:: images/v0.13/export_data_button.png - :align: center - :width: 180 - -Metadata explorer ------------------ - -Data objects have a ``.meta`` attribute that can be used to store arbitrary -metadata. For example for FITS files (in astronomy), this contains the header of -the file. We have now added the ability to visualize this metadata: - -.. image:: images/v0.13/metadata.png - :align: center - :width: 350 - -To view the metadata for a given dataset, control-click on the dataset in the -top left data collection view and select **View metadata/header**. - -New link editor ---------------- - -The ability to link datasets is one of the core pieces of functionality in glue. -However, when dealing with more than a few datasets, the list of existing links -was previously difficult to conceptualize. We have now improved the link editor -to include a visualization all links between datasets, and to make it so that -links are only listed for the currently selected datasets. To set up one or more -links between two datasets, you can select both datasets in the graph then add -links below. You can also edit existing links between two datasets by clicking -on the line connecting them: - -.. image:: images/v0.13/link_editor.png - :align: center - :width: 600 - -Profile viewer --------------- - -Glue now features a new profile viewer that can be used to show data collapsed -along all but one dimension using a variety of functions (mean, median, maximum, -minimum, and so on). This new viewer replaces the previous 'spectrum' tool -(which was restricted to 3 dimensions and mostly designed to work with -astronomical data) and includes the same functionality to fit models to profiles -or collapse data in an image viewer based on an interval selected in the profile -viewer. The new profile viewer makes it possible to visualize the profile for -subsets as well as for the whole dataset. - -.. image:: images/v0.13/profile_with_image.png - :align: center - :width: 600 - -To create a profile viewer, either click on the profile icon (|profile_icon|) in -an image viewer, or drag a dataset onto the main canvas and select **1D -Profile**. - -.. |profile_icon| image:: images/v0.13/profile_icon.png - -Single-pixel extraction tool ----------------------------- - -We have also added a new subset selection mode in the image viewer which is to -select a single pixel in the image plane. When used in conjunction with the -profile viewer, this makes it possible to hover over an image and see the -profile (e.g. spectrum) at the current mouse position: - -.. image:: images/v0.13/profile_single_pixel.png - :align: center - :width: 600 - -Export Python scripts ---------------------- - -While it has been possible for a while to export plots to e.g. PNG, PDF, or EPS -files from different image viewers it is now possible to export a Python script -that can be used to reproduce the plot for some of the core viewers (including -the 2D scatter and image viewers and the histogram viewer). To use this -functionality, click on the **Save** icon and select **Save Python script to -reproduce plot**. This functionality is still experimental, and will be extended -to more data viewers in future. - -Datetime64 support ------------------- - -Datasets that include attributes with a Numpy ``datetime64`` dtype will now -be recognized by the scatter and histogram viewers, which will correctly format -the axis labels using dates/times. In future we will make it possible to use -this functionality to format dates/times read from files. - -Relative paths in session files -------------------------------- - -When exporting a session file from glue, it is now possible to select to refer -to the data files using relative paths rather than absolute paths. The relative -paths are determine relative to the location of the session file. This makes it -easier to send small session files to other users who have the same data already -on disk. - -Mouse interaction with subsets in the image viewer --------------------------------------------------- - -It is now possible to select and manipulate subsets in the image viewer using -the mouse. Previously, only newly created subsets could be resized and -relocated. Now it is possible to relocate any subset simply by left-clicking on -it and dragging it to a new location. Right-clicking on an existing subset opens -a context menu with an option for deleting that subset. Note that for now this -only works with the image viewer but in future we will generalize this to other -viewers. - -Performance ------------ - -This release includes a number of significant performance enhancements (both -in terms of speed and memory usage). The linking infrastructure has been -refactored to be much more efficient (previously, linking more than a dozen -datasets together could lead to significant performance issues), and the -propagation of selections across datasets in the presence of linking functions -has also been improved. Reading HDF5 files will now use memory mapping when -possible to avoid loading all the data into memory, and 3D selections are -represented in a much more memory-efficient way. - -PySide2 -------- - -Glue 0.13 is now compatible with the `PySide2 -`_ library (the Python bindings for Qt5 -developed by the Qt company). Since PySide2 was still in pre-release at the time -of writing, we recommend using Glue with PyQt5 for now. In parallel with this, -support for PyQt4 and PySide has now been removed from glue. - -.. _whatsnew_012: - -What's new in glue v0.12? -========================= - -Improved 2D scatter viewer --------------------------- - -It is now possible to show arrows/vectors in the **2D Scatter** viewer: - -.. image:: images/v0.12/vectors.png - :align: center - :width: 600 - -To use this, see the new **Vectors** tab below the layer list when using the 2D -Scatter viewer. In addition, we have made a number of improvements to the user -interface of this viewer to make it easier to enable/disable different -visualizations (markers, lines, errorbars, and vectors). - -User interface improvements ---------------------------- - -We have made a number of improvements to the user interface and general -usability. In particular, selecting disabled layers in a viewer will now give a -clear explanation that the layer is disabled and will give possible causes, -rather than relying solely on visual cues (such as graying out a layer). - -Custom startup actions ----------------------- - -It is now possible to define custom startup actions to execute when glue starts -up by writing simple Python functions in a config.py file or in plugin packages. -Startup actions are executed once glue is open and any data provided on the -command-line (if any) has been opened. Find out more about :ref:`writing custom -startup actions `. - -Custom data/subset actions --------------------------- - -It is now possible to specify custom actions that can be accessed by -right/control-clicking on a dataset or subset, using :ref:`the new -@layer_action ` decorator. The following screenshot shows -an example of a custom action added by a user to smooth data in a particular -way: - -.. image:: images/v0.12/smooth.png - :align: center - :width: 400 - -Experimental SAMP plugin ------------------------- - -A number of Astronomy applications including `TOPCAT -`_, `Aladin -`_, `DS9 `_, -`WorldWideTelescope `_ and more support a -message-passing system that allows data and subsets to be exchanged between -applications. We have now developed a plugin that adds SAMP capabilities to -glue. To try this out, you will need to install the `glue-samp -`_ package, using:: - - conda install -c glueviz glue-samp - -if you use conda, or:: - - pip install glue-samp - -otherwise. To use this, once inside glue, go to the **Plugins** menu, then -select **Open SAMP plugin**. The window that open will provide information on -using this plugin. Note however that this plugin is experimental and is still -missing a number of features. In addition, SAMP implementation in other -applications is sometimes buggy, so you may encounter issues unrelated to glue. - -Improved performance in 3D viewers ----------------------------------- - -The **3D Volume Rendering** is now significantly faster for large datasets. In -particular, the resolution of the rendering is now reduced when rotating or -zooming, and we have made it so that the viewer is now much more efficient in -terms of memory. - -Experimental fixed layout/dashboards [advanced] ------------------------------------------------ - -By default, glue uses a free-form canvas to contain viewers, which gives you the -ability to arrange the data viewers in any way you like. However, for certain -applications, it can be helpful to defined tabs that contain data viewers in a -predefined layout. :ref:`This is now possible `, although note -that this is still an experimental feature and requires some knowledge of how to -set up Qt widgets. In future, we will make it possible to define layouts in a -more intuitive way. - -Full list of Changes --------------------- - -In addition to the above features, a number of bugs has been fixed since the -last release, and a few other small features have been added. A full list of -changes can be found in the -`CHANGES.md `_ file - -What's new in glue v0.11? -========================= - -The v0.11 release of glue includes a number of exciting new features and -improvements, so let's take a look at what's new! - -New Slack community -------------------- - -We have now set up Slack for any glue help/discussions, and we encourage you to -sign up! You will need to first get an account `here -`_ after which you will be able to -sign in to https://glueviz.slack.com. - -Improved interface ------------------- - -The main interface of the application has been improved, and now features a -toolbar at the top with quick access to common functionality, as well as a -clearer link to the error console (which replaces the square in the bottom right -of the interface). - -.. image:: images/v0.11/application_ui.jpg - :align: center - :width: 900 - -We've also done a lot of work to improve the layout of many of the option -widgets, fixing font sizes, and so on. We hope you like the updated interface! - -New built-in viewers --------------------- - -The built-in histogram, scatter, and image viewers have now been completely -re-written and now include new functionality. For example, the scatter viewer -now includes the ability to color-code or resize points based on another -attribute (this was previously possible in the 3D scatter viewer only): - -.. image:: images/v0.11/scatter_color_size.jpg - :align: center - :width: 700 - -The scatter viewer now also includes the ability to show symmetric error bars in -the x and/or y direction. - -.. image:: images/v0.11/scatter_error.jpg - :align: center - :width: 700 - -Finally, this viewer also allows you to plot the data using a continuous line -rather than individual points. - -The image viewer has also seen an overhaul - the main change in user experience -is that multi-color images are now made via layers instead of using a special -RGB mode, and any number of images can be combined using an arbitrary number of -colormaps or colors, rather than being restricted to RGB colors: - -.. image:: images/v0.11/image_rgb.jpg - :align: center - :width: 700 - -Subset mask importers and exporters ------------------------------------ - -While it was already possible to export data subsets as actual subsets of the -data, it is now possible to import and export the boolean masks for subsets. -At the moment, there is only built-in support for importing/exporting from -FITS files, but defining new importers/exporters can easily be done, as -described in :ref:`custom_subset_mask_importer` and -:ref:`custom_subset_mask_exporter`. In future, we will add built-in support -for a wider range of file formats. - -Performance improvements ------------------------- - -This release includes a number of significant performance improvements. For -example, there should now no longer be any delays when setting up links, and -selections should propagate between viewers more efficiently. - -Experimental WorldWide Telescope plugin ---------------------------------------- - -We have developed a plugin that provides a `WorldWide Telescope (WWT) -`_ viewer inside glue: - -.. image:: images/v0.11/plugin_wwt.jpg - :align: center - :width: 900 - -To use this viewer, you -will need to install the `glue-wwt `_ -plugin, using:: - - conda install -c glueviz glue-wwt - -if you use conda, or:: - - pip install glue-wwt - -otherwise. This viewer is experimental and is still missing a number of -features. For example, it can only be used to show datasets that have RA/Dec -columns (not other types of coordinates). Please report any issues or feature -requests `here `__. If you would be -interested in contributing to or help maintaining this plugin, we would also -love to hear from you! (see :ref:`here ` for different ways of getting in -touch). - -Experimental geospatial plugin ------------------------------- - -We have started to develop a plugin which aims to collect functionality relevant -to the analysis of geospatial data. For now, the plugin provides a data factory -that uses the `rasterio `_ package to read -geospatial raster data. This includes reading in the coordinate system and -showing longitude/latitude lines in image viewers, and also allows -longitude/latitude scatter data to be overplotted: - -.. image:: images/v0.11/plugin_geospatial.jpg - :align: center - :width: 700 - -To use this, you will need to install the `glue-geospatial -`_ plugin, using:: - - conda install -c glueviz glue-geospatial - -if you use conda, or:: - - pip install glue-geospatial - -otherwise. - -This plugin is experimental, and if you run into any issues or would like to see -new features, please open an issue `here -`__. If you would be interested in -contributing to or help maintaining this plugin, we would also love to hear from -you! (see :ref:`here ` for different ways of getting in touch). - -Backward-incompatible changes ------------------------------ - -If you programmatically create viewers from Python scripts, if you currently set -attributes on viewers, you will need to update this code if using the built-in -histogram, scatter, or image viewers. - -The main change is that the viewer classes have been renamed as follows: - -* ``ScatterWidget`` is now ``ScatterViewer`` -* ``ImageWidget`` is now ``ImageViewer`` -* ``HistogramWidget`` is now ``HistogramViewer`` -* ``TableWidget`` is now ``TableViewer`` - -In addition, attributes related to the visualization are no longer set directly -on the viewer object but instead using a ``state`` attribute on the viewers. For -example to set the ``x`` attribute on the scatter plot viewer, you should set:: - - >>> viewer.state.x_att = ... - -instead of:: - - >>> viewer.xatt = ... - -The motivation for this change is that the ``state`` object is a new object that -represents the state of the viewer in a GUI-framework-independent way, and is a -cleaner way to encapsulate all the information needed to control the -visualization. See the :ref:`programmatic` section for more details. - -New conda glueviz channel -------------------------- - -We now provide our own conda channel called ``glueviz`` (rather than using -conda-forge), which should help alleviate installation issues some users have -reported in the past. This channel contains the stable versions of glue and -various plugin packages. - -Note that it is also possible to install the latest developer version from the -``glueviz/label/dev`` channel, though be aware that while you will get the -latest cutting-edge features, you may also be more prone to bugs/breakage. - -Full list of Changes --------------------- - -In addition to the above features, a number of bugs has been fixed since the -last release, and a few other small features have been added. A full list of -changes can be found in the -`CHANGES.md `_ file - -.. _whatsnew_010: - -What's new in glue v0.10? -========================= - -Improved linking dialog ------------------------ - -The data linking dialog has been redesigned and improved: - -.. image:: images/v0.10/link_window.png - :align: center - :width: 900 - -In particular, it is now clear in the list of links which components correspond -to which datasets. This also fixes previous undesirable behaviors such as -components changing names when using the identity link, and such as components -being shown alphabetically instead of in their original native order (which has -now been fixed). Linking functions can also be grouped by categories. - -New data/subset exporters -------------------------- - -It is now possible to easily export datasets and subsets by right-clicking (or -control-clicking) on them and selecting **Export Data** or **Export Subsets**. - -.. image:: ../customizing_guide/images/export_data.png - :align: center - :width: 450 - -Custom data/subset exporters can be easily be defined by users - see -:ref:`custom_data_exporter` for more details. Currently only a small number of -formats are supported by default but this will be expanded in future. - -Performance improvements ------------------------- - -Performance has been significantly improved (in some cases by factors of 10-100) -for cases where 2D datasets were linked with 3D or higher-dimensional datasets, -and selections were made in 2D. - -Ginga plugin now moved to a separate package --------------------------------------------- - -The plugin that allows `ginga `_ viewers to -be used inside glue has been moved to a new package, -`glue-ginga `_. To install -this plugin, simply do:: - - pip install glue-ginga - -Compatibility with PyQt5 and Matplotlib 2.x -------------------------------------------- - -Glue and the 3D viewers are now fully compatible with PyQt5 and Matplotlib 2.x, -which together provide sharper plots on high DPI (e.g. retina) displays. - -Creating subset states for categorical components [advanced] ------------------------------------------------------------- - -For users who like to create subsets programmatically or in the built-in -IPython console, it is now possible to create subset states for categorical -components using e.g.:: - - d.id['source'] == 'name' - -Subsets now share more attributes with parent Data objects [advanced] ---------------------------------------------------------------------- - -:class:`~glue.core.subset.Subset` objects now have properties such as -``components``, ``visible_components``, ``ndim``, ``shape``, and more which are -inherited from parent datasets. - -Full list of Changes --------------------- - -In addition to the above features, a number of bugs has been fixed since the -last release, and a few other small features have been added. A full list of -changes can be found in the -`CHANGES.md `_ file - -.. _whatsnew_09: - -What's new in glue v0.9? -======================== - -New table viewer ----------------- - -Glue now includes a table viewer for data with 1-dimensional components (such as -tables). The table viewer highlights selections made in other viewers, and -also allows selections to be made in the viewer: - -.. image:: images/v0.9/table_viewer.png - :align: center - :width: 847 - -To make a selection in the table, either select an existing subset in the **Data -Collection** panel in the top left if you want to modify a subset, or make sure -no subset is selected to make a new subset (as in other viewers), then -click the button on the left in the table viewer toolbar, select rows you want -to include in the subset, and press enter to validate the selection. You can -also combine this with the usual logical selections ('and', 'or', etc.) from -glue to modify existing subsets by adding/removing rows. - -Improvements to 3D viewers --------------------------- - -There have been a number of improvements to the 3D viewers provided by the -`glue-vispy-viewers `_ package -(now automatically installed with glue). - -.. image:: images/v0.9/3d_viewers.png - :align: center - :width: 731 - -The main changes are: - -* The axes now include ticks, tick labels, and axis labels. For volume - renderings, the values are the pixel coordinates for now, but in future we - will allow the world coordinates to be shown. - -* Catalogs/tables can now be overplotted on top of volume renderings, as for the - 2-d image viewer. To use this, you will first need to make sure that you link - three components of the catalog/table to the three **world** coordinates of - the cube shown in the volume rendering, then drag the catalog/table dataset - onto the volume rendering. By selecting the layer corresponding to the - catalog, you can then change the appearance of the markers. - -* There is now an option in the bottom left options panel to remove data that - falls outside the coordinate axes box. This can be used for both the scatter - viewer and volume rendering viewer. - -* There is also now an option to show the data in its original aspect ratio, - assuming that the voxels are cubes. By default, the 3D viewers stretch the - data so that it fills a cube. - -* It is now possible to visualize datasets of any dimensionality in the scatter - plot viewer - however, note that this viewer currently becomes slow above - a million points. - -Improved plot.ly exporter -------------------------- - -Glue has included the ability to export plots to the `plot.ly `_ -service for a few versions now, but there was no way to control the privacy -level of the resulting plots. When exporting to plotly, you will now be -presented with a window that allows much finer control over the export: - -.. image:: images/v0.9/plotly_exporter.png - :align: center - :width: 514 - -World coordinates in slices ---------------------------- - -When viewing 3+ dimensional data in the image viewer, if the data has a -world coordinate system defined, the coordinates can now be shown in the -image slicers: - -.. image:: images/v0.9/world_slicing.png - :align: center - :width: 288 - -If the world coordinate system is not linearly related to the pixel slices, -a warning will be shown to indicate that the world coordinates are measured -along a line that goes down the center of the cube. - -Improvements to Astronomy-specific functionality ------------------------------------------------- - -If you have the `spectral-cube `__ package -installed, glue can now use this to read in spectral cubes in FITS format. To -use this, you will need to go through the **Open Data Set** menu item and -explicitly select **FITS Spectral Cube** from the list: - -.. image:: images/v0.9/spectral_cube_import.png - :align: center - :width: 288 - -We haven't enable this by default at this time because this modifies the order -of the axes, and splits the Stokes components into glue data components, which -may not always be desirable. - -In addition, units are now properly read from FITS and VO tables, and are -shown in the table viewer. In future, we will also show the units in the -different viewers. - -Finally, a new linking function is now available to link celestial Galactic -coordinates to 3D Galactocentric coordinates. - -Improvements to ``join_on_key`` [advanced] ------------------------------------------- - -The :meth:`~glue.core.data.Data.join_on_key` method can be used for advanced -linking scenarios - for instance linking datasets by e.g. an ID in two different -datasets. In this version, we have added some advanced possibilities, for -example linking by combinations of keys, as well as allowing one-to-many and -many-to-one linking. For more information, see the documentation for -:meth:`~glue.core.data.Data.join_on_key`. - -This functionality is still experimental, and we will provide in future a window -in the graphical user interface to explain and make it easy for users to set up -these kinds of links. - -Data updating [advanced] ------------------------- - -A new method, :meth:`~glue.core.data.Data.update_values_from_data` has been -added to allow values in datasets to be updated based on another dataset. This -allows users to open a dataset, create different viewers, make selections, -and then load a more recent version of the dataset and update the values, -keeping all the viewers open. - -Infrastructure changes [advanced] ---------------------------------- - -The code to handle toolbars in viewers has now been completely refactored, and -it is much easier for people developing their own viewers to define toolbars -and tools for their viewers. A new page, :ref:`Custom tools for viewers and -custom toolbars `, has been added to the documentation to show -how to use this functionality. - -We have now also switched from using the ``glue.external.qt`` module to the -`QtPy `__ package for supporting different -Python Qt wrappers (PyQt4 and 5, as well as PySide). See :ref:`qtpy` for more -information. - -Full list of Changes --------------------- - -In addition to the above features, a number of bugs has been fixed since the -last release, and a few other small features have been added. A full list of -changes can be found in the -`CHANGES.md `_ file - -.. _whatsnew_08: - -What's new in Glue v0.8? -======================== - -Playback controls for image slicing ------------------------------------ - -When using the image viewer to view datasets with more than two dimensions, the -sliders snow include playback controls that you can use to animate the slicing. - -.. image:: cube_playback_controls.png - :align: center - -By clicking multiple times on the play forward or play back button (the buttons -on either side of the stop button), you can speed up the animation. - -Improved new component window ------------------------------ - -The *Define New Component* window has now been significantly improved. The -syntax of the expression for the new component is now validated on-the-fly, and -the component cannot be created until the expression validates: - -.. image:: new_component.png - :align: center - -Adding data to glue from Python -------------------------------- - -If you make use of the :func:`~glue.qglue` function to launch glue from IPython -or the Jupyter notebook, you can now easily continue to add data to glue from -that Python session, by doing e.g.:: - - >>> app = qglue(data1=array1) - >>> app.add_data(data2=array2) - -More details can be found :ref:`here `. - -New preferences dialog ----------------------- - -A new dialog for preferences is now available via **File -> Edit Preferences**, -and allows you to change the settings for the foreground/background color of -viewers, as well as the default data color and transparency: - -.. image :: preferences.png - :align: center - -Improved feedback window ------------------------- - -The *Send Feedback* functionality available through the Help menu and the window -to send crash reports to the developers now also provide the option to specify -an email address in case you want to be contacted about the feedback/bug report: - -.. image:: feedback.png - :align: center - -Circular and polygonal profile extraction ------------------------------------------- - -The spectrum/profile extraction tool for the image viewer now supports -extraction using circular and polygonal regions. - -Full list of Changes --------------------- - -In addition to the above features, a number of bugs has been fixed since the -last release. A full list of changes can be found in the -`CHANGES.md `_ file - -.. _whatsnew_07: - -What's new in Glue v0.7? -======================== - -Code and development reorganization ------------------------------------ - -Since the v0.6.x releases, a lot of work has gone into reorganizing the Glue -code base to make it more modular and more accessible for new developers. The -documentation has also been completely reorganized and expanded, and now -includes a description of some aspects of the :ref:`Glue architecture -` and information for anyone interested in getting -involved in :ref:`Glue development `. - -We also have a new mailing list `glue-viz-dev -`_ for anyone interested -in development, so if you are interested in getting involved, please join the -list and let us know! - -As a result of the code reorganization, some imports may need to be updated if -you are using glue from scripts. Please see :doc:`this -<0.7_code_reorganization>` page for more details on what's changed! - -Layer artist options --------------------- - -When visualizing data in one of the data viewers, the list of layers is given -in the 'Plot Layers' list in the left sidebar. While it was possible to edit -the style of these layers via a contextual menu previously, we have now made -the options more visible below the list: - -.. image:: layer_options.png - :width: 300px - :align: center - -This is currently implemented for the scatter and histogram viewers, and will -be extended to other viewers in future. - -Numpy reader ------------- - -Glue is now able to read in ``.npy`` and ``.npz`` files produced by Numpy. -Thanks to Adrian Price-Whelan for contributing this feature! - -Bug fixes and usability improvements ------------------------------------- - -A number of usability issues have been fixed. Of particular note, in the v0.6.x -releases, lasso selection no longer worked in scatter plots with categorical -components on one or more of the axes, but this has now been fixed (thanks to -Will Dampier for refactoring the way selection of categorical components is -handled internally!). - -Full list of Changes --------------------- - -A full list of changes can be found in the -`CHANGES.md `_ file - -.. _whatsnew_06: - -What's new in Glue v0.6? -======================== - -Improved file readers ---------------------- - -A significant amount of work has gone into improving the build-in readers for -various data formats. In particular: - -- Glue now supports a wider range of ASCII table formats. Any format that can - be read by the `Astropy `_ package can now be read - by Glue. If you run into ASCII tables that cannot be read, please let us - know! - -- The HDF5 file reader will now read in all datasets from a file, including - both tabular and gridded data. The path to the HDF5 dataset is now - reflected in the label for the dataset in Glue. - -- The Excel file reader is now significantly more robust. In particular, it - can now read in files with multiple sheets, and the sheet name is now - included in the label for the data. - -- The FITS file reader (a data format commonly used in Astronomy) will now - read in all header-data units (HDUs) from FITS files rather than simply - reading the first. In addition, FITS files with compressed HDUs will now be - read correctly. - -Plugin manager --------------- - -In Glue v0.5, we introduced the ability to develop separate plugin packages -and have these be registered automatically with glue once installed. In some -cases it can be useful to disable/enable specific plugins, so the **Plugins** -menu now includes a plugin manager that can be used to enable/disable -plugins. This is then stored in a configuration file in the user's home -directory, and the configuration is preserved from one session to the next: - -.. image:: plugin_manager.png - :width: 50% - :align: center - -Improvements to image viewer ----------------------------- - -The image viewer now includes an **Aspect** setting that can be used to -control the aspect ratio of the pixels: - -.. image:: aspect_combo.png - :width: 50% - :align: center - -If this is set to **Square Pixels** (the default), the data is always shown -with square pixels, which may result in empty space around the data but is -more correct when the data is an actual image: - -.. image:: aspect_square.png - :width: 50% - :align: center - -On the other hand, if this is set to **Automatic**, the data is distorted to -fill the axes: - -.. image:: aspect_auto.png - :width: 50% - :align: center - -For data cubes, the slider(s) used to move through slices along the extra -dimension(s) now includes the ability to manually specify the slice to move -to, as well as buttons to step through slices, and go to the first or last -slice: - -.. image:: cube_slider.png - :width: 50% - :align: center - -Finally, when extracting a spectrum/profile from a data cube, the box used to -extract the spectrum can now be moved around by pressing the control key and -dragging the box around, resulting in the spectrum/profile being updated in -real time. - -Data factories --------------- - -For anyone developing custom data factories, the ``@data_factory`` decorator -can now accept a ``priority=`` argument that should be set to a positive -integer value (with the default being zero). This priority is then used in -case of multiple data factories being able to read a given file. For example, -if you are develop a data factory that reads FITS files in a special way, and -want it to take precedence on all other data factories, you can set the -priority to a large value. - -Experimental support for PyQt5 ------------------------------- - -Glue should now work with the -`PyQt5 `_ package, but -support is experimental for now. If you do try out PyQt5, please -`report any issues `_ you encounter! - -Python 2.6 support ------------------- - -This will be the last major release to support Python 2.6. Future releases -will support only Python 2.7 and 3.3 and above. - -Other Improvements ------------------- - -In addition to the new features described above, we have made a number of -internal improvements to the code structure, and have fixed a number of -usability bugs reported by users. - -Full list of Changes --------------------- - -A full list of changes can be found in the -`CHANGES.md `_ file - -.. _whatsnew_05: - -What's new in Glue v0.5? -======================== - -Python 3 compatibility ----------------------- - -Glue v0.5 is now fully compatible with Python 2.6, 2.7, and 3.3 and later. - -Installation with conda ------------------------ - -If you make use of Anaconda or Miniconda, Glue can now be installed very easily -by doing:: - - conda install glueviz - -This will install glue itself as well as all of the required and many optional -dependencies. - -New features ------------- - -Glue v0.5 includes a number of new features, in particular: - -* The ability to :ref:`include a copy of all datasets ` in - ``.glu`` session files, to make it easier to exchange session files with - other users. When saving a session file, choose the option to include all - data from the drop-down menu: - -.. figure:: ../getting_started/images/save_with_data.png - :align: center - :width: 400px - -* The ability to write and register - :ref:`custom data importers `, as well as - :ref:`custom menubar tools `. - -* An improved interface for :ref:`creating new components `, - including tab-completion and color highlighting of component names. - -* The ability to pass ``HDUList`` objects when using ``qglue``. - -* The ability to define floating-point and text parameter boxes when defining - custom viewers. - -* Support for more coordinate frames for the Astronomy coordinate system - transformations. - -* The ability to drag existing selection regions by pressing 'control', - selecting a selection, and moving it around. - -Improvements ------------- - -In addition to the new features described above, we have made a number of -internal improvements to the code structure, and have fixed a number of -usability bugs reported by users. - -Full list of Changes --------------------- - -A full list of changes can be found in the -`CHANGES.md `_ file diff --git a/glue/__init__.py b/glue/__init__.py index 30a8fedf6..8e0a8839e 100644 --- a/glue/__init__.py +++ b/glue/__init__.py @@ -5,6 +5,7 @@ import os import sys +import warnings import importlib.metadata @@ -14,12 +15,18 @@ sys.meta_path.append(MatplotlibBackendSetter()) from glue.viewers.custom.helper import custom_viewer +from glue.utils.error import GlueDeprecationWarning # Load user's configuration file from .config import load_configuration env = load_configuration() -from .qglue import qglue + +def qglue(*args, **kwargs): + warnings.warn('glue.qglue is deprecated, import qglue from the glue_qt module instead', GlueDeprecationWarning) + from glue_qt import qglue + return qglue(*args, **kwargs) + from .main import load_plugins # noqa @@ -35,17 +42,3 @@ def test(no_optional_skip=False): from glue._settings_helpers import load_settings load_settings() - - -# In PyQt 5.5+, PyQt overrides the default exception catching and fatally -# crashes the Qt application without printing out any details about the error. -# Below we revert the exception hook to the original Python one. Note that we -# can't just do sys.excepthook = sys.__excepthook__ otherwise PyQt will detect -# the default excepthook is in place and override it. - - -def handle_exception(exc_type, exc_value, exc_traceback): - sys.__excepthook__(exc_type, exc_value, exc_traceback) - - -sys.excepthook = handle_exception diff --git a/glue/_deps.py b/glue/_deps.py deleted file mode 100755 index dafdaef4a..000000000 --- a/glue/_deps.py +++ /dev/null @@ -1,300 +0,0 @@ -#!/usr/bin/env python -""" -Guide users through installing Glue's dependencies -""" - -import os -from collections import OrderedDict - -import sys -import importlib - -from glue._plugin_helpers import iter_plugin_entry_points - - -class Dependency(object): - - def __init__(self, module, info, package=None, min_version=None): - self.module = module - self.info = info - self.package = package or module - self.min_version = min_version - self.failed = False - - @property - def installed(self): - try: - importlib.import_module(self.module) - return True - except ImportError: - return False - - @property - def version(self): - try: - module = __import__(self.module) - return module.__version__ - except ImportError: - return 'unknown version' - except AttributeError: - try: - return module.__VERSION__ - except AttributeError: - return 'unknown version' - - def help(self): - result = f""" -{self.module}: -****************** - -{self.info} - -PIP package name: -{self.package} -""" - return result - - def __str__(self): - if self.installed: - status = f'INSTALLED ({self.version})' - elif self.failed: - status = f'FAILED ({self.info})' - else: - status = f'MISSING ({self.info})' - return f"{self.package:>20}:\t{status}" - - -class Python(Dependency): - - def __init__(self): - self.module = 'Python' - self.package = 'Python' - self.info = 'Interpreter and core library' - - @property - def installed(self): - return True - - @property - def version(self): - return sys.version.split()[0] - - -class QtDependency(Dependency): - - def __str__(self): - if self.installed: - status = f'INSTALLED ({self.version})' - else: - status = 'NOT INSTALLED' - return f"{self.module:>20}:\t{status}" - - -class PyQt5(QtDependency): - - @property - def version(self): - try: - from PyQt5 import Qt - return f"PyQt: {Qt.PYQT_VERSION_STR} - Qt: {Qt.QT_VERSION_STR}" - except (ImportError, AttributeError): - return 'unknown version' - - -class PyQt6(QtDependency): - - @property - def version(self): - try: - from PyQt6 import QtCore - return f"PyQt: {QtCore.PYQT_VERSION_STR} - Qt: {QtCore.QT_VERSION_STR}" - except (ImportError, AttributeError): - return 'unknown version' - - -class PySide2(QtDependency): - - @property - def version(self): - try: - import PySide2 - from PySide2 import QtCore - return f"PySide2: {PySide2.__version__} - Qt: {QtCore.__version__}" - except (ImportError, AttributeError): - return 'unknown version' - - -class PySide6(QtDependency): - - @property - def version(self): - try: - import PySide6 - from PySide6 import QtCore - return f"PySide6: {PySide6.__version__} - Qt: {QtCore.__version__}" - except (ImportError, AttributeError): - return 'unknown version' - - -class QtPy(Dependency): - - @property - def installed(self): - try: - importlib.import_module(self.module) - return True - except Exception: - # QtPy raises a PythonQtError in some cases, so we can't use - # ImportError. - return False - - -# Add any dependencies here -# Make sure to add new categories to the categories tuple - -python = ( - Python(), -) - -gui_framework = ( - PyQt5('PyQt5', 'Facultative QtPy backend'), - PyQt6('PyQt6', 'Facultative QtPy backend'), - PySide2('PySide2', 'Facultative QtPy backend'), - PySide6('PySide6', 'Facultative QtPy backend') -) - -required = ( - QtPy('qtpy', 'Required', min_version='1.9'), - Dependency('setuptools', 'Required', min_version='30.3'), - Dependency('echo', 'Required', min_version='0.5'), - Dependency('numpy', 'Required', min_version='1.17'), - Dependency('matplotlib', 'Required for plotting', min_version='3.2'), - Dependency('pandas', 'Adds support for Excel files and DataFrames', min_version='1.2'), - Dependency('astropy', 'Used for FITS I/O, table reading, and WCS Parsing', min_version='4.0'), - Dependency('dill', 'Used when saving Glue sessions', min_version='0.2'), - Dependency('h5py', 'Used to support HDF5 files', min_version='2.10'), - Dependency('xlrd', 'Used to support Excel files', min_version='1.2'), - Dependency('openpyxl', 'Used to support Excel files', min_version='3.0'), - Dependency('mpl_scatter_density', 'Used to make fast scatter density plots', 'mpl-scatter-density', min_version='0.7'), -) - -general = ( - Dependency('scipy', 'Used for some image processing calculation', min_version='1.1'), - Dependency('skimage', - 'Used to read popular image formats (jpeg, png, etc.)', - 'scikit-image')) - - -ipython = ( - Dependency('IPython', 'Needed for interactive IPython terminal', min_version='4'), - Dependency('qtconsole', 'Needed for interactive IPython terminal'), - Dependency('ipykernel', 'Needed for interactive IPython terminal'), - Dependency('traitlets', 'Needed for interactive IPython terminal'), - Dependency('pygments', 'Needed for interactive IPython terminal'), - Dependency('zmq', 'Needed for interactive IPython terminal', 'pyzmq')) - - -astronomy = ( - Dependency('pyavm', 'Used to parse AVM metadata in image files', 'PyAVM'), - Dependency('spectral_cube', 'Used to read in spectral cubes', 'spectral-cube'), - Dependency('astrodendro', 'Used to read in and represent dendrograms', 'astrodendro')) - - -testing = ( - Dependency('mock', 'Used in test code'), - Dependency('pytest', 'Used in test code')) - -export = ( - Dependency('plotly', 'Used to explort plots to Plot.ly'), -) - - -def plugins(): - modules = [] - dependencies = [] - for entry_point in iter_plugin_entry_points(): - module_name, _, _ = entry_point.module.partition('.') - package = entry_point.dist.name - modules.append((module_name, package)) - for module, package in sorted(set(modules)): - dependencies.append(Dependency(module, '', package=package)) - return dependencies - - -categories = (('python', python), - ('gui framework', gui_framework), - ('required', required), - ('plugins', plugins()), - ('ipython terminal', ipython), - ('general', general), - ('astronomy', astronomy), - ('testing', testing), - ('export', export)) - - -dependencies = dict((d.package, d) for c in categories for d in c[1]) - - -def get_status(): - s = "" - for category, deps in categories: - s += "%21s" % category.upper() + os.linesep - for dep in deps: - s += str(dep) + os.linesep - s += os.linesep - return s - - -def get_status_as_odict(): - status = OrderedDict() - for category, deps in categories: - for dep in deps: - if dep.installed: - status[dep.package] = dep.version - else: - status[dep.package] = "Not installed" - return status - - -def show_status(): - print(get_status()) - - -USAGE = """usage: -#show all dependencies -glue-deps list - -#display information about a dependency -glue-deps info astropy -""" - - -def main(argv=None): - argv = argv or sys.argv - - if len(argv) < 2 or argv[1] not in ['list', 'info']: - sys.stderr.write(USAGE) - sys.exit(1) - - if argv[1] == 'info': - if len(argv) != 3: - sys.stderr.write(USAGE) - sys.stderr.write("Please specify a dependency\n") - sys.exit(1) - - dep = dependencies.get(argv[2], None) - - if dep is None: - sys.stderr.write(f"Unrecognized dependency: {argv[2]:s}\n") - sys.exit(1) - - print(dep.help()) - sys.exit(0) - - if argv[1] == 'list': - show_status() - sys.exit(0) - - -if __name__ == "__main__": - main() diff --git a/glue/app/qt/__init__.py b/glue/app/qt/__init__.py index 1870f8602..c8a89500b 100644 --- a/glue/app/qt/__init__.py +++ b/glue/app/qt/__init__.py @@ -1,2 +1,4 @@ -from .application import GlueApplication # noqa -from . import keyboard_shortcuts # noqa +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.app.qt is deprecated, use glue_qt.app instead', GlueDeprecationWarning) +from glue_qt.app import * # noqa diff --git a/glue/app/qt/actions.py b/glue/app/qt/actions.py index 7d10fd792..070bb1d7b 100644 --- a/glue/app/qt/actions.py +++ b/glue/app/qt/actions.py @@ -1,31 +1,4 @@ -from qtpy import QtWidgets -from glue.icons.qt import get_icon - - -class GlueActionButton(QtWidgets.QPushButton): - - def set_action(self, action, text=True): - self._text = text - self._action = action - self.clicked.connect(action.trigger) - action.changed.connect(self._sync_to_action) - self._sync_to_action() - - def _sync_to_action(self): - self.setIcon(self._action.icon()) - if self._text: - self.setText(self._action.text()) - self.setToolTip(self._action.toolTip()) - self.setWhatsThis(self._action.whatsThis()) - self.setEnabled(self._action.isEnabled()) - - -def action(name, parent, tip='', icon=None, shortcut=None): - """ Factory for making a new action """ - a = QtWidgets.QAction(name, parent) - a.setToolTip(tip) - if icon: - a.setIcon(get_icon(icon)) - if shortcut: - a.setShortcut(shortcut) - return a +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.app.qt.actions is deprecated, use glue_qt.app.actions instead', GlueDeprecationWarning) +from glue_qt.app.actions import * # noqa diff --git a/glue/app/qt/application.py b/glue/app/qt/application.py index ed3ef86b7..4f59db281 100644 --- a/glue/app/qt/application.py +++ b/glue/app/qt/application.py @@ -1,1457 +1,4 @@ -# pylint: disable=W0223 - -import os -import sys -import weakref import warnings -import webbrowser - -from qtpy import QtCore, QtWidgets, QtGui, compat -from qtpy.QtCore import Qt - -from glue.core.application_base import Application -from glue.core.message import ApplicationClosedMessage, DataCollectionMessage, SettingsChangeMessage -from glue.core import command, BaseData -from glue.core.coordinates import WCSCoordinates -from glue import env -from glue.main import load_plugins -from glue.icons.qt import get_icon -from glue.utils.qt import get_qapp, update_global_font_size -from glue.app.qt.actions import action -from glue.dialogs.data_wizard.qt import data_wizard -from glue.dialogs.link_editor.qt import LinkEditor -from glue.dialogs.autolinker.qt import run_autolinker -from glue.dialogs.component_arithmetic.qt import ArithmeticEditorWidget -from glue.app.qt.edit_subset_mode_toolbar import EditSubsetModeToolBar -from glue.app.qt.mdi_area import GlueMdiArea -from glue.app.qt.layer_tree_widget import PlotAction, LayerTreeWidget -from glue.app.qt.preferences import PreferencesDialog -from glue.viewers.common.qt.data_viewer import DataViewer -from glue.viewers.scatter.qt import ScatterViewer -from glue.viewers.image.qt import ImageViewer -from glue.utils import nonpartial, defer_draw -from glue.utils.qt import (pick_class, GlueTabBar, qurl_to_path, - set_cursor_cm, messagebox_on_error, load_ui) - -from glue.app.qt.feedback import submit_bug_report, submit_feedback -from glue.app.qt.plugin_manager import QtPluginManager -from glue.app.qt.versions import QVersionsDialog -from glue.app.qt.terminal import glue_terminal, IPythonTerminalError - -from glue.config import qt_fixed_layout_tab, qt_client, startup_action, keyboard_shortcut -from glue.app.qt.save_data import SaveDataDialog - -__all__ = ['GlueApplication'] -DOCS_URL = 'http://www.glueviz.org' - - -def _fix_ipython_pylab(): - - try: - from IPython import get_ipython - except ImportError: - return - - shell = get_ipython() - - if shell is None: - return - - from IPython.core.error import UsageError - - # UnknownBackend exists only in IPython 5.0 and above, so if it doesn't - # exist we just set UnknownBackend to be a fake exception class - try: - from IPython.terminal.pt_inputhooks import UnknownBackend - except ImportError: - class UnknownBackend(Exception): - pass - - try: - shell.enable_pylab('agg', import_all=True) - except ValueError: - # if the shell is a normal terminal shell, we get here - pass - except UnknownBackend: - # if the shell is a normal terminal shell, we can also get here - pass - except UsageError: - pass - except KeyError: - # old versions of ipython - pass - - # Make sure we disable interactive mode (where figures get redrawn for - # every single Matplotlib command) - import matplotlib - matplotlib.interactive(False) - - -class GlueLogger(QtWidgets.QWidget): - - """ - A window to display error messages - """ - - def __init__(self, button_console, parent=None): - - super(GlueLogger, self).__init__(parent) - - self.button_console = button_console - self.button_stylesheet = button_console.styleSheet() - - self.button_console.clicked.connect(self._show) - - self._text = QtWidgets.QTextEdit() - self._text.setTextInteractionFlags(Qt.TextSelectableByMouse) - - clear = QtWidgets.QPushButton("Clear") - clear.clicked.connect(self._clear) - - report = QtWidgets.QPushButton("Send Bug Report") - report.clicked.connect(self._send_report) - - if isinstance(sys.stderr, GlueLogger): - if isinstance(sys.stderr._stderr_original, GlueLogger): - raise Exception('Too many nested GlueLoggers') - self._stderr_original = sys.stderr._stderr_original - else: - self._stderr_original = sys.stderr - - sys.stderr = self - - l = QtWidgets.QVBoxLayout() - h = QtWidgets.QHBoxLayout() - l.setContentsMargins(2, 2, 2, 2) - l.setSpacing(2) - h.setContentsMargins(0, 0, 0, 0) - - l.addWidget(self._text) - h.insertStretch(0) - h.addWidget(report) - h.addWidget(clear) - l.addLayout(h) - - self.setLayout(l) - - def fileno(self): - return self._stderr_original.fileno() - - def _set_console_button(self, attention): - if attention: - style = 'color: red; text-decoration: underline;' - else: - style = self.button_stylesheet - try: - self.button_console.setStyleSheet(style) - except RuntimeError: # Prevent RuntimeError: wrapped C/C++ object of type QToolButton has been deleted - pass - - def write(self, message): - """ - Interface for sys.excepthook - """ - # On Windows, sys.stderr can sometimes be None, in which case we only - # show the warnings/errors in the graphical glue logger. - if self._stderr_original is not None: - self._stderr_original.write(message) - self._text.moveCursor(QtGui.QTextCursor.End) - self._text.insertPlainText(message) - self._set_console_button(attention=True) - - def flush(self): - """ - Interface for sys.excepthook - """ - pass - - def _send_report(self, *args): - """ - Send the contents of the log as a bug report - """ - text = self._text.document().toPlainText() - submit_bug_report(text) - - def _clear(self, *args): - """ - Erase the log - """ - self._text.setText('') - self._set_console_button(attention=False) - self.close() - - def _show(self): - """ - Show the log - """ - self.show() - self.raise_() - - def keyPressEvent(self, event): - """ - Hide window on escape key - """ - if event.key() == Qt.Key_Escape: - self.hide() - - def closeEvent(self, event): - if sys.stderr is self: - sys.stderr = self._stderr_original - - -class ExportHelper(object): - """ - This class is needed because setting up the callbacks requires using - nonpartial but if the callback was a method on GlueApplication this would - result in a circular reference - hence we use a helper object with a - weak reference to the application. - """ - - def __init__(self, app): - self.app = weakref.ref(app) - - @messagebox_on_error("Failed to export session") - def _choose_export_session(self, saver, checker, outmode): - app = self.app() - if app is None: - return - checker(app) - if outmode is None: - return saver(app) - elif outmode in ['file', 'directory']: - outfile, file_filter = compat.getsavefilename(parent=app) - if not outfile: - return - return saver(app, outfile) - else: - assert outmode == 'label' - label, ok = QtWidgets.QInputDialog.getText(app, 'Choose a label:', - 'Choose a label:') - if not ok: - return - return saver(app, label) - - -class ImportHelper(object): - - def __init__(self, app): - self.app = weakref.ref(app) - - def _choose_load_data_wizard(self, *args): - self._choose_load_data(data_importer=data_wizard) - - def _choose_load_data(self, data_importer=None): - - app = self.app() - if app is None: - return - - if data_importer is None: - app.add_datasets(data_wizard()) - else: - data = data_importer() - if not isinstance(data, list): - raise TypeError("Data loader should return list of " - "Data objects") - for item in data: - if not isinstance(item, BaseData): - raise TypeError("Data loader should return list of " - "Data objects") - app.add_datasets(data) - - -class GlueApplication(Application, QtWidgets.QMainWindow): - - """ The main GUI application for the Qt frontend""" - - def __init__(self, data_collection=None, session=None): - - # At this point we need to check if a Qt application already exists - - # this happens for example if using the %gui qt/qt5 mode in Jupyter. We - # should keep a reference to the original icon so that we can restore it - # later - self._original_app = QtWidgets.QApplication.instance() - if self._original_app is not None: - self._original_icon = self._original_app.windowIcon() - - self._export_helper = ExportHelper(self) - self._import_helper = ImportHelper(self) - - # Now we can get the application instance, which involves setting it - # up if it doesn't already exist. - self.app = get_qapp() - - QtWidgets.QMainWindow.__init__(self) - Application.__init__(self, data_collection=data_collection, - session=session) - - # Pull in any keybindings from an external file - self.keybindings = keyboard_shortcut - - icon = get_icon('app_icon') - self.app.setWindowIcon(icon) - - # Even though we loaded the plugins in start_glue, we re-load them here - # in case glue was started directly by initializing this class. - load_plugins(require_qt_plugins=True) - - self.set_window_title() - self.setWindowIcon(icon) - self.setAttribute(Qt.WA_DeleteOnClose) - self._actions = {} - self._terminal = None - self._setup_ui() - self.tab_widget.setMovable(True) - self.tab_widget.setTabsClosable(True) - - # The following is a counter that never goes down, even if tabs are - # deleted (this is by design, to avoid having two tabs called the - # same if a tab is removed then a new one added again) - self._total_tab_count = 0 - - lwidget = self._layer_widget - a = PlotAction(lwidget, self) - lwidget.ui.layerTree.addAction(a) - - self._tweak_geometry() - self._create_actions() - self._create_menu() - self._connect() - self.new_tab() - self._update_viewer_in_focus() - - def _update_viewer_in_focus(self, *args): - - if not hasattr(self, '_viewer_in_focus'): - self._viewer_in_focus = None - - mdi_area = self.current_tab - active = mdi_area.activeSubWindow() - - # Disable any active tool in the viewer that was previously in focus. - # Note that we want to do this even if active is None, which means that - # the user may have switched application. - if (self._viewer_in_focus is not None and - (active is None or active.widget() is not self._viewer_in_focus)): - try: - self._viewer_in_focus.toolbar.active_tool = None - except AttributeError: - pass # not all viewers have toolbars - - if active is None: - first_viewer = None - for win in mdi_area.subWindowList(): - if self._viewer_in_focus is win.widget(): - break - elif isinstance(win.widget(), DataViewer): - first_viewer = win.widget() - else: - self._viewer_in_focus = first_viewer - self._update_focus_decoration() - self._update_plot_dashboard() - else: - self._viewer_in_focus = active.widget() - self._update_focus_decoration() - self._update_plot_dashboard() - - def run_startup_action(self, name): - if name in startup_action.members: - startup_action.members[name](self.session, self.data_collection) - else: - raise Exception("Unknown startup action: {0}".format(name)) - - def _setup_ui(self): - self._ui = load_ui('application.ui', None, - directory=os.path.dirname(__file__)) - self.setCentralWidget(self._ui) - self._ui.tabWidget.setTabBar(GlueTabBar()) - - lw = LayerTreeWidget(session=self._session) - lw.set_checkable(False) - self._vb = QtWidgets.QVBoxLayout() - self._vb.setContentsMargins(0, 0, 0, 0) - self._vb.addWidget(lw) - self._ui.data_layers.setLayout(self._vb) - self._layer_widget = lw - - # Data toolbar - - self._data_toolbar = QtWidgets.QToolBar("Session and Data toolbar") - - self._data_toolbar.setIconSize(QtCore.QSize(16, 16)) - - self._button_open_session = QtWidgets.QToolButton() - self._button_open_session.setText("Open Session") - self._button_open_session.setIcon(get_icon('glue_open')) - self._button_open_session.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) - self._button_open_session.clicked.connect(self._restore_session) - - self._data_toolbar.addWidget(self._button_open_session) - - self._button_save_session = QtWidgets.QToolButton() - self._button_save_session.setText("Export Session") - self._button_save_session.setIcon(get_icon('glue_filesave')) - self._button_save_session.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) - self._button_save_session.clicked.connect(self._choose_save_session) - - self._data_toolbar.addWidget(self._button_save_session) - self._button_open_data = QtWidgets.QToolButton() - self._button_open_data.setText("Import Data") - self._button_open_data.setIcon(get_icon('glue_open')) - self._button_open_data.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) - self._button_open_data.clicked.connect(self._import_helper._choose_load_data_wizard) - - self._data_toolbar.addWidget(self._button_open_data) - - self._button_save_data = QtWidgets.QToolButton() - self._button_save_data.setText("Export Data/Subsets") - self._button_save_data.setIcon(get_icon('glue_filesave')) - self._button_save_data.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) - self._button_save_data.clicked.connect(self._choose_save_data) - - self._data_toolbar.addWidget(self._button_save_data) - - self._button_link_data = QtWidgets.QToolButton() - self._button_link_data.setText("Link Data") - self._button_link_data.setIcon(get_icon('glue_link')) - self._button_link_data.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) - self._button_link_data.clicked.connect(self._set_up_links) - - self._data_toolbar.addWidget(self._button_link_data) - - self._button_edit_components = QtWidgets.QToolButton() - self._button_edit_components.setText("Arithmetic attributes") - self._button_edit_components.setIcon(get_icon('arithmetic')) - self._button_edit_components.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) - self._button_edit_components.clicked.connect(self._artihmetic_dialog) - - self._data_toolbar.addWidget(self._button_edit_components) - - self.addToolBar(self._data_toolbar) - - self._on_data_collection_change() - - # Selection mode toolbar - - tbar = EditSubsetModeToolBar(parent=self) - self._mode_toolbar = tbar - - self.addToolBar(self._mode_toolbar) - - # Error console toolbar - - self._console_toolbar = QtWidgets.QToolBar('Advanced toolbar') - - self._console_toolbar.setIconSize(QtCore.QSize(14, 14)) - - spacer = QtWidgets.QWidget() - spacer.setSizePolicy(QtWidgets.QSizePolicy.Expanding, - QtWidgets.QSizePolicy.Preferred) - - self._console_toolbar.addWidget(spacer) - - self._button_ipython = QtWidgets.QToolButton() - self._button_ipython.setCheckable(True) - self._button_ipython.setText("Terminal") - self._button_ipython.setIcon(get_icon('IPythonConsole')) - self._button_ipython.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) - self._button_ipython.clicked.connect(self._toggle_terminal) - - self._console_toolbar.addWidget(self._button_ipython) - - self._button_preferences = QtWidgets.QToolButton() - self._button_preferences.setText("Preferences") - self._button_preferences.setIcon(get_icon('glue_settings')) - self._button_preferences.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) - self._button_preferences.clicked.connect(self._edit_settings) - - self._console_toolbar.addWidget(self._button_preferences) - - self._button_console = QtWidgets.QToolButton() - self._button_console.setText("Error Console") - self._button_console.setToolButtonStyle(Qt.ToolButtonTextOnly) - - self._console_toolbar.addWidget(self._button_console) - - self.addToolBar(self._console_toolbar) - - self._log = GlueLogger(button_console=self._button_console) - self._log.window().setWindowTitle("Console Log") - self._log.resize(550, 550) - self._log.hide() - - self._hub.subscribe(self, DataCollectionMessage, handler=self._on_data_collection_change) - self._hub.subscribe(self, SettingsChangeMessage, handler=self._on_ui_settings_change) - - def _artihmetic_dialog(self, *event): - dialog = ArithmeticEditorWidget(self.data_collection) - dialog.exec_() - - def _on_data_collection_change(self, *event): - self._button_save_data.setEnabled(len(self.data_collection) > 0) - self._button_link_data.setEnabled(len(self.data_collection) > 1) - self._button_edit_components.setEnabled(len(self.data_collection) > 0) - - def _on_ui_settings_change(self, *event): - update_global_font_size() - - def keyPressEvent(self, event): - if self.current_tab.activeSubWindow() and self.current_tab.activeSubWindow().widget(): - active_window = self.current_tab.activeSubWindow().widget() - else: - active_window = None - - # keybindings is a data structure in the form of dict(dict) - # which uses the DataViewer as the first key, the key pressed - # as the second key, and the function associated with those two - # as the value. - if type(active_window) in self.keybindings: - for k, func in self.keybindings.members[type(active_window)].items(): - if event.key() == k: - func(self.session) - return - - # If key does not correspond with viewers, it might correspond - # with the global application, thus, None - if None in self.keybindings: - for k, func in self.keybindings.members[None].items(): - if event.key() == k: - func(self.session) - return - - return super(GlueApplication, self).keyPressEvent(event) - - def _set_up_links(self, event): - LinkEditor.update_links(self.data_collection) - - def _tweak_geometry(self): - """Maximize window by default.""" - self._ui.main_splitter.setStretchFactor(0, 1) - self._ui.main_splitter.setStretchFactor(1, 9) - self._ui.data_plot_splitter.setStretchFactor(0, 1) - self._ui.data_plot_splitter.setStretchFactor(1, 2) - self._ui.data_plot_splitter.setStretchFactor(2, 1) - - @property - def tab_widget(self): - return self._ui.tabWidget - - @property - def tab_bar(self): - return self._ui.tabWidget.tabBar() - - @property - def tab_count(self): - """ - The number of open tabs - """ - return self._ui.tabWidget.count() - - @property - def current_tab(self): - return self._ui.tabWidget.currentWidget() - - def get_tab_index(self, widget): - for idx in range(self.tab_count): - if self.tab(idx) == widget: - return idx - raise Exception("Tab not found") - - def tab(self, index=None): - if index is None: - return self.current_tab - return self._ui.tabWidget.widget(index) - - def new_tab(self, *args): - """Spawn a new tab page""" - layout = QtWidgets.QGridLayout() - layout.setSpacing(1) - layout.setContentsMargins(0, 0, 0, 0) - widget = GlueMdiArea(self) - widget.setLayout(layout) - tab = self.tab_widget - self._total_tab_count += 1 - tab.addTab(widget, str("Tab %i" % self._total_tab_count)) - tab.setCurrentWidget(widget) - widget.subWindowActivated.connect(self._update_viewer_in_focus) - - def close_tab(self, index, warn=True): - """ Close a tab window and all associated data viewers """ - - # do not delete the last tab - if self.tab_widget.count() == 1: - return - - w = self.tab_widget.widget(index) - - if len(w.subWindowList()) > 0: - - if warn and not os.environ.get('GLUE_TESTING'): - buttons = QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel - dialog = QtWidgets.QMessageBox.warning( - self, "Confirm Close", - "Are you sure you want to close this tab? " - "This will close all data viewers in the tab.", - buttons=buttons, defaultButton=QtWidgets.QMessageBox.Cancel) - if not dialog == QtWidgets.QMessageBox.Ok: - return - - for window in w.subWindowList(): - widget = window.widget() - if isinstance(widget, DataViewer): - widget.close(warn=False) - - w.close() - - self.tab_widget.removeTab(index) - - def add_widget(self, new_widget, label=None, tab=None, - hold_position=False): - """ - Add a widget to one of the tabs. - - Returns the window that this widget is wrapped in. - - :param new_widget: new QtWidgets.QWidget to add - - :param label: label for the new window. Optional - :type label: str - - :param tab: Tab to add to. Optional (default: current tab) - :type tab: int - - :param hold_position: If True, then override Qt's default - placement and retain the original position - of new_widget - :type hold_position: bool - """ - - # Find first tab that supports addSubWindow - if tab is None: - if hasattr(self.current_tab, 'addSubWindow'): - pass - else: - for tab in range(self.tab_count): - page = self.tab(tab) - if hasattr(page, 'addSubWindow'): - break - else: - self.new_tab() - tab = self.tab_count - 1 - - page = self.tab(tab) - pos = getattr(new_widget, 'position', None) - sub = new_widget.mdi_wrap() - - sub.closed.connect(self._update_viewer_in_focus) - - if label: - sub.setWindowTitle(label) - page.addSubWindow(sub) - page.setActiveSubWindow(sub) - - if hold_position and pos is not None: - new_widget.move(pos[0], pos[1]) - - self.tab_widget.setCurrentWidget(page) - - return sub - - def _edit_settings(self, *args): - self._editor = PreferencesDialog(self, parent=self) - self._editor.show() - - def gather_current_tab(self, *args): - """Arrange windows in current tab via tiling""" - self.current_tab.tileSubWindows() - - def _get_plot_dashboards(self, widget): - - if not isinstance(widget, DataViewer): - return QtWidgets.QWidget(), QtWidgets.QWidget(), "" - - layer_view = widget.layer_view() - options_widget = widget.options_widget() - - return layer_view, options_widget, str(widget) - - def _clear_dashboard(self): - - for widget, title in [(self._ui.plot_layers, "Plot Layers"), - (self._ui.plot_options, "Plot Options")]: - layout = widget.layout() - if layout is None: - layout = QtWidgets.QVBoxLayout() - layout.setContentsMargins(4, 4, 4, 4) - widget.setLayout(layout) - while layout.count(): - layout.takeAt(0).widget().hide() - - self._ui.plot_options_label.setText("Plot Options") - self._ui.plot_layers_label.setText("Plot Layers") - - def _update_plot_dashboard(self, *args): - - self._clear_dashboard() - - if self._viewer_in_focus is None: - return - - layer_view, options_widget, title = self._get_plot_dashboards(self._viewer_in_focus) - - layout = self._ui.plot_layers.layout() - layout.addWidget(layer_view) - - layout = self._ui.plot_options.layout() - layout.addWidget(options_widget) - - layer_view.show() - options_widget.show() - - if title: - self._ui.plot_options_label.setText("Plot Options - %s" % title) - self._ui.plot_layers_label.setText("Plot Layers - %s" % title) - else: - self._ui.plot_options_label.setText("Plot Options") - self._ui.plot_layers_label.setText("Plot Layers") - - self._update_focus_decoration() - - def _update_focus_decoration(self): - mdi_area = self.current_tab - for win in mdi_area.subWindowList(): - widget = win.widget() - if isinstance(widget, DataViewer): - widget.set_focus(widget is self._viewer_in_focus) - - def _connect(self): - self.setAcceptDrops(True) - self._layer_widget.setup(self._data) - - self.tab_widget.tabCloseRequested.connect(self.close_tab) - self.tab_widget.currentChanged.connect(self._update_viewer_in_focus) - - def _create_menu(self): - mbar = self.menuBar() - menu = QtWidgets.QMenu(mbar) - menu.setTitle("&File") - - menu.addAction(self._actions['data_new']) - if 'data_importers' in self._actions: - submenu = menu.addMenu("I&mport data") - for a in self._actions['data_importers']: - submenu.addAction(a) - # menu.addAction(self._actions['data_save']) # XXX add this - menu.addAction(self._actions['session_reset']) - menu.addAction(self._actions['session_restore']) - menu.addAction(self._actions['session_save']) - menu.addAction(self._actions['export_data']) - if 'session_export' in self._actions: - submenu = menu.addMenu("Advanced E&xporters") - for a in self._actions['session_export']: - submenu.addAction(a) - menu.addSeparator() - menu.addAction("Edit &Preferences", self._edit_settings) - # Here we use close instead of self.app.quit because if we are launching - # glue from an environment with a Qt event loop already existing, we - # don't want to quit this. Using close here is safer, though it does - # mean that any dialog we launch from glue has to be either modal (to - # prevent quitting) or correctly define its parent so that it gets - # closed too. - menu.addAction("&Quit", self.close) - mbar.addMenu(menu) - - menu = QtWidgets.QMenu(mbar) - menu.setTitle("&Edit ") - menu.addAction(self._actions['undo']) - menu.addAction(self._actions['redo']) - mbar.addMenu(menu) - - menu = QtWidgets.QMenu(mbar) - menu.setTitle("&View ") - - a = QtWidgets.QAction("&Console Log", menu) - a.triggered.connect(self._log._show) - menu.addAction(a) - mbar.addMenu(menu) - - menu = QtWidgets.QMenu(mbar) - menu.setTitle("&Canvas") - menu.addAction(self._actions['tab_new']) - menu.addAction(self._actions['viewer_new']) - menu.addAction(self._actions['fixed_layout_tab_new']) - menu.addSeparator() - menu.addAction(self._actions['gather']) - menu.addAction(self._actions['tab_rename']) - mbar.addMenu(menu) - - menu = QtWidgets.QMenu(mbar) - menu.setTitle("Data &Manager") - menu.addActions(self._layer_widget.actions()) - - mbar.addMenu(menu) - - menu = QtWidgets.QMenu(mbar) - menu.setTitle("&Plugins") - menu.addAction(self._actions['plugin_manager']) - menu.addSeparator() - - if 'plugins' in self._actions: - for plugin in self._actions['plugins']: - menu.addAction(plugin) - - mbar.addMenu(menu) - - # trigger inclusion of Mac Native "Help" tool - menu = mbar.addMenu("&Help") - - a = QtWidgets.QAction("&Online Documentation", menu) - a.triggered.connect(nonpartial(webbrowser.open, DOCS_URL)) - menu.addAction(a) - - a = QtWidgets.QAction("Send &Feedback", menu) - a.triggered.connect(nonpartial(submit_feedback)) - menu.addAction(a) - - menu.addSeparator() - menu.addAction("Version information", self._show_glue_info) - - def _show_glue_info(self): - window = QVersionsDialog(parent=self) - window.show() - window.exec_() - - def _choose_save_data(self, *args): - dialog = SaveDataDialog(data_collection=self.data_collection, parent=self) - dialog.exec_() - - def _create_actions(self): - """ Create and connect actions, store in _actions dict """ - self._actions = {} - - a = action("&New Data Viewer", self, - tip="Open a new visualization window in the current tab", - shortcut=QtGui.QKeySequence.New) - a.triggered.connect(self._choose_new_data_viewer_nodata) - self._actions['viewer_new'] = a - - if len(qt_client.members) == 0: - a.setEnabled(False) - - a = action("New Fixed Layout Tab", self, - tip="Create a new tab with a fixed layout") - a.triggered.connect(self.choose_new_fixed_layout_tab) - self._actions['fixed_layout_tab_new'] = a - - if len(qt_fixed_layout_tab.members) == 0: - a.setEnabled(False) - - a = action('New &Tab', self, - shortcut=QtGui.QKeySequence.AddTab, - tip='Add a new tab') - a.triggered.connect(self.new_tab) - self._actions['tab_new'] = a - - a = action('&Rename Tab', self, - shortcut="Ctrl+R", - tip='Set a new label for the current tab') - a.triggered.connect(nonpartial(self.tab_bar.choose_rename_tab)) - self._actions['tab_rename'] = a - - a = action('&Gather Windows', self, - tip='Gather plot windows side-by-side', - shortcut='Ctrl+G') - a.triggered.connect(self.gather_current_tab) - self._actions['gather'] = a - - a = action('&Export Session', self, - tip='Save the current session') - a.triggered.connect(self._choose_save_session) - self._actions['session_save'] = a - - # Add file loader as first item in File menu for convenience. We then - # also add it again below in the Import menu for consistency. - a = action("&Open Data Set", self, tip="Open a new data set", - shortcut=QtGui.QKeySequence.Open) - a.triggered.connect(self._import_helper._choose_load_data_wizard) - self._actions['data_new'] = a - - # We now populate the "Import data" menu - from glue.config import importer - - acts = [] - - # Add default file loader (later we can add this to the registry) - a = action("Import from file", self, tip="Import from file") - a.triggered.connect(self._import_helper._choose_load_data_wizard) - acts.append(a) - - for label, data_importer in importer: - a = action(label, self, tip=label) - a.triggered.connect(nonpartial(self._import_helper._choose_load_data, data_importer)) - acts.append(a) - - self._actions['data_importers'] = acts - - from glue.config import exporters - if len(exporters) > 0: - acts = [] - for e in exporters: - label, saver, checker, mode = e - a = action(label, self, - tip='Export the current session to %s format' % - label) - a.triggered.connect(nonpartial(self._export_helper._choose_export_session, - saver, checker, mode)) - acts.append(a) - - self._actions['session_export'] = acts - - a = action('Open S&ession', self, - tip='Restore a saved session') - a.triggered.connect(self._restore_session) - self._actions['session_restore'] = a - - a = action('Reset S&ession', self, - tip='Reset session to clean state') - a.triggered.connect(self._reset_session) - self._actions['session_reset'] = a - - a = action('Export D&ata/Subsets', self, - tip='Export data to a file') - a.triggered.connect(self._choose_save_data) - self._actions['export_data'] = a - - a = action("Undo", self, - tip='Undo last action', - shortcut=QtGui.QKeySequence.Undo) - a.triggered.connect(self.undo) - a.setEnabled(False) - self._actions['undo'] = a - - a = action("Redo", self, - tip='Redo last action', - shortcut=QtGui.QKeySequence.Redo) - a.triggered.connect(self.redo) - a.setEnabled(False) - self._actions['redo'] = a - - # Create actions for menubar plugins - from glue.config import menubar_plugin - acts = [] - for label, function in menubar_plugin: - a = action(label, self, tip=label) - a.triggered.connect(nonpartial(function, - self.session, - self.data_collection)) - acts.append(a) - self._actions['plugins'] = acts - - a = action('&Plugin Manager', self, - tip='Open plugin manager') - a.triggered.connect(self.plugin_manager) - self._actions['plugin_manager'] = a - - def undo(self, *args): - super(GlueApplication, self).undo() - - def redo(self, *args): - super(GlueApplication, self).redo() - - def choose_new_fixed_layout_tab(self, *args): - """ - Creates a new tab with a fixed layout - """ - - tab_cls = pick_class(list(qt_fixed_layout_tab.members), title='Fixed layout tab', - label="Choose a new fixed layout tab", - sort=True) - - return self.add_fixed_layout_tab(tab_cls) - - def add_fixed_layout_tab(self, tab_cls): - - tab = tab_cls(session=self.session) - - self._total_tab_count += 1 - - name = 'Tab {0}'.format(self._total_tab_count) - if hasattr(tab, 'LABEL'): - name += ': ' + tab.LABEL - self.tab_widget.addTab(tab, name) - self.tab_widget.setCurrentWidget(tab) - tab.subWindowActivated.connect(self._update_viewer_in_focus) - - return tab - - def _choose_new_data_viewer_nodata(self): - self.choose_new_data_viewer() - - def choose_new_data_viewer(self, data=None): - """ Create a new visualization window in the current tab - """ - - if data and data.ndim == 1 and ScatterViewer in qt_client.members: - default = ScatterViewer - elif data and data.ndim > 1 and ImageViewer in qt_client.members: - default = ImageViewer - else: - default = None - - client = pick_class(list(qt_client.members), title='Data Viewer', - label="Choose a new data viewer", - default=default, sort=True) - - if client is None: - return - - cmd = command.NewDataViewer(viewer=client, data=data) - return self.do(cmd) - - @defer_draw - def new_data_viewer(self, viewer_class, data=None, state=None): - viewer = super(GlueApplication, self).new_data_viewer(viewer_class, data=data, state=state) - if viewer is not None: - viewer.show() - return viewer - - def set_window_title(self, detail=None): - """Set the window title""" - if detail is None: - title = "Glue" - else: - title = "Glue (" + detail + ")" - self.setWindowTitle(title) - - def _on_session_changed(self, name): - """Call when the session is changed""" - self.set_window_title(name) - - def _choose_save_session(self, *args): - """ Save the data collection and hub to file. - - Can be restored via restore_session - """ - - # include file filter twice, so it shows up in Dialog - outfile, file_filter = compat.getsavefilename( - parent=self, basedir=getattr(self, '_last_session_name', 'session.glu'), - filters=("Glue Session with absolute paths to data (*.glu);; " - "Glue Session with relative paths to data (*.glu);; " - "Glue Session including data (*.glu)"), - selectedfilter=getattr(self, '_last_session_filter', - 'Glue Session with relative paths to data (*.glu)')) - - # This indicates that the user cancelled - if not outfile: - return - - # Add extension if not specified - if '.' not in outfile: - outfile += '.glu' - - self._last_session_name = outfile - self._last_session_filter = file_filter - - with set_cursor_cm(Qt.WaitCursor): - self.save_session(outfile, - include_data="including data" in file_filter, - absolute_paths="absolute" in file_filter) - self._on_session_changed(outfile) - - @messagebox_on_error("Failed to restore session") - def _restore_session(self, *args): - """ Load a previously-saved state, and restart the session """ - fltr = "Glue sessions (*.glu)" - file_name, file_filter = compat.getopenfilename( - parent=self, filters=fltr) - if not file_name: - return - - ga = self.restore_session_and_close(file_name) - return ga - - @property - def is_empty(self): - """ - Returns `True` if there are no viewers and no data. - """ - return (len([viewer for tab in self.viewers for viewer in tab]) == 0 and - len(self.data_collection) == 0) - - def _reset_session(self, *args, **kwargs): - """ - Reset session to clean state. - """ - - warn = kwargs.pop('warn', False) - - if not os.environ.get('GLUE_TESTING') and warn and not self.is_empty: - buttons = QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel - dialog = QtWidgets.QMessageBox.warning( - self, "Confirm Close", - "Are you sure you want to reset the session? " - "This will close all datasets, subsets, and data viewers", - buttons=buttons, defaultButton=QtWidgets.QMessageBox.Cancel) - if not dialog == QtWidgets.QMessageBox.Ok: - return - - # Make sure the closeEvent gets executed to close the GlueLogger - self._log.close() - if self.app is not None: - self.app.processEvents() - - ga = GlueApplication() - ga.start(block=False) - - # NOTE: we need to keep a reference to this new application object - # otherwise it will immediately garbage collect - this is a hack and - # we should find a better solution in future. - self._new_application = ga - - # We need to close this after we open the next application otherwise - # Qt will quit since there are no actively open windows. - self.close() - - return ga - - @staticmethod - def restore_session(path, show=True): - """ - Reload a previously-saved session - - Parameters - ---------- - path : str - Path to the file to load - show : bool, optional - If True (the default), immediately show the widget - - Returns - ------- - app : :class:`glue.app.qt.application.GlueApplication` - The loaded application - """ - ga = Application.restore_session(path) - if show: - ga.start(block=False) - ga._on_session_changed(path) - return ga - - def has_terminal(self, create_if_not=True): - """ - Returns True if the IPython terminal is present. - """ - if self._terminal is None and create_if_not: - self._create_terminal() - return self._terminal is not None - - def _create_terminal(self): - - if self._terminal is not None: # already set up - return - - try: - widget = glue_terminal(data_collection=self._data, - dc=self._data, - hub=self._hub, - session=self.session, - application=self, - **vars(env)) - except IPythonTerminalError: - self._button_ipython.setEnabled(False) - else: - self._terminal = self.add_widget(widget) - self._terminal.closed.connect(self._on_terminal_close) - self._hide_terminal() - - def _toggle_terminal(self): - if self._terminal is None: - self._create_terminal() - if self._terminal.isVisible(): - self._hide_terminal() - if self._terminal.isVisible(): - warnings.warn("An unexpected error occurred while " - "trying to hide the terminal") - else: - self._show_terminal() - if not self._terminal.isVisible(): - warnings.warn("An unexpected error occurred while " - "trying to show the terminal") - - def _on_terminal_close(self): - if self._button_ipython.isChecked(): - self._button_ipython.blockSignals(True) - self._button_ipython.setChecked(False) - self._button_ipython.blockSignals(False) - - def _hide_terminal(self): - self._terminal.hide() - - def _show_terminal(self): - self._terminal.show() - self._terminal.widget().show() - - def start(self, size=None, position=None, block=True, maximized=True): - """ - Show the GUI and start the application. - - Parameters - ---------- - size : (int, int) Optional - The default width/height of the application. - If not provided, uses the full screen - position : (int, int) Optional - The default position of the application - """ - if maximized: - self.showMaximized() - else: - self.show() - if size is not None: - self.resize(*size) - if position is not None: - self.move(*position) - - self.raise_() # bring window to front - - # at some point during all this, the MPL backend - # switches. This call restores things, so - # figures are still inlined in the notebook. - # XXX find out a better place for this - _fix_ipython_pylab() - - if block: - return self.app.exec_() - - exec_ = start - - def dragEnterEvent(self, event): - if event.mimeData().hasUrls(): - event.accept() - else: - event.ignore() - - @messagebox_on_error("Failed to load files") - def dropEvent(self, event): - - urls = event.mimeData().urls() - - paths = [qurl_to_path(url) for url in urls] - - if any(path.endswith('.glu') for path in paths): - if len(paths) != 1: - raise Exception("When dragging and dropping files onto glue, " - "only a single .glu session file can be " - "dropped at a time, or multiple datasets, but " - "not a mix of both.") - else: - self.restore_session_and_close(paths[0]) - else: - self.load_data(paths) - - event.accept() - - @messagebox_on_error("Failed to restore session") - def restore_session_and_close(self, path, warn=True): - - if warn and not self.is_empty: - buttons = QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel - dialog = QtWidgets.QMessageBox.warning( - self, "Confirm Close", - "Loading a session file will close the existing session. Are you " - "sure you want to continue?", - buttons=buttons, defaultButton=QtWidgets.QMessageBox.Cancel) - if not dialog == QtWidgets.QMessageBox.Ok: - return - - with set_cursor_cm(Qt.WaitCursor): - app = self.restore_session(path) - app.setGeometry(self.geometry()) - - # NOTE: we need to keep a reference to this new application object - # otherwise it will immediately garbage collect - this is a hack and - # we should find a better solution in future. - self._new_application = app - - self.close() - - return app - - def closeEvent(self, event): - """Emit a message to hub before closing.""" - - # Clear the namespace in the terminal to avoid cicular references - if self._terminal is not None: - self._terminal.widget().clear_ns(['data_collection', 'dc', 'hub', 'session', 'application']) - - for tab in self.viewers: - for viewer in tab: - viewer.close(warn=False) - self._viewer_in_focus = None - self._clear_dashboard() - self._log.close() - self._hub.broadcast(ApplicationClosedMessage(None)) - event.accept() - if self._original_app is not None: - self._original_app.setWindowIcon(self._original_icon) - self._original_app = None - self.app = None - - def report_error(self, message, detail): - """ - Display an error in a modal - - :param message: A short description of the error - :type message: str - :param detail: A longer description - :type detail: str - """ - qmb = QtWidgets.QMessageBox(QtWidgets.QMessageBox.Critical, "Error", message) - qmb.setDetailedText(detail) - qmb.resize(400, qmb.size().height()) - qmb.exec_() - - def plugin_manager(self, *args): - from glue.main import _installed_plugins - pm = QtPluginManager(installed=_installed_plugins) - pm.ui.exec_() - - def _update_undo_redo_enabled(self, *args): - undo, redo = self._cmds.can_undo_redo() - self._actions['undo'].setEnabled(undo) - self._actions['redo'].setEnabled(redo) - self._actions['undo'].setText('Undo ' + self._cmds.undo_label) - self._actions['redo'].setText('Redo ' + self._cmds.redo_label) - - @property - def viewers(self): - """ - A list of lists of open Data Viewers. - - Each inner list contains the viewers open on a particular tab. - """ - result = [] - for t in range(self.tab_count): - tab = self.tab(t) - item = [] - for subwindow in tab.subWindowList(): - widget = subwindow.widget() - if isinstance(widget, DataViewer): - item.append(widget) - result.append(tuple(item)) - return tuple(result) - - @property - def tab_names(self): - """ - The name of each tab - - A list of strings - """ - return [self.tab_bar.tabText(i) for i in range(self.tab_count)] - - @tab_names.setter - def tab_names(self, values): - for index, value in enumerate(values): - self.tab_bar.setTabText(index, value) - - @staticmethod - def _choose_merge(data, others, suggested_label): - - w = load_ui('merge.ui', None, directory=os.path.dirname(__file__)) - w.button_yes.clicked.connect(w.accept) - w.button_no.clicked.connect(w.reject) - w.show() - w.raise_() - - # Add the main dataset to the list. Some of the 'others' may also be - # new ones, so it doesn't really make sense to distinguish between - # the two here. The main point is that some datasets, including at - # least one new one, have a common shape. - others.append(data) - others.sort(key=lambda x: x.label) - for i, d in enumerate(others): - if isinstance(d.coords, WCSCoordinates): - if i == 0: - break - else: - others[0], others[i] = others[i], others[0] - break - - w.merged_label.setText(suggested_label) - - entries = [QtWidgets.QListWidgetItem(other.label) for other in others] - for e in entries: - e.setCheckState(Qt.Checked) - - for d, item in zip(others, entries): - w.choices.addItem(item) - - if not w.exec_(): - return None, None - - result = [layer for layer, entry in zip(others, entries) - if entry.checkState() == Qt.Checked] - - if result: - return result, str(w.merged_label.text()) - - return None, None - - def screenshot(self, filename): - """ - Save a screenshot of the current application window to a file. - """ - image = QtGui.QImage(self.size(), QtGui.QImage.Format_RGB32) - painter = QtGui.QPainter(image) - flags = self.IgnoreMask | self.DrawWindowBackground | self.DrawChildren - self.render(painter, QtCore.QPoint(), QtGui.QRegion(), flags) - image.save(filename) - painter.end() - - def move_viewer_to_tab(self, viewer, tab): - """ - Move the given viewer to the given tab. - If the given tab is the same as the current tab, - do nothing. - """ - current_window = viewer.parent() - current_tab = current_window.mdiArea() - new_tab = self.tab(tab) - if new_tab is None: - raise ValueError(f"Invalid tab index: {tab}") - if current_tab is not new_tab: - - # We do this rather than just use setParent on current_window - # so that the moved window is put in a reasonable place - # in the new tab (i.e. not on top of another viewer), - # because there may be another viewer in the new tab - # with the same position. - # Also, if we don't resize, moved windows will get progressively - # smaller. This is because the new MDI window will be sized - # according to the size of the old viewer, which is slightly - # smaller than the parent window. - current_tab.removeSubWindow(current_window) - viewer.resize(current_window.size()) - current_window.setWidget(None) - current_window.close() - - self.add_widget(viewer, tab=tab) - - def add_datasets(self, *args, **kwargs): - result = super(GlueApplication, self).add_datasets(*args, **kwargs) - run_autolinker(self.data_collection) - return result - - def __gluestate__(self, context): - state = super(GlueApplication, self).__gluestate__(context) - state['tab_names'] = self.tab_names - return state - - @classmethod - def __setgluestate__(cls, rec, context): - self = super(GlueApplication, cls).__setgluestate__(rec, context) - if 'tab_names' in rec: - self.tab_names = rec['tab_names'] - return self +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.app.qt.application is deprecated, use glue_qt.app.application instead', GlueDeprecationWarning) +from glue_qt.app.application import * # noqa diff --git a/glue/app/qt/application.ui b/glue/app/qt/application.ui deleted file mode 100644 index 6c786c8fd..000000000 --- a/glue/app/qt/application.ui +++ /dev/null @@ -1,191 +0,0 @@ - - - GlueApplication - - - - 0 - 0 - 1024 - 557 - - - - Form - - - - 3 - - - 3 - - - 3 - - - 2 - - - - - Qt::Horizontal - - - - Qt::Vertical - - - - - 0 - - - - - - 75 - true - - - - Data Collection - - - - - - - - 0 - 0 - - - - Qt::LeftToRight - - - false - - - - - - - - - 0 - - - - - - 75 - true - - - - Plot Layers - - - - - - - - 0 - 0 - - - - Qt::LeftToRight - - - false - - - - - - - - - 0 - - - - - - 75 - true - - - - Plot Options - - - - - - - - 0 - 0 - - - - Qt::LeftToRight - - - false - - - - - - - - - true - - - - 1 - 0 - - - - - 400 - 0 - - - - QTabWidget::Rounded - - - -1 - - - Qt::ElideRight - - - false - - - false - - - false - - - true - - - - - - - - - diff --git a/glue/app/qt/edit_subset_mode_toolbar.py b/glue/app/qt/edit_subset_mode_toolbar.py index 2a168f1bc..417a14ea3 100644 --- a/glue/app/qt/edit_subset_mode_toolbar.py +++ b/glue/app/qt/edit_subset_mode_toolbar.py @@ -1,203 +1,4 @@ -from qtpy import QtCore, QtWidgets -from glue.core.edit_subset_mode import (NewMode, OrMode, - AndNotMode, AndMode, XorMode, - ReplaceMode) -from glue.app.qt.actions import action -from glue.utils import nonpartial, avoid_circular -from glue.utils.qt import update_combobox -from glue.core.message import EditSubsetMessage, SubsetMessage -from glue.core.hub import HubListener -from glue.icons.qt import layer_icon - - -class EditSubsetModeToolBar(QtWidgets.QToolBar, HubListener): - - def __init__(self, title="Subset mode toolbar", parent=None): - super(EditSubsetModeToolBar, self).__init__(title, parent) - - self.subset_combo = QtWidgets.QComboBox() - self.subset_combo.setMinimumContentsLength(10) - - spacer = QtWidgets.QWidget() - spacer.setMinimumSize(10, 10) - spacer.setSizePolicy(QtWidgets.QSizePolicy.Fixed, - QtWidgets.QSizePolicy.Preferred) - - self.addWidget(spacer) - self.addWidget(QtWidgets.QLabel("Active Subset:")) - self.addWidget(self.subset_combo) - - self._label_subset_mode = QtWidgets.QLabel("Mode:") - self.addWidget(self._label_subset_mode) - self.setIconSize(QtCore.QSize(16, 16)) - self._group = QtWidgets.QActionGroup(self) - self._modes = {} - self._add_actions() - self._edit_subset_mode = self.parent()._session.edit_subset_mode - self._modes[self._edit_subset_mode.mode].trigger() - self._backup_mode = None - - spacer = QtWidgets.QWidget() - spacer.setMinimumSize(20, 10) - spacer.setSizePolicy(QtWidgets.QSizePolicy.Fixed, - QtWidgets.QSizePolicy.Preferred) - - self.parent()._hub.subscribe(self, EditSubsetMessage, handler=self._update_mode) - self.parent()._hub.subscribe(self, SubsetMessage, handler=self._update_subset_combo) - - self._data_collection = self.parent().data_collection - self._update_subset_combo() - self.subset_combo.currentIndexChanged.connect(self._on_subset_combo_change) - - self._update_mode_visibility() - - def _update_subset_combo(self, msg=None): - """ - Set up the combo listing the subsets. - """ - - # Prepare contents of combo box - we include a 'Create subset' item as - # the last item - labeldata = [(subset.label, subset) for subset in self._data_collection.subset_groups] - labeldata.append(('None/Create New', None)) - - # We now update the combo box, but we block the signals as we don't want - # this to cause the current subset being edited to be modified. - - self.subset_combo.blockSignals(True) - - # The block_signals here is to prevent signals from being turned back - # on inside update_combobox. - update_combobox(self.subset_combo, labeldata, block_signals=False) - self.subset_combo.setIconSize(QtCore.QSize(12, 12)) - for index, subset in enumerate(self._data_collection.subset_groups): - self.subset_combo.setItemIcon(index, layer_icon(subset)) - - self.subset_combo.blockSignals(False) - - def _update_mode_visibility(self): - visible_modes = not self.subset_combo.currentIndex() == self.subset_combo.count() - 1 - if visible_modes: - self._label_subset_mode.setText("Mode:") - else: - self._label_subset_mode.setText("(the next selection will create a subset)") - for mode in self._modes.values(): - if isinstance(mode, QtWidgets.QAction): - mode.setVisible(visible_modes) - - @avoid_circular - def _on_subset_combo_change(self, event=None): - """ - Update the EditSubsetMode when the subset combo changes. - """ - - subset = self.subset_combo.currentData() - - if subset is None: - self._edit_subset_mode.edit_subset = [] - else: - self._edit_subset_mode.edit_subset = [self.subset_combo.currentData()] - if self._edit_subset_mode.mode is NewMode: - self._edit_subset_mode.mode = ReplaceMode - self._update_mode_visibility() - - # We now force the combo to be refreshed in case it included the - # temporary 'Multiple subsets' entry which needs to be removed. - self.subset_combo.blockSignals(True) - self._update_subset_combo() - self.subset_combo.blockSignals(False) - - @avoid_circular - def _on_edit_subset_mode_change(self): - """ - Update the subset combo when the EditSubsetMode changes. - """ - - # We block signals since we don't want to trigger a change in EditSubsetMode. - - self.subset_combo.blockSignals(True) - - edit_subset = self._edit_subset_mode.edit_subset - - if self._edit_subset_mode.mode is NewMode: - index = self.subset_combo.count() - 1 - elif len(edit_subset) > 1: - # We temporarily add an item - we remove this if the combo changes - # again. - self.subset_combo.insertItem(0, 'Multiple subsets') - index = 0 - else: - self._update_subset_combo() - # In some cases, this can be called from _update_subset_combo before - # EditSubsetMode has been updated, so the edit subset may still be - # the old one and not exist in the list of subset groups. - if edit_subset: - if edit_subset[0] in self._data_collection.subset_groups: - index = self._data_collection.subset_groups.index(edit_subset[0]) - else: - index = self.subset_combo.count() - 1 - elif len(edit_subset) == 0: - index = self.subset_combo.count() - 1 - - self.subset_combo.setCurrentIndex(index) - self.subset_combo.blockSignals(False) - - self._update_mode_visibility() - - def _make_mode(self, name, tip, icon, mode): - - def set_mode(mode): - self._edit_subset_mode.mode = mode - - a = action(name, self, tip, icon) - a.setCheckable(True) - a.triggered.connect(nonpartial(set_mode, mode)) - self._group.addAction(a) - self.addAction(a) - self._modes[mode] = a - label = name.split()[0].lower().replace('&', '') - self._modes[label] = mode - - def _add_actions(self): - self._make_mode("&Replace Mode", "Replace selection", - 'glue_replace', ReplaceMode) - self._make_mode("&Or Mode", "Add to selection", - 'glue_or', OrMode) - self._make_mode("&And Mode", "Set selection as intersection", - 'glue_and', AndMode) - self._make_mode("&Xor Mode", "Set selection as exclusive intersection", - 'glue_xor', XorMode) - self._make_mode("&Not Mode", "Remove from selection", - 'glue_andnot', AndNotMode) - - def _update_mode(self, message): - self.set_mode(message.mode) - self._on_edit_subset_mode_change() - - def set_mode(self, mode): - """Temporarily set the edit mode to mode - :param mode: Name of the mode (Or, Not, And, Xor, Replace) - :type mode: str - """ - if mode == 'new' or mode is NewMode: - if self._edit_subset_mode.mode is not NewMode: - self._edit_subset_mode.mode = NewMode - for act in self._modes.values(): - if isinstance(act, QtWidgets.QAction): - act.setChecked(False) - return - elif isinstance(mode, str): - try: - mode = self._modes[mode] # label to mode class - except KeyError: - raise KeyError("Unrecognized mode: %s" % mode) - - self._backup_mode = self._backup_mode or self._edit_subset_mode.mode - self._modes[mode].trigger() # mode class to action - - def unset_mode(self): - """Restore the mode to the state before set_mode was called""" - mode = self._backup_mode - self._backup_mode = None - if mode: - self._modes[mode].trigger() +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.app.qt.edit_subset_mode_toolbar is deprecated, use glue_qt.app.edit_subset_mode_toolbar instead', GlueDeprecationWarning) +from glue_qt.app.edit_subset_mode_toolbar import * # noqa diff --git a/glue/app/qt/feedback.py b/glue/app/qt/feedback.py index 3511bfbf9..0750eb2bb 100644 --- a/glue/app/qt/feedback.py +++ b/glue/app/qt/feedback.py @@ -1,177 +1,4 @@ -""" -Widgets for sending feedback reports -""" -import os - -from qtpy import QtGui, QtWidgets -from urllib.parse import urlencode -from urllib.request import Request, urlopen -from glue.utils.qt import load_ui -from glue._deps import get_status_as_odict - - -__all__ = ['submit_bug_report', 'submit_feedback'] - - -def diagnostics(): - """ - Return a some system informaton useful for debugging - """ - versions = "" - for package, version in get_status_as_odict().items(): - versions += "* {0}: {1}\n".format(package, version) - return versions.strip() - - -class BaseReportWidget(QtWidgets.QDialog): - - def accept(self): - """ - Send a report to bugs.glueviz.org - """ - - # website expects a post request with a report and specific key - url = 'http://bugs.glueviz.org' - - values = dict(report=self.content, key='72z29Q9BzM8sgATeQdu4') - - data = urlencode(values) - - req = Request(url, data.encode('utf-8')) - urlopen(req) - - self.close() - - @property - def comments(self): - return self.ui.area_comments.document().toPlainText() or "No comments" - - @property - def email(self): - return self.ui.value_email.text() or "Not provided" - - -FEEDBACK_TEMPLATE = """ -Email address: {email} - -Comments --------- - -{comments} - -System information ------------------- - -{report} -""" - - -class FeedbackWidget(BaseReportWidget): - """ - A Dialog to enter and send feedback - """ - - def __init__(self, parent=None): - - super(FeedbackWidget, self).__init__(parent=parent) - - self.ui = load_ui('report_feedback.ui', self, - directory=os.path.dirname(__file__)) - - self.ui.area_comments.moveCursor(QtGui.QTextCursor.Start) - - @property - def report(self): - if self.ui.checkbox_system_info.isChecked(): - return diagnostics() - else: - return "No version information provided" - - @property - def content(self): - """ - The contents of the feedback window - """ - return FEEDBACK_TEMPLATE.format(email=self.email, - comments=self.comments, - report=self.report) - - -REPORT_TEMPLATE = """ -Email address: {email} - -Comments --------- - -{comments} - -Report ------- - -{report} -""" - - -class CrashReportWidget(BaseReportWidget): - """ - A dialog to report crashes/errors - """ - - def __init__(self, crash_report='', parent=None): - """ - :param feedback: The default feedback report - :type feedback: str - - Feedback will be supplemented with diagnostic system information. - The user can modify or add to any of this - """ - - super(CrashReportWidget, self).__init__(parent=parent) - - self.ui = load_ui('report_crash.ui', self, - directory=os.path.dirname(__file__)) - - self.ui.area_report.insertPlainText(diagnostics() + "\n\n" + crash_report) - self.ui.area_comments.moveCursor(QtGui.QTextCursor.Start) - - @property - def report(self): - return self.ui.area_report.document().toPlainText() or "No report" - - @property - def content(self): - """ - The contents of the feedback window - """ - return REPORT_TEMPLATE.format(email=self.email, - comments=self.comments, - report=self.report) - - -def submit_bug_report(report=''): - """ - Present a user interface for sending a crash report - - Parameters - ---------- - report : str - The crash report/trackback - """ - widget = CrashReportWidget(crash_report=report) - widget.exec_() - - -def submit_feedback(): - """ - Present a user interface for modifying and sending a feedback message - """ - widget = FeedbackWidget() - widget.exec_() - - -if __name__ == "__main__": - - from glue.utils.qt import get_qapp - app = get_qapp() - submit_bug_report(report="Crash log here") - submit_feedback() +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.app.qt.feedback is deprecated, use glue_qt.app.feedback instead', GlueDeprecationWarning) +from glue_qt.app.feedback import * # noqa diff --git a/glue/app/qt/keyboard_shortcuts.py b/glue/app/qt/keyboard_shortcuts.py index c28f8e21e..5c3917eee 100644 --- a/glue/app/qt/keyboard_shortcuts.py +++ b/glue/app/qt/keyboard_shortcuts.py @@ -1,51 +1,4 @@ -""" -Jesse Averbukh -October 12, 2017 -The file where keyboard shortcuts are created. In the future, many of these -values will be populated using a GUI. -""" - -from qtpy import QtCore -from glue.config import keyboard_shortcut -from glue.config import viewer_tool -from glue.viewers.scatter.qt.data_viewer import ScatterViewer -from glue.viewers.histogram.qt.data_viewer import HistogramViewer -from glue.viewers.image.qt.data_viewer import ImageViewer -from glue.viewers.table.qt.data_viewer import DataTableModel - - -def check_duplicate_shortcut(key_shortcut): - """ - Checks to make sure that a key_shortcut is not already used within the - glue application somewhere else. - This will become simpler with the implementation of a GUI - """ - list_of_shortcuts = [] - for k in viewer_tool.__iter__(): - list_of_shortcuts.append(viewer_tool.members[k].shortcut) - - if key_shortcut in list_of_shortcuts: - return True - return False - - -@keyboard_shortcut(QtCore.Qt.Key_Tab, [ImageViewer, HistogramViewer, ScatterViewer, DataTableModel]) -def cycle_through_windows(session): - """ - Cycle through all active windows within the current tab - """ - if check_duplicate_shortcut("tab"): - return - - return session.application.current_tab.activateNextSubWindow() - - -@keyboard_shortcut(QtCore.Qt.Key_Backspace, [ImageViewer, HistogramViewer, ScatterViewer, DataTableModel]) -def delete_current_window(session): - """ - Deletes the currently active window - """ - if check_duplicate_shortcut("backspace"): - return - - return session.application._viewer_in_focus.close(warn=True) +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.app.qt.keyboard_shortcuts is deprecated, use glue_qt.app.keyboard_shortcuts instead', GlueDeprecationWarning) +from glue_qt.app.keyboard_shortcuts import * # noqa diff --git a/glue/app/qt/layer_tree_widget.py b/glue/app/qt/layer_tree_widget.py index 8fca8eab1..c1d0b0910 100644 --- a/glue/app/qt/layer_tree_widget.py +++ b/glue/app/qt/layer_tree_widget.py @@ -1,659 +1,4 @@ -""" -Class which embellishes the DataCollectionView with buttons and actions for -editing the data collection -""" - -import os -import weakref - -from qtpy import QtCore, QtWidgets, QtGui -from qtpy.QtCore import Qt - -from glue.core.edit_subset_mode import AndMode, OrMode, XorMode, AndNotMode -from glue.config import layer_action -from glue import core -from glue.dialogs.link_editor.qt import LinkEditor -from glue.icons.qt import get_icon -from glue.dialogs.component_arithmetic.qt import ArithmeticEditorWidget -from glue.dialogs.component_manager.qt import ComponentManagerWidget -from glue.dialogs.subset_facet.qt import SubsetFacetDialog -from glue.dialogs.data_wizard.qt import data_wizard -from glue.utils import nonpartial -from glue.utils.decorators import avoid_circular -from glue.utils.qt import load_ui -from glue.core.message import EditSubsetMessage -from glue.core.hub import HubListener -from glue.app.qt.metadata import MetadataDialog - - -@core.decorators.singleton -class Clipboard(object): - - def __init__(self): - self.contents = None - - -class LayerAction(QtWidgets.QAction): - _title = '' - _icon = None - _tooltip = None - _enabled_on_init = False - _shortcut = None - _shortcut_context = Qt.WidgetShortcut - - def __init__(self, layer_tree_widget): - self._parent = layer_tree_widget.ui.layerTree - super(LayerAction, self).__init__(self._title, self._parent) - self._layer_tree = layer_tree_widget - if self._icon: - self.setIcon(get_icon(self._icon)) - if self._tooltip: - self.setToolTip(self._tooltip) - self.setEnabled(self._enabled_on_init) - if self._shortcut_context is not None: - self.setShortcutContext(self._shortcut_context) - if self._shortcut: - self.setShortcut(self._shortcut) - self._parent.addAction(self) - self._connect() - self.setIconVisibleInMenu(False) - - def _connect(self): - self._parent.selection_changed.connect(nonpartial(self.update_enabled)) - self.triggered.connect(nonpartial(self._do_action)) - - def selected_layers(self): - return self._layer_tree.selected_layers() - - @property - def data_collection(self): - return self._layer_tree.data_collection - - def update_enabled(self): - enabled = self._can_trigger() - self.setEnabled(enabled) - self.setVisible(enabled) - - def single_selection(self): - return len(self.selected_layers()) == 1 - - def single_selection_data(self): - layers = self.selected_layers() - if len(layers) != 1: - return False - return isinstance(layers[0], core.Data) - - def single_selection_subset(self): - layers = self.selected_layers() - if len(layers) != 1: - return False - return isinstance(layers[0], core.Subset) - - def single_selection_subset_group(self): - layers = self.selected_layers() - if len(layers) != 1: - return False - return isinstance(layers[0], core.SubsetGroup) - - def _can_trigger(self): - raise NotImplementedError - - def _do_action(self): - raise NotImplementedError - - -class PlotAction(LayerAction): - - """Visualize the selection. Requires GlueApplication""" - _title = "Create new viewer" - _tooltip = "Create a new viewer from this object" - - def __init__(self, tree, app): - super(PlotAction, self).__init__(tree) - self.app = weakref.ref(app) - - def _can_trigger(self): - if not self.single_selection(): - return False - return isinstance(self.selected_layers()[0], (core.Subset, core.Data)) - - def _do_action(self): - assert self._can_trigger() - data = self.selected_layers()[0].data - app = self.app() - if app is not None: - app.choose_new_data_viewer(data) - - -class FacetAction(LayerAction): - - """Add a sequence of subsets which facet a ComponentID""" - _title = "Create faceted subsets" - _tooltip = "Create faceted subsets" - - def _can_trigger(self): - return len(self._layer_tree.data_collection) > 0 - - def _do_action(self): - layers = self.selected_layers() - try: - default = layers[0].data - except (AttributeError, TypeError, IndexError): - default = None - SubsetFacetDialog.facet(self._layer_tree.data_collection, - parent=self._layer_tree, default=default) - - -class MetadataAction(LayerAction): - - _title = "View metadata/header" - _tooltip = "View metadata/header" - _shortcut = QtGui.QKeySequence('Ctrl+I') - - def _can_trigger(self): - return self.single_selection_data() or self.single_selection_subset() - - def _do_action(self): - layers = self.selected_layers() - if isinstance(layers[0], core.Subset): - data = layers[0].data - else: - data = layers[0] - MetadataDialog(data).exec_() - - -class NewAction(LayerAction): - _title = "New Subset" - _tooltip = "Create a new subset" - _shortcut = QtGui.QKeySequence('Ctrl+Shift+N') - _icon = 'glue_subset' - - def _can_trigger(self): - return len(self.data_collection) > 0 - - def _do_action(self): - assert self._can_trigger() - self.data_collection.new_subset_group() - - -class ClearAction(LayerAction): - _title = "Clear subset" - _tooltip = "Clear current subset" - _shortcut = QtGui.QKeySequence('Ctrl+K') - - def _can_trigger(self): - return self.single_selection_subset_group() - - def _do_action(self): - assert self._can_trigger() - subset = self.selected_layers()[0] - subset.subset_state = core.subset.SubsetState() - - -class DeleteAction(LayerAction): - _title = "Delete Layer" - _tooltip = "Delete the selected data and/or subset Groups" - _shortcut = QtGui.QKeySequence(Qt.Key_Backspace) - - def _can_trigger(self): - selection = self.selected_layers() - return all(isinstance(s, (core.Data, core.SubsetGroup)) - for s in selection) - - def _do_action(self): - assert self._can_trigger() - selection = self.selected_layers() - for s in selection: - if isinstance(s, core.Data): - self._layer_tree.data_collection.remove(s) - else: - assert isinstance(s, core.SubsetGroup) - self._layer_tree.data_collection.remove_subset_group(s) - - -class LinkAction(LayerAction): - _title = "Link Data" - _tooltip = "Define links between data sets" - _data_link_message = "Define links between data sets" - _icon = 'glue_link' - - def __init__(self, *args, **kwargs): - super(LinkAction, self).__init__(*args, **kwargs) - - def _can_trigger(self): - return len(self.data_collection) > 0 - - def _do_action(self): - LinkEditor.update_links(self.data_collection) - - -class MaskifySubsetAction(LayerAction): - _title = "Transform subset to pixel mask" - _tooltip = "Transform a subset to a pixel mask" - - def _can_trigger(self): - return self.single_selection() and \ - isinstance(self.selected_layers()[0], core.Subset) - - def _do_action(self): - s = self.selected_layers()[0] - s.subset_state = s.state_as_mask() - - -class ExportDataAction(LayerAction): - - _title = "Export data values" - _tooltip = "Save the data to a file" - - def _can_trigger(self): - return self.single_selection_data() - - def _do_action(self): - assert self._can_trigger() - data = self.selected_layers()[0] - from glue.core.data_exporters.qt.dialog import export_data - export_data(data) - - -class ArithmeticAction(LayerAction): - - _title = "Add/edit arithmetic attributes" - _tooltip = "Add/edit attributes derived from existing ones" - - def _can_trigger(self): - return self.single_selection_data() - - def _do_action(self): - assert self._can_trigger() - data = self.selected_layers()[0] - dialog = ArithmeticEditorWidget(self._layer_tree.data_collection, - initial_data=data) - dialog.exec_() - - -class ManageComponentsAction(LayerAction): - - _title = "Reorder/rename data attributes" - _tooltip = "Reorder/rename data attributes" - - def _can_trigger(self): - return self.single_selection_data() - - def _do_action(self): - assert self._can_trigger() - data = self.selected_layers()[0] - dialog = ComponentManagerWidget(self._layer_tree.data_collection, - initial_data=data) - dialog.exec_() - - -class ExportSubsetAction(ExportDataAction): - - _title = "Export subset values" - _tooltip = "Save the data subset to a file" - - def _can_trigger(self): - return self.single_selection_subset() - - -class ImportSubsetMaskAction(LayerAction): - - _title = "Import subset mask(s)" - _tooltip = "Import subset mask from a file" - - def _can_trigger(self): - return self.single_selection_subset() or self.single_selection_data() - - def _do_action(self): - assert self._can_trigger() - data = self.selected_layers()[0] - from glue.io.qt.subset_mask import QtSubsetMaskImporter - QtSubsetMaskImporter().run(data, self._layer_tree._data_collection) - - -class ExportSubsetMaskAction(LayerAction): - - _title = "Export subset mask(s)" - _tooltip = "Export subset mask to a file" - - def _can_trigger(self): - return (len(self.data_collection.subset_groups) > 0 and - (self.single_selection_subset() or self.single_selection_data())) - - def _do_action(self): - assert self._can_trigger() - data = self.selected_layers()[0] - from glue.io.qt.subset_mask import QtSubsetMaskExporter - QtSubsetMaskExporter().run(data) - - -class CopyAction(LayerAction): - _title = "Copy subset" - _tooltip = "Copy the definition for the selected subset" - _shortcut = QtGui.QKeySequence.Copy - - def _can_trigger(self): - return self.single_selection_subset_group() - - def _do_action(self): - assert self._can_trigger() - subset = self.selected_layers()[0] - Clipboard().contents = subset.subset_state - - -class PasteAction(LayerAction): - _title = "Paste subset" - _tooltip = "Overwrite selected subset with contents from clipboard" - _shortcut = QtGui.QKeySequence.Paste - - def _can_trigger(self): - if not self.single_selection_subset_group(): - return False - cnt = Clipboard().contents - if not isinstance(cnt, core.subset.SubsetState): - return False - return True - - def _do_action(self): - assert self._can_trigger() - layer = self.selected_layers()[0] - layer.paste(Clipboard().contents) - - -class PasteSpecialAction(PasteAction): - _title = "Paste Special..." - _tooltip = "Paste with boolean logic" - _shortcut = None - - def __init__(self, *args, **kwargs): - super(PasteSpecialAction, self).__init__(*args, **kwargs) - self.setMenu(self.menu()) - - def menu(self): - m = QtWidgets.QMenu() - - a = QtWidgets.QAction("Or", m) - a.setIcon(get_icon('glue_or')) - a.triggered.connect(nonpartial(self._paste, OrMode)) - m.addAction(a) - - a = QtWidgets.QAction("And", m) - a.setIcon(get_icon('glue_and')) - a.triggered.connect(nonpartial(self._paste, AndMode)) - m.addAction(a) - - a = QtWidgets.QAction("XOR", m) - a.setIcon(get_icon('glue_xor')) - a.triggered.connect(nonpartial(self._paste, XorMode)) - m.addAction(a) - - a = QtWidgets.QAction("Not", m) - a.setIcon(get_icon('glue_andnot')) - a.triggered.connect(nonpartial(self._paste, AndNotMode)) - m.addAction(a) - return m - - def _paste(self, mode): - if not self._can_trigger(): - return - assert self._can_trigger() - layer = self.selected_layers()[0] - mode(layer, Clipboard().contents) - - def _do_action(self): - pass - - -class Inverter(LayerAction): - _title = "Invert Subset" - _tooltip = "Invert selected subset" - - def _can_trigger(self): - """ Can trigger iff one subset is selected """ - return self.single_selection_subset_group() - - def _do_action(self): - """Replace selected subset with its inverse""" - assert self._can_trigger() - subset, = self.selected_layers() - subset.subset_state = core.subset.InvertState(subset.subset_state) - - -class MergeAction(LayerAction): - _title = "Merge datasets" - _tooltip = "Merge the selected datasets into a single dataset" - - def _can_trigger(self): - layers = self.selected_layers() - if len(layers) < 2: - return False - - if not all(isinstance(l, core.Data) for l in layers): - return False - - shp = layers[0].shape - return all(d.shape == shp for d in layers[1:]) - - def _do_action(self): - self.data_collection.merge(*self.selected_layers()) - - -class UserAction(LayerAction): - """ - User-defined callback functions to expose. - - Users register new actions via the :member:`glue.config.layer_action` member - - Callback functions are passed the layer (or list of layers) and data - collection - """ - - def __init__(self, layer_tree_widget, callback=None, label='User Action', - tooltip=None, icon=None, single=False, data=False, - subset_group=False, subset=False): - self._title = label - self._tooltip = tooltip - self._icon = icon - self._single = single - self._data = data - self._subset_group = subset_group - self._subset = subset - self._callback = callback - super(UserAction, self).__init__(layer_tree_widget) - - def _can_trigger(self): - if self._single: - if self.single_selection(): - layer = self.selected_layers()[0] - if isinstance(layer, core.Data) and self._data: - return True - elif isinstance(layer, core.SubsetGroup) and self._subset_group: - return True - elif isinstance(layer, core.Subset) and self._subset: - return True - else: - return False - else: - return False - else: - return len(self.selected_layers()) > 0 - - def _do_action(self): - if self._single: - subset = self.selected_layers()[0] - return self._callback(subset, self.data_collection) - else: - return self._callback(self.selected_layers(), self.data_collection) - - -class LayerCommunicator(QtCore.QObject): - layer_check_changed = QtCore.Signal(object, bool) - - -class LayerTreeWidget(QtWidgets.QMainWindow, HubListener): - - """The layertree widget provides a way to visualize the various - data and subset layers in a Glue session. - - This widget relies on sending/receiving messages to/from the hub - to maintin synchronization with the data collection it manages. If - it isn't attached to a hub, interactions may not propagate properly. - """ - - def __init__(self, session=None, parent=None): - - super(LayerTreeWidget, self).__init__(parent) - - self.session = session - - self.ui = load_ui('layer_tree_widget.ui', None, directory=os.path.dirname(__file__)) - self.setCentralWidget(self.ui) - - self._signals = LayerCommunicator() - self._is_checkable = True - self._layer_check_changed = self._signals.layer_check_changed - self._layer_dict = {} - - self._actions = {} - - self._create_actions() - self._data_collection = None - self._hub = None - self.ui.layerTree.setDragEnabled(True) - self.setMinimumSize(300, 0) - - @property - def data_collection(self): - return self._data_collection - - def setup(self, collection): - self._data_collection = collection - self._hub = collection.hub - self.ui.layerTree.set_data_collection(collection) - self.bind_selection_to_edit_subset() - - def unregister(self, hub): - """Unsubscribe from hub""" - self.ui.layerTree.unregister(hub) - - def is_checkable(self): - """ Return whether checkboxes appear next o layers""" - return self.ui.layerTree.checkable - - def set_checkable(self, state): - """ Setw hether checkboxes appear next o layers""" - self.ui.layerTree.checkable = state - - def selected_layers(self): - """ Return a list of selected layers (subsets and data objects) """ - return self.ui.layerTree.selected_layers() - - def current_layer(self): - """Return the layer if a single item is selected, else None """ - layers = self.selected_layers() - if len(layers) == 1: - return layers[0] - - def actions(self): - """ Return the list of actions attached to this widget """ - return self.ui.layerTree.actions() - - def bind_selection_to_edit_subset(self): - self.ui.layerTree.selection_changed.connect(self._update_edit_subset) - self._data_collection.hub.subscribe(self, EditSubsetMessage, - handler=self._update_subset_selection) - - @avoid_circular - def _update_subset_selection(self, message): - """ - Update current selection to match edit subsets - """ - self.ui.layerTree.set_selected_layers(message.subset) - - @avoid_circular - def _update_edit_subset(self): - """ - Update edit subsets to match current selection - """ - mode = self.session.edit_subset_mode - mode.edit_subset = [s for s in self.selected_layers() if isinstance(s, core.SubsetGroup)] - - def _create_actions(self): - tree = self.ui.layerTree - - sep = QtWidgets.QAction("", tree) - sep.setSeparator(True) - tree.addAction(sep) - - # Actions relating to I/O - self._actions['save_data'] = ExportDataAction(self) - self._actions['save_subset'] = ExportSubsetAction(self) - self._actions['import_subset_mask'] = ImportSubsetMaskAction(self) - self._actions['export_subset_mask'] = ExportSubsetMaskAction(self) - - self._actions['copy'] = CopyAction(self) - self._actions['paste'] = PasteAction(self) - try: - self._actions['paste_special'] = PasteSpecialAction(self) - except AttributeError: - # On some PyQt6 versions setMenu does not exist - pass - self._actions['invert'] = Inverter(self) - self._actions['new'] = NewAction(self) - self._actions['clear'] = ClearAction(self) - self._actions['delete'] = DeleteAction(self) - self._actions['facet'] = FacetAction(self) - self._actions['metadata'] = MetadataAction(self) - self._actions['merge'] = MergeAction(self) - self._actions['maskify'] = MaskifySubsetAction(self) - self._actions['link'] = LinkAction(self) - self._actions['new_component'] = ArithmeticAction(self) - self._actions['manage_components'] = ManageComponentsAction(self) - - # Add user-defined layer actions. Note that _asdict is actually a public - # method, but just has an underscore to prevent conflict with - # namedtuple attributes. - for item in layer_action: - self._actions[item.label] = UserAction(self, **item._asdict()) - - # right click pulls up menu - tree.setContextMenuPolicy(Qt.ActionsContextMenu) - - def _on_item_change(self, item, column): - """emit check_state_changed signal when checkbox clicked""" - if item is None or item not in self or column != 0: - return - is_checked = item.checkState(0) == Qt.Checked - layer = self[item] - self._layer_check_changed.emit(layer, is_checked) - - def _load_data(self): - """ Interactively loads data from a data set. Adds - as new layer """ - layers = data_wizard() - self.data_collection.extend(layers) - - def __getitem__(self, key): - raise NotImplementedError() - return self.ui.layerTree[key] - - def __setitem__(self, key, value): - raise NotImplementedError() - self.ui.layerTree[key] = value - - def __contains__(self, obj): - return obj in self.ui.layerTree - - def __len__(self): - return len(self.ui.layerTree) - - -if __name__ == "__main__": - from glue.core.data_collection import DataCollection - collection = DataCollection() - from glue.utils.qt import get_qapp - app = get_qapp() - widget = LayerTreeWidget() - widget.setup(collection) - widget.show() - app.exec_() +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.app.qt.layer_tree_widget is deprecated, use glue_qt.app.layer_tree_widget instead', GlueDeprecationWarning) +from glue_qt.app.layer_tree_widget import * # noqa diff --git a/glue/app/qt/layer_tree_widget.ui b/glue/app/qt/layer_tree_widget.ui deleted file mode 100644 index ecc0bc0c2..000000000 --- a/glue/app/qt/layer_tree_widget.ui +++ /dev/null @@ -1,47 +0,0 @@ - - - LayerTree - - - - 0 - 0 - 266 - 185 - - - - Form - - - - 2 - - - 5 - - - - - - 0 - 10 - - - - QAbstractItemView::ExtendedSelection - - - - - - - - DataCollectionView - QTreeView -
glue.core.qt.data_collection_model
-
-
- - -
diff --git a/glue/app/qt/mdi_area.py b/glue/app/qt/mdi_area.py index 11f131a89..54092e4ab 100644 --- a/glue/app/qt/mdi_area.py +++ b/glue/app/qt/mdi_area.py @@ -1,118 +1,4 @@ -import weakref - -from qtpy.QtCore import Qt -from qtpy import QtCore, QtGui, QtWidgets -from glue import core -from glue.core.qt.mime import LAYER_MIME_TYPE, LAYERS_MIME_TYPE - - -class GlueMdiArea(QtWidgets.QMdiArea): - - """Glue's MdiArea implementation. - - Drop events with :class:`~glue.core.data.Data` objects in - :class:`~glue.utils.qt.PyMimeData` load these objects into new - data viewers - """ - - def __init__(self, application, parent=None): - """ - :param application: The Glue application to which this is attached - :type application: :class:`~glue.app.qt.application.GlueApplication` - """ - super(GlueMdiArea, self).__init__(parent) - self._application = weakref.ref(application) - self.setAcceptDrops(True) - self.setAttribute(Qt.WA_DeleteOnClose) - self.setBackground(QtGui.QBrush(QtGui.QColor(250, 250, 250))) - self.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) - self.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) - - def addSubWindow(self, sub): - super(GlueMdiArea, self).addSubWindow(sub) - self.repaint() - - def dragEnterEvent(self, event): - """ Accept the event if it has an application/py_instance format """ - - if event.mimeData().hasFormat(LAYERS_MIME_TYPE): - event.accept() - elif event.mimeData().hasFormat(LAYER_MIME_TYPE): - event.accept() - else: - event.ignore() - - def dropEvent(self, event): - """ Load a new data viewer if the event has a glue Data object """ - md = event.mimeData() - - def new_layer(layer): - application = self._application() - if application is None: - return - if isinstance(layer, (core.data.BaseData, core.subset.Subset)): - application.choose_new_data_viewer(layer) - else: - raise TypeError("Expected a Data or Subset, got {0}".format(type(layer))) - - if md.hasFormat(LAYER_MIME_TYPE): - new_layer(md.data(LAYER_MIME_TYPE)) - - assert md.hasFormat(LAYERS_MIME_TYPE) - for layer in md.data(LAYERS_MIME_TYPE): - new_layer(layer) - - event.accept() - - def close(self): - self.closeAllSubWindows() - super(GlueMdiArea, self).close() - - def paintEvent(self, event): - super(GlueMdiArea, self).paintEvent(event) - - painter = QtGui.QPainter(self.viewport()) - painter.setPen(QtGui.QColor(210, 210, 210)) - font = painter.font() - font.setPointSize(font.pointSize() * 4) - font.setWeight(font.Black) - painter.setFont(font) - rect = self.contentsRect() - painter.drawText(rect, Qt.AlignHCenter | Qt.AlignVCenter, - "Drag Data To Plot") - - def wheelEvent(self, event): - - # NOTE: when a scroll wheel event happens on top of a GlueMdiSubWindow, - # we need to ignore it in GlueMdiArea to prevent the canvas from moving - # around. I couldn't find a clean way to do this with events, so instead - # in GlueMdiSubWindow I set a flag, _wheel_event, to indicate that a - # wheel event has happened in a subwindow, which means the next time - # the GlueMdiArea.wheelEvent gets called, we should ignore the wheel - # event. - - any_subwindow_wheel = False - - for window in self.subWindowList(): - if getattr(window, '_wheel_event', None): - any_subwindow_wheel = True - window._wheel_event = None - - if any_subwindow_wheel: - event.ignore() - return - - super(GlueMdiArea, self).wheelEvent(event) - - -class GlueMdiSubWindow(QtWidgets.QMdiSubWindow): - closed = QtCore.Signal() - - def wheelEvent(self, event): - # See NOTE in GlueMdiArea.wheelEvent - self._wheel_event = True - super(GlueMdiSubWindow, self).wheelEvent(event) - - def closeEvent(self, event): - super(GlueMdiSubWindow, self).closeEvent(event) - self.closed.emit() +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.app.qt.mdi_area is deprecated, use glue_qt.app.mdi_area instead', GlueDeprecationWarning) +from glue_qt.app.mdi_area import * # noqa diff --git a/glue/app/qt/merge.ui b/glue/app/qt/merge.ui deleted file mode 100644 index 0f516c19b..000000000 --- a/glue/app/qt/merge.ui +++ /dev/null @@ -1,94 +0,0 @@ - - - MergeDialog - - - - 0 - 0 - 443 - 410 - - - - Merge Datasets - - - true - - - - 8 - - - - - Several of the datasets (including at least one that you have added) have the same shape. Do you want to merge them? Merging means that these separate datasets will then become attributes of a single dataset. - - - false - - - Qt::AlignJustify|Qt::AlignVCenter - - - true - - - - - - - - - - - - Final label - - - - - - - - - - - - - - Do not merge - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Merge the selected datasets - - - true - - - - - - - - - - diff --git a/glue/app/qt/metadata.py b/glue/app/qt/metadata.py index 231c71d31..44ace5293 100644 --- a/glue/app/qt/metadata.py +++ b/glue/app/qt/metadata.py @@ -1,36 +1,4 @@ -import os -from collections import OrderedDict - -from qtpy import QtWidgets -from qtpy.QtCore import Qt - -from glue.utils.qt import load_ui, CenteredDialog - -__all__ = ['MetadataDialog'] - - -class MetadataDialog(CenteredDialog): - """ - A dialog to view the metadata in a data object. - """ - - def __init__(self, data, *args, **kwargs): - - super(MetadataDialog, self).__init__(*args, **kwargs) - - self.ui = load_ui('metadata.ui', self, directory=os.path.dirname(__file__)) - - self.resize(400, 500) - self.setWindowFlags(Qt.Window | Qt.WindowStaysOnTopHint) - - self._text = "" - for name, value in OrderedDict(data.meta).items(): - QtWidgets.QTreeWidgetItem(self.ui.meta_tree.invisibleRootItem(), [name, str(value)]) - - if data.label: - self.setWindowTitle("Metadata for {0}".format(data.label)) - - self.ui.label_ndim.setText(str(data.ndim)) - self.ui.label_shape.setText(str(data.shape)) - - self.center() +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.app.qt.metadata is deprecated, use glue_qt.app.metadata instead', GlueDeprecationWarning) +from glue_qt.app.metadata import * # noqa diff --git a/glue/app/qt/metadata.ui b/glue/app/qt/metadata.ui deleted file mode 100644 index 3bacf1154..000000000 --- a/glue/app/qt/metadata.ui +++ /dev/null @@ -1,133 +0,0 @@ - - - Dialog - - - - 0 - 0 - 362 - 397 - - - - Metadata viewer - - - false - - - - - - 5 - - - - - ndim - - - - - - - shape - - - - - - - Qt::Horizontal - - - - 40 - 5 - - - - - - - - Data shape: - - - - - - - Number of dimensions: - - - - - - - Qt::Horizontal - - - - 40 - 5 - - - - - - - - - - Qt::Vertical - - - QSizePolicy::Fixed - - - - 5 - 5 - - - - - - - - true - - - QAbstractItemView::NoSelection - - - true - - - true - - - false - - - false - - - - Keyword - - - - - Value - - - - - - - - - diff --git a/glue/app/qt/plugin_manager.py b/glue/app/qt/plugin_manager.py index 1f544f369..7ba92f677 100644 --- a/glue/app/qt/plugin_manager.py +++ b/glue/app/qt/plugin_manager.py @@ -1,73 +1,4 @@ -import os - -from qtpy.QtCore import Qt -from qtpy import QtWidgets -from glue._plugin_helpers import PluginConfig -from glue.utils.qt import load_ui - - -__all__ = ["QtPluginManager"] - - -class QtPluginManager(object): - - def __init__(self, installed=None): - - self.ui = load_ui('plugin_manager.ui', None, - directory=os.path.dirname(__file__)) - - self.ui.cancel.clicked.connect(self.reject) - self.ui.confirm.clicked.connect(self.finalize) - - self._checkboxes = {} - - self.update_list(installed=installed) - - def clear(self): - self._checkboxes.clear() - self.ui.tree.clear() - - def update_list(self, installed=None): - - self.clear() - - config = PluginConfig.load() - if installed is not None: - config.filter(installed) - - for plugin in sorted(config.plugins): - check = QtWidgets.QTreeWidgetItem(self.ui.tree.invisibleRootItem(), - ["", plugin]) - check.setFlags(check.flags() | Qt.ItemIsUserCheckable) - if config.plugins[plugin]: - check.setCheckState(0, Qt.Checked) - else: - check.setCheckState(0, Qt.Unchecked) - self._checkboxes[plugin] = check - - self.ui.tree.resizeColumnToContents(0) - self.ui.tree.resizeColumnToContents(1) - - def reject(self): - self.ui.reject() - - def finalize(self): - - config = PluginConfig.load() - - for name in self._checkboxes: - config.plugins[name] = self._checkboxes[name].checkState(0) == Qt.Checked - - try: - config.save() - except Exception: - import traceback - detail = str(traceback.format_exc()) - message = QtWidgets.QMessageBox(QtWidgets.QMessageBox.Critical, - "Error", - "Could not save plugin configuration") - message.setDetailedText(detail) - message.exec_() - return - - self.ui.accept() +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.app.qt.plugin_manager is deprecated, use glue_qt.app.plugin_manager instead', GlueDeprecationWarning) +from glue_qt.app.plugin_manager import * # noqa diff --git a/glue/app/qt/plugin_manager.ui b/glue/app/qt/plugin_manager.ui deleted file mode 100644 index da382bd66..000000000 --- a/glue/app/qt/plugin_manager.ui +++ /dev/null @@ -1,115 +0,0 @@ - - - Dialog - - - - 0 - 0 - 405 - 478 - - - - Plugin Manager - - - false - - - - - - - - Select plugins to enable them, de-select to disable - - - Qt::AlignCenter - - - - - - - - - Qt::ScrollBarAlwaysOff - - - true - - - true - - - true - - - - Plugin Name - - - - - Select - - - - - - - - - - Selections will take effect next time Glue is restarted - - - Qt::AlignCenter - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Cancel - - - - - - - Save Configuration - - - true - - - false - - - - - - - - - - - - diff --git a/glue/app/qt/preferences.py b/glue/app/qt/preferences.py index 684f4e0bc..30383ddba 100644 --- a/glue/app/qt/preferences.py +++ b/glue/app/qt/preferences.py @@ -1,187 +1,4 @@ -import os -import weakref -import platform -from collections import OrderedDict - -import numpy as np -from matplotlib.colors import ColorConverter - -from qtpy import QtWidgets -from glue.core.message import SettingsChangeMessage -from glue.utils.qt import load_ui, ColorProperty, get_qapp -from glue.utils.qt.widget_properties import (CurrentComboTextProperty, - ValueProperty, ButtonProperty) -from glue._settings_helpers import save_settings - -__all__ = ["PreferencesDialog"] - -rgb = ColorConverter().to_rgb - -AUTOLINK_OPTIONS = OrderedDict([ - ('always_show', 'Always show suggestions'), - ('always_accept', 'Always accept suggestions'), - ('always_ignore', 'Always ignore suggestions') -]) - - -class AutolinkPreferencesPane(QtWidgets.QWidget): - - def __init__(self, parent=None): - super(AutolinkPreferencesPane, self).__init__(parent=parent) - - from glue.config import settings, autolinker # noqa - - layout = QtWidgets.QGridLayout() - - self.combos = {} - - if len(autolinker) > 0: - - for i, (label, _) in enumerate(autolinker): - combo = QtWidgets.QComboBox() - for short, display in AUTOLINK_OPTIONS.items(): - combo.addItem(display, userData=short) - if label in settings.AUTOLINK: - index = list(AUTOLINK_OPTIONS.keys()).index(settings.AUTOLINK[label]) - else: - index = 0 - combo.setCurrentIndex(index) - layout.addWidget(QtWidgets.QLabel(label), i, 0) - layout.addWidget(combo, i, 1) - self.combos[label] = combo - - layout.addWidget(QtWidgets.QWidget(), i + 1, 0) - - self.setLayout(layout) - - def finalize(self): - from glue.config import settings - for label, combo in self.combos.items(): - settings.AUTOLINK[label] = combo.currentData() - - -class PreferencesDialog(QtWidgets.QDialog): - - theme = CurrentComboTextProperty('ui.combo_theme') - background = ColorProperty('ui.color_background') - foreground = ColorProperty('ui.color_foreground') - data_color = ColorProperty('ui.color_default_data') - data_alpha = ValueProperty('ui.slider_alpha', value_range=(0, 1)) - data_apply = ButtonProperty('ui.checkbox_apply') - save_to_disk = ButtonProperty('ui.checkbox_save') - font_size = ValueProperty('ui.spinner_font_size') - - def __init__(self, application, parent=None): - - super(PreferencesDialog, self).__init__(parent=parent) - - self._app = weakref.ref(application) - - self.ui = load_ui('preferences.ui', self, - directory=os.path.dirname(__file__)) - - self.ui.cancel.clicked.connect(self.reject) - self.ui.ok.clicked.connect(self.accept) - - self.ui.combo_theme.currentIndexChanged.connect(self._update_colors_from_theme) - - self.ui.button_reset_dialogs.clicked.connect(self._reset_dialogs) - - # The following is needed because of a bug in Qt which means that - # tab titles don't get scaled right. - if platform.system() == 'Darwin': - app = get_qapp() - app_font = app.font() - self.ui.tab_widget.setStyleSheet('font-size: {0}px'.format(app_font.pointSize())) - - from glue.config import settings - self.background = settings.BACKGROUND_COLOR - self.foreground = settings.FOREGROUND_COLOR - self.data_color = settings.DATA_COLOR - self.data_alpha = settings.DATA_ALPHA - self.font_size = settings.FONT_SIZE - - self._update_theme_from_colors() - - self._autolink_pane = AutolinkPreferencesPane() - self.ui.tab_widget.addTab(self._autolink_pane, 'Autolinking') - - self.panes = [] - - from glue.config import preference_panes - for label, widget_cls in sorted(preference_panes): - pane = widget_cls() - self.ui.tab_widget.addTab(pane, label) - self.panes.append(pane) - - def _update_theme_from_colors(self, *args): - if (rgb(self.background) == (1, 1, 1) and rgb(self.foreground) == (0, 0, 0) and - rgb(self.data_color) == (0.35, 0.35, 0.35) and np.allclose(self.data_alpha, 0.8)): - self.theme = 'Black on White' - elif (rgb(self.background) == (0, 0, 0) and rgb(self.foreground) == (1, 1, 1) and - rgb(self.data_color) == (0.75, 0.75, 0.75) and np.allclose(self.data_alpha, 0.8)): - self.theme = 'White on Black' - else: - self.theme = 'Custom' - - def _update_colors_from_theme(self, *args): - if self.theme == 'Black on White': - self.foreground = 'black' - self.background = 'white' - self.data_color = '0.35' - self.data_alpha = 0.8 - elif self.theme == 'White on Black': - self.foreground = 'white' - self.background = 'black' - self.data_color = '0.75' - self.data_alpha = 0.8 - elif self.theme != 'Custom': - raise ValueError("Unknown theme: {0}".format(self.theme)) - - def _reset_dialogs(self, *args): - from glue.config import settings - for key, _, _ in settings: - if key.lower().startswith(('show_info', 'show_warn', 'show_large')): - setattr(settings, key, True) - - def accept(self): - - # Update default settings - - from glue.config import settings - settings.FOREGROUND_COLOR = self.foreground - settings.BACKGROUND_COLOR = self.background - settings.DATA_COLOR = self.data_color - settings.DATA_ALPHA = self.data_alpha - settings.FONT_SIZE = self.font_size - - self._autolink_pane.finalize() - - for pane in self.panes: - pane.finalize() - - # Save to disk if requested - if self.save_to_disk: - save_settings() - else: - settings._save_to_disk = True - - # Trigger viewers to update defaults - - app = self._app() - - if app is not None: - app._hub.broadcast(SettingsChangeMessage(self, ('FOREGROUND_COLOR', 'BACKGROUND_COLOR', 'FONT_SIZE'))) - if self.data_apply: # If requested, trigger data to update color - app.set_data_color(settings.DATA_COLOR, settings.DATA_ALPHA) - - super(PreferencesDialog, self).accept() - - -if __name__ == "__main__": - - app = get_qapp() - widget = PreferencesDialog() - widget.show() - widget.raise_() - app.exec_() +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.app.qt.preferences is deprecated, use glue_qt.app.preferences instead', GlueDeprecationWarning) +from glue_qt.app.preferences import * # noqa diff --git a/glue/app/qt/preferences.ui b/glue/app/qt/preferences.ui deleted file mode 100644 index 04956bab7..000000000 --- a/glue/app/qt/preferences.ui +++ /dev/null @@ -1,320 +0,0 @@ - - - Form - - - - 0 - 0 - 462 - 375 - - - - Preferences - - - true - - - - - - - - Save preferences to disk - - - true - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Cancel - - - - - - - OK - - - true - - - - - - - - - 0 - - - - User interface - - - - - - Global Font Size - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Reset visibility of info messages and warnings - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - Qt::Horizontal - - - - - - - Default data color: - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - - - - - - - - - - - Apply to existing datasets: - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - Foreground color: - - - - - - - 100 - - - Qt::Horizontal - - - - - - - - - - true - - - - - - - Default data transparency: - - - - - - - QComboBox::AdjustToMinimumContentsLengthWithIcon - - - - Black on White - - - - - White on Black - - - - - Custom - - - - - - - - Qt::Horizontal - - - - - - - Background color: - - - - - - - - - - - - - - Theme for viewers: - - - - - - - - 0 - 0 - - - - Font size for most widgets (Default: 8.0) - - - 1 - - - 8.000000000000000 - - - 56.000000000000000 - - - 8.000000000000000 - - - - - combo_theme - label_2 - label_3 - label_4 - color_default_data - color_foreground - line - checkbox_apply - label_5 - slider_alpha - label - label_6 - color_background - line_2 - verticalSpacer_2 - label_7 - spinner_font_size - - - - - - - - QColorBox - QLabel -
glue.utils.qt.colors
-
-
- - -
diff --git a/glue/app/qt/report_crash.ui b/glue/app/qt/report_crash.ui deleted file mode 100644 index 98bbe4d91..000000000 --- a/glue/app/qt/report_crash.ui +++ /dev/null @@ -1,126 +0,0 @@ - - - FeedbackForm - - - - 0 - 0 - 418 - 474 - - - - Crash Report - - - true - - - - 8 - - - 16 - - - 2 - - - 2 - - - - - - - Crash report: - - - - - - - Submit Feedback - - - Qt::NoTextInteraction - - - - - - - If possible, please let us know what you were doing when the above crash happened: - - - true - - - - - - - - - - Optionally provide your email address if you want to be notified when the issue is fixed: - - - true - - - - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - - - - - - buttons - accepted() - FeedbackForm - accept() - - - 248 - 254 - - - 157 - 274 - - - - - buttons - rejected() - FeedbackForm - reject() - - - 316 - 260 - - - 286 - 274 - - - - - diff --git a/glue/app/qt/report_feedback.ui b/glue/app/qt/report_feedback.ui deleted file mode 100644 index 291ba3b71..000000000 --- a/glue/app/qt/report_feedback.ui +++ /dev/null @@ -1,119 +0,0 @@ - - - FeedbackForm - - - - 0 - 0 - 418 - 474 - - - - Submit Feedback - - - true - - - - 8 - - - 16 - - - 2 - - - 2 - - - - - - - Please include any feedback below! - - - true - - - - - - - - - - Include information on glue and dependency versions - - - true - - - - - - - Optionally provide your email address if you want us to be able to respond to your feedback! - - - true - - - - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - - - - - - buttons - accepted() - FeedbackForm - accept() - - - 248 - 254 - - - 157 - 274 - - - - - buttons - rejected() - FeedbackForm - reject() - - - 316 - 260 - - - 286 - 274 - - - - - diff --git a/glue/app/qt/save_data.py b/glue/app/qt/save_data.py index 01aa828e8..b755a9430 100644 --- a/glue/app/qt/save_data.py +++ b/glue/app/qt/save_data.py @@ -1,160 +1,4 @@ -import os - -from qtpy.QtWidgets import QDialog, QListWidgetItem -from qtpy.QtCore import Qt - -from glue import config - -from echo import SelectionCallbackProperty -from echo.qt import autoconnect_callbacks_to_qt - -from glue.utils.qt import load_ui -from glue.core.state_objects import State -from echo import ChoiceSeparator -from glue.core.data_combo_helper import ComponentIDComboHelper, DataCollectionComboHelper -from glue.core.data_exporters.qt.dialog import export_data - -__all__ = ['SaveDataDialog'] - - -class SaveDataState(State): - - data = SelectionCallbackProperty() - subset = SelectionCallbackProperty() - component = SelectionCallbackProperty() - exporter = SelectionCallbackProperty() - - def __init__(self, data_collection=None): - - super(SaveDataState, self).__init__() - - self.data_helper = DataCollectionComboHelper(self, 'data', data_collection) - self.component_helper = ComponentIDComboHelper(self, 'component', - data_collection=data_collection) - - self.add_callback('data', self._on_data_change) - self._on_data_change() - - self._sync_data_exporters() - - def _sync_data_exporters(self): - - exporters = list(config.data_exporter) - - def display_func(exporter): - if exporter.extension == '': - return "{0} (*)".format(exporter.label) - else: - return "{0} ({1})".format(exporter.label, ' '.join('*.' + ext for ext in exporter.extension)) - - SaveDataState.exporter.set_choices(self, exporters) - SaveDataState.exporter.set_display_func(self, display_func) - - def _on_data_change(self, event=None): - self.component_helper.set_multiple_data([self.data]) - self._sync_subsets() - - def _sync_subsets(self): - - def display_func(subset): - if subset is None: - return "All data (no subsets applied)" - else: - return subset.label - - subsets = [None] + list(self.data.subsets) - - SaveDataState.subset.set_choices(self, subsets) - SaveDataState.subset.set_display_func(self, display_func) - - -class SaveDataDialog(QDialog): - - def __init__(self, data_collection=None, parent=None): - - super(SaveDataDialog, self).__init__(parent=parent) - - self.state = SaveDataState(data_collection=data_collection) - - self.ui = load_ui('save_data.ui', parent=self, - directory=os.path.dirname(__file__)) - self._connections = autoconnect_callbacks_to_qt(self.state, self.ui) - - self.ui.button_cancel.clicked.connect(self.reject) - self.ui.button_ok.clicked.connect(self.accept) - self.ui.button_select_none.clicked.connect(self.select_none) - self.ui.button_select_all.clicked.connect(self.select_all) - - self.ui.list_component.itemChanged.connect(self._on_check_change) - - self.state.add_callback('component', self._on_data_change) - - self._on_data_change() - - def _on_data_change(self, *event): - - components = getattr(type(self.state), 'component').get_choices(self.state) - - self.ui.list_component.clear() - - for component in components: - - if isinstance(component, ChoiceSeparator): - item = QListWidgetItem(str(component)) - item.setFlags(item.flags() & ~Qt.ItemIsSelectable) - item.setForeground(Qt.gray) - else: - item = QListWidgetItem(component.label) - item.setCheckState(Qt.Checked) - self.ui.list_component.addItem(item) - - def _on_check_change(self, *event): - - any_checked = False - - for idx in range(self.ui.list_component.count()): - item = self.ui.list_component.item(idx) - if item.checkState() == Qt.Checked: - any_checked = True - break - - self.button_ok.setEnabled(any_checked) - - def select_none(self, *event): - self._set_all_checked(False) - - def select_all(self, *event): - self._set_all_checked(True) - - def _set_all_checked(self, check_state): - for idx in range(self.ui.list_component.count()): - item = self.ui.list_component.item(idx) - item.setCheckState(Qt.Checked if check_state else Qt.Unchecked) - - def accept(self): - components = [] - for idx in range(self.ui.list_component.count()): - item = self.ui.list_component.item(idx) - if item.checkState() == Qt.Checked: - components.append(self.state.data.id[item.text()]) - if self.state.subset is None: - data = self.state.data - else: - data = self.state.subset - export_data(data, components=components, exporter=self.state.exporter.function) - super(SaveDataDialog, self).accept() - - -if __name__ == "__main__": - - from glue.core import DataCollection, Data - from glue.utils.qt import get_qapp - - data1 = Data(x=[1, 2, 3], y=[2, 3, 4], label='data1') - data2 = Data(a=[1, 2, 3], b=[2, 3, 4], label='data2') - dc = DataCollection([data1, data2]) - - app = get_qapp() - - dialog = SaveDataDialog(data_collection=dc) - dialog.exec_() +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.app.qt.save_data is deprecated, use glue_qt.app.save_data instead', GlueDeprecationWarning) +from glue_qt.app.save_data import * # noqa diff --git a/glue/app/qt/save_data.ui b/glue/app/qt/save_data.ui deleted file mode 100644 index c2b34f00a..000000000 --- a/glue/app/qt/save_data.ui +++ /dev/null @@ -1,176 +0,0 @@ - - - Dialog - - - - 0 - 0 - 420 - 472 - - - - - 0 - 0 - - - - Dialog - - - - 12 - - - 12 - - - 12 - - - 5 - - - 7 - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Select the file format to save to: - - - - - - - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - false - - - - - - - Select All - - - - - - - Choose what part of the data you want to save: - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Cancel - - - - - - - Export - - - true - - - - - - - - - Select None - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Choose the dataset and attribute you want to save: - - - - - - - Once you click on "Export" you will be prompted to choose the filename. - - - true - - - - - - - - diff --git a/glue/app/qt/splash_screen.py b/glue/app/qt/splash_screen.py index f16c700a9..6cf0f7315 100644 --- a/glue/app/qt/splash_screen.py +++ b/glue/app/qt/splash_screen.py @@ -1,48 +1,4 @@ -import os - -from qtpy import QtWidgets, QtGui -from qtpy.QtCore import Qt, QRect - -__all__ = ['QtSplashScreen'] - - -class QtSplashScreen(QtWidgets.QWidget): - - def __init__(self, *args, **kwargs): - - super(QtSplashScreen, self).__init__(*args, **kwargs) - - self.resize(627, 310) - self.setStyleSheet("background-color:white;") - self.setWindowFlags(Qt.Window | Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint) - - self.center() - - self.progress = QtWidgets.QProgressBar() - - self.layout = QtWidgets.QVBoxLayout(self) - self.layout.addStretch() - self.layout.addWidget(self.progress) - - pth = os.path.join(os.path.dirname(__file__), '..', '..', 'logo.png') - self.image = QtGui.QPixmap(pth) - - def set_progress(self, value): - self.progress.setValue(int(value)) - QtWidgets.QApplication.processEvents() # update progress bar - - def paintEvent(self, event): - painter = QtGui.QPainter(self) - painter.drawPixmap(QRect(20, 20, 587, 229), self.image) - - def center(self): - # Adapted from StackOverflow - # https://stackoverflow.com/questions/20243637/pyqt4-center-window-on-active-screen - frameGm = self.frameGeometry() - try: - screen = QtWidgets.QApplication.desktop().screenNumber(QtWidgets.QApplication.desktop().cursor().pos()) - centerPoint = QtWidgets.QApplication.desktop().screenGeometry(screen).center() - except AttributeError: - centerPoint = QtWidgets.QApplication.primaryScreen().geometry().center() - frameGm.moveCenter(centerPoint) - self.move(frameGm.topLeft()) +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.app.qt.splash_screen is deprecated, use glue_qt.app.splash_screen instead', GlueDeprecationWarning) +from glue_qt.app.splash_screen import * # noqa diff --git a/glue/app/qt/terminal.py b/glue/app/qt/terminal.py index a8dd0aba8..eae551147 100644 --- a/glue/app/qt/terminal.py +++ b/glue/app/qt/terminal.py @@ -1,252 +1,4 @@ -""" -A GUI Ipython terminal window which can interact -with Glue. Based on code from - - https://stackoverflow.com/a/9796491/1332492 - -and - - https://stackoverflow.com/a/11525205/1332492 - -Usage: - new_widget = glue_terminal(**kwargs) -""" - -# must import these first, to set up Qt properly -from qtpy import QtWidgets - -from IPython import get_ipython - -from IPython.core.interactiveshell import InteractiveShell -from IPython.core.completer import IPCompleter - -from ipykernel.inprocess.ipkernel import InProcessInteractiveShell -from ipykernel.connect import get_connection_file - -from qtconsole.inprocess import QtInProcessKernelManager -from qtconsole.rich_jupyter_widget import RichJupyterWidget -from qtconsole.client import QtKernelClient - -from glue.app.qt.mdi_area import GlueMdiSubWindow -from glue.utils import as_variable_name -from glue.utils.qt import get_qapp - -# We need to do the following to make sure that the outputs in the IPython -# terminal don't get cached. This is because if a user does e.g. -# -# In [1]: viewer -# Out [1]: -# -# then there will be a remaining reference to the viewer in the IPython -# namespace. -InteractiveShell.cache_size.default_value = 0 - -# Make sure that tab competion only shows items returned by -# _ipython_key_completions_ -if hasattr(IPCompleter, 'dict_keys_only'): - IPCompleter.dict_keys_only.default_value = True - -kernel_manager = None -kernel_client = None - - -class IPythonTerminalError(Exception): - pass - - -def start_in_process_kernel(): - - global kernel_manager, kernel_client - - kernel_manager = QtInProcessKernelManager() - kernel_manager.start_kernel() - - kernel_client = kernel_manager.client() - kernel_client.start_channels() - - -def in_process_console(console_class=RichJupyterWidget, **kwargs): - """ - Create a console widget, connected to an in-process Kernel - - Keyword arguments will be added to the namespace of the shell. - - Parameters - ---------- - console_class : `type` - The class of the console widget to create - """ - - global kernel_manager, kernel_client - - if kernel_manager is None: - start_in_process_kernel() - - app = get_qapp() - - def stop(): - kernel_client.stop_channels() - kernel_manager.shutdown_kernel() - app.exit() - - control = console_class() - control._display_banner = False - control.kernel_manager = kernel_manager - control.kernel_client = kernel_client - control.exit_requested.connect(stop) - control.shell = kernel_manager.kernel.shell - control.shell.user_ns.update(**kwargs) - control.setWindowTitle('IPython Terminal - type howto() for instructions') - - return control - - -def connected_console(console_class=RichJupyterWidget, **kwargs): - """ - Create a console widget, connected to another kernel running in the current - process. - - This is used for instance if glue has been started from IPython. - - Keyword arguments will be added to the namespace of the shell. - - Parameters - ---------- - console_class : `type` - The class of the console widget to create - """ - - shell = get_ipython() - - if shell is None: - raise RuntimeError("There is no IPython kernel in this process") - - client = QtKernelClient(connection_file=get_connection_file()) - client.load_connection_file() - client.start_channels() - - control = console_class() - control.kernel_client = client - control.shell = shell - control.shell.user_ns.update(**kwargs) - - return control - - -glue_banner = """ -This is the built-in IPython terminal. You can type any valid Python code here, and you also have access to the following pre-defined variables: - - * data_collection (aliased to dc) - * application - * hub - -In addition, you can drag and drop any dataset or subset onto the terminal to create a new variable, and you will be prompted for a name. -""" - - -def howto(): - print(glue_banner.strip()) - - -class DragAndDropTerminal(RichJupyterWidget): - - def __init__(self, **kwargs): - super(DragAndDropTerminal, self).__init__(**kwargs) - self.setAcceptDrops(True) - self.shell = None - - def mdi_wrap(self): - sub = GlueMdiSubWindow() - sub.setWidget(self) - self.destroyed.connect(sub.close) - sub.resize(self.size()) - self._mdi_wrapper = sub - return sub - - @property - def namespace(self): - return self.shell.user_ns if self.shell is not None else None - - def dragEnterEvent(self, event): - fmt = 'application/py_instance' - if self.shell is not None and event.mimeData().hasFormat(fmt): - event.accept() - else: - event.ignore() - - def update_namespace(self, kwargs): - if self.shell is not None: - self.shell.push(kwargs) - - def dropEvent(self, event): - obj = event.mimeData().data('application/py_instance') - - try: - lbl = obj[0].label - except (IndexError, AttributeError): - lbl = 'x' - lbl = as_variable_name(lbl) - var, ok = QtWidgets.QInputDialog.getText(self, "Choose a variable name", - "Choose a variable name", text=lbl) - if ok: - # unpack single-item lists for convenience - if isinstance(obj, list) and len(obj) == 1: - obj = obj[0] - - var = {as_variable_name(str(var)): obj} - self.update_namespace(var) - event.accept() - else: - event.ignore() - - def clear_ns(self, names): - if self.shell is not None: - for name in names: - self.shell.user_ns.pop(name) - - -FROM_TERMINAL_MESSAGE = """ -Due to a limitation in IPython, the IPython terminal in glue does not work well -or at all if glue was started from an IPython terminal, or a Jupyter qtconsole -or notebook. However, you can use the original Python session you started glue -from instead to interact with glue objects. For example, if you started the -application with: - - In [2]: app = qglue(...) - -or - - In [2]: app = GlueApplication(...) - -you can then access e.g. the viewers with app.viewers, the data collection with -app.data_collection, and so on. If the IPython shell hangs and doesn't allow -you to type anything else until glue is closed, make sure you type: - - In [1]: %gui qt - -before starting glue to avoid this kind of issue. -""".strip() - - -def glue_terminal(**kwargs): - """ - Return a qt widget which embed an IPython interpreter. - - Keyword arguments will be added to the namespace of the shell. - """ - - kwargs['howto'] = howto - shell = get_ipython() - - if shell is None or isinstance(shell, InProcessInteractiveShell): - return in_process_console(console_class=DragAndDropTerminal, **kwargs) - else: - raise IPythonTerminalError(FROM_TERMINAL_MESSAGE) - - # TODO: if glue is launched from a qtconsole or a notebook, we should in - # principle be able to use the connected_console function above, but this - # doesn't behave quite right and requires further investigation. - - # Note however that if we do this, then we need to avoid polluting the - # namespace of the original IPython console, as described in this issue: - # https://github.com/glue-viz/glue/issues/1209 +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.app.qt.terminal is deprecated, use glue_qt.app.terminal instead', GlueDeprecationWarning) +from glue_qt.app.terminal import * # noqa diff --git a/glue/app/qt/tests/__init__.py b/glue/app/qt/tests/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/glue/app/qt/tests/test_actions.py b/glue/app/qt/tests/test_actions.py deleted file mode 100644 index a4a2a474f..000000000 --- a/glue/app/qt/tests/test_actions.py +++ /dev/null @@ -1,24 +0,0 @@ -# pylint: disable=I0011,W0613,W0201,W0212,E1101,E1103 - -from qtpy import QtWidgets, QtGui - -from ..actions import GlueActionButton - - -def test_glue_action_button(): - a = QtWidgets.QAction(None) - a.setToolTip("testtooltip") - a.setWhatsThis("testwhatsthis") - a.setIcon(QtGui.QIcon("dummy_file")) - a.setText('testtext') - b = GlueActionButton() - b.set_action(a) - - # assert b.icon() == a.icon() icons are copied, apparently - assert b.text() == a.text() - assert b.toolTip() == a.toolTip() - assert b.whatsThis() == a.whatsThis() - - # stays in sync - a.setText('test2') - assert b.text() == 'test2' diff --git a/glue/app/qt/tests/test_application.py b/glue/app/qt/tests/test_application.py deleted file mode 100644 index c9e798874..000000000 --- a/glue/app/qt/tests/test_application.py +++ /dev/null @@ -1,527 +0,0 @@ -# pylint: disable=I0011,W0613,W0201,W0212,E1101,E1103 - -import os -import sys - -import pytest -import numpy as np -from unittest.mock import patch, MagicMock - -from qtpy import QtCore -from glue.core.data import Data -from glue.core.component_link import ComponentLink -from glue.core.data_collection import DataCollection -from glue.core.tests.test_state import Cloner, doubler, clone -from glue.tests.helpers import requires_ipython # noqa -from glue.viewers.image.qt import ImageViewer -from glue.viewers.scatter.qt import ScatterViewer -from glue.viewers.histogram.qt import HistogramViewer -from glue.utils.qt import process_events - - -from ..application import GlueApplication, GlueLogger - - -def tab_count(app): - return app.tab_bar.count() - - -class TestGlueApplication(object): - - def setup_method(self, method): - self.app = GlueApplication() - self.app._create_terminal() - - def teardown_method(self, method): - self.app.close() - self.app = None - - def test_new_tabs(self): - t0 = tab_count(self.app) - self.app.new_tab() - assert tab_count(self.app) == t0 + 1 - - def test_save_session_ok(self): - with patch.object(self.app, 'save_session') as save: - with patch('qtpy.compat.getsavefilename') as fd: - fd.return_value = '/tmp/junk', 'jnk' - self.app._choose_save_session() - save.assert_called_once_with('/tmp/junk.glu', include_data=False, absolute_paths=False) - fd.reset_mock() - save.reset_mock() - - def test_save_session_cancel(self): - """shouldnt try to save file if no file name provided""" - with patch.object(self.app, 'save_session') as save: - with patch('glue.app.qt.application.compat.getsavefilename') as fd: - fd.return_value = '', 'jnk' - self.app._choose_save_session() - assert save.call_count == 0 - fd.reset_mock() - save.reset_mock() - - def test_choose_save_session_ioerror(self): - """should show box on ioerror""" - with patch('qtpy.compat.getsavefilename') as fd: - with patch('builtins.open') as op: - op.side_effect = IOError - fd.return_value = '/tmp/junk', '/tmp/junk' - with patch('qtpy.QtWidgets.QMessageBox') as mb: - self.app._choose_save_session() - assert mb.call_count == 1 - mb.reset_mock() - op.reset_mock() - fd.reset_mock() - - @requires_ipython - def test_terminal_present(self): - """For good setups, terminal is available""" - if not self.app.has_terminal(): - sys.stderr.write(self.app._terminal_exception) - assert False - - def is_terminal_importable(self): - import glue.qt.widgets.glue_terminal # noqa - - @requires_ipython - def test_toggle_terminal(self): - with patch.object(self.app, '_terminal') as term: - - self.app._terminal = term - - term.isVisible.return_value = False - with pytest.warns(UserWarning, match='An unexpected error'): - self.app._button_ipython.click() - assert term.show.call_count == 1 - - term.isVisible.return_value = True - with pytest.warns(UserWarning, match='An unexpected error'): - self.app._button_ipython.click() - assert term.hide.call_count == 1 - - term.reset_mock() - - def test_close_tab(self): - - assert self.app.tab_widget.count() == 1 - assert self.app.tab_bar.tabText(0) == 'Tab 1' - - self.app.new_tab() - assert self.app.tab_widget.count() == 2 - assert self.app.tab_bar.tabText(0) == 'Tab 1' - assert self.app.tab_bar.tabText(1) == 'Tab 2' - - self.app.close_tab(0) - assert self.app.tab_widget.count() == 1 - assert self.app.tab_bar.tabText(0) == 'Tab 2' - - # do not delete last tab - self.app.close_tab(0) - assert self.app.tab_widget.count() == 1 - - # check that counter always goes up - self.app.new_tab() - assert self.app.tab_bar.tabText(0) == 'Tab 2' - assert self.app.tab_bar.tabText(1) == 'Tab 3' - - def test_new_data_viewer_cancel(self): - with patch('glue.app.qt.application.pick_class') as pc: - pc.return_value = None - - ct = len(self.app.current_tab.subWindowList()) - - self.app.choose_new_data_viewer() - assert len(self.app.current_tab.subWindowList()) == ct - - pc.reset_mock() - - def test_new_data_viewer_ok(self): - - with patch('glue.app.qt.application.pick_class') as pc: - - pc.return_value = ScatterViewer - - ct = len(self.app.current_tab.subWindowList()) - - viewer = self.app.choose_new_data_viewer() - assert len(self.app.current_tab.subWindowList()) == ct + 1 - viewer.close() - # TODO: figure out why this doesn't work as expected - # assert len(self.app.current_tab.subWindowList()) == ct - - pc.reset_mock() - - def test_move(self): - viewer = self.app.new_data_viewer(ScatterViewer) - viewer.move(10, 20) - assert viewer.position == (10, 20) - viewer.close() - - def test_resize(self): - viewer = self.app.new_data_viewer(ScatterViewer) - viewer.viewer_size = (100, 200) - assert viewer.viewer_size == (100, 200) - viewer.close() - - def test_new_data_defaults(self): - - with patch('glue.app.qt.application.pick_class') as pc: - pc.return_value = None - - d2 = Data(x=np.array([[1, 2, 3], [4, 5, 6]])) - d1 = Data(x=np.array([1, 2, 3])) - - self.app.choose_new_data_viewer(data=d1) - args, kwargs = pc.call_args - assert kwargs['default'] is ScatterViewer - - self.app.choose_new_data_viewer(data=d2) - args, kwargs = pc.call_args - assert kwargs['default'] is ImageViewer - - pc.reset_mock() - - def test_drop_load_data(self): - - load_data = MagicMock() - load_session = MagicMock() - self.app.load_data = load_data - self.app.restore_session_and_close = load_session - - e = MagicMock() - - m = QtCore.QMimeData() - m.setUrls([QtCore.QUrl('test.fits')]) - e.mimeData.return_value = m - - self.app.dropEvent(e) - assert load_data.called_once_with('test.fits') - assert load_session.call_count == 0 - - load_data.reset_mock() - - m = QtCore.QMimeData() - m.setUrls([QtCore.QUrl('test1.fits'), QtCore.QUrl('test2.fits')]) - e.mimeData.return_value = m - self.app.dropEvent(e) - assert load_data.called_once_with(['test1.fits', 'test2.fits']) - assert load_session.call_count == 0 - - load_data.reset_mock() - - m = QtCore.QMimeData() - m.setUrls([QtCore.QUrl('test.glu')]) - e.mimeData.return_value = m - self.app.dropEvent(e) - assert load_data.call_count == 0 - assert load_session.called_once_with(['test.glu']) - - load_data.reset_mock() - - m = QtCore.QMimeData() - m.setUrls([QtCore.QUrl('test.glu'), QtCore.QUrl('test.fits')]) - e.mimeData.return_value = m - with pytest.raises(Exception) as exc: - self.app.dropEvent(e) - assert exc.value.args[0].startswith("When dragging and dropping files") - - load_data.reset_mock() - - def test_subset_facet(self): - # regression test for 335 - - act = self.app._layer_widget._actions['facet'] - self.app.data_collection.append(Data(x=[1, 2, 3])) - with patch('glue.dialogs.subset_facet.qt.SubsetFacetDialog.exec_'): - act._do_action() - - def test_move_viewer_to_tab(self): - - # Create a viewer in the first tab - viewer = self.app.new_data_viewer(ScatterViewer) - assert viewer.parent().mdiArea() is self.app.tab(0) - - # Move it to a new tab - self.app.new_tab() - self.app.move_viewer_to_tab(viewer, 1) - assert viewer.parent().mdiArea() is self.app.tab(1) - assert set(self.app.tab(0).subWindowList()) == {self.app._terminal} - assert set(self.app.tab(1).subWindowList()) == {viewer.parent()} - - # Move it back to the first tab - self.app.move_viewer_to_tab(viewer, 0) - assert viewer.parent().mdiArea() is self.app.tab(0) - assert set(self.app.tab(0).subWindowList()) == {self.app._terminal, viewer.parent()} - assert len(self.app.tab(1).subWindowList()) == 0 - - # Check that we do nothing if the given tab is the same as the - # viewer's current tab - parent = viewer.parent() - viewer_state = viewer.state - self.app.move_viewer_to_tab(viewer, 0) - assert parent is viewer.parent() - assert viewer_state is viewer.state - - # FIXME: The following test fails and causes subsequent issues if run with - # - # pytest -s -v -x glue - # - # Need to investigate this, but for now, no solution other than skipping - # the test. - # - # def test_suggest_merge(self): - # - # x = Data(x=[1, 2, 3], label='x') - # y = Data(y=[4, 5, 6, 7], label='y') - # z = Data(z=[8, 9, 10], label='z') - # - # self.app.data_collection.append(x) - # self.app.data_collection.append(y) - # - # with process_dialog(delay=500, accept=True): - # result = self.app.add_datasets(self.app.data_collection, z) - # - # np.testing.assert_equal(self.app.data_collection[0]['x'], [1, 2, 3]) - # np.testing.assert_equal(self.app.data_collection[0]['z'], [8, 9, 10]) - # np.testing.assert_equal(self.app.data_collection[1]['y'], [4, 5, 6, 7]) - - -def check_clone_app(app): - c = Cloner(app) - copy = c.us.object('__main__') - - hub1 = app.session.hub - hub2 = copy.session.hub - - assert len(hub1._subscriptions) == len(hub2._subscriptions) - - # data collections are the same - for d1, d2 in zip(app.session.data_collection, - copy.session.data_collection): - assert d1.label == d2.label - for cid1, cid2 in zip(d1.components, d2.components): - assert cid1.label == cid2.label - - # order of components unspecified if label collisions - cid2 = c.get(cid1) - np.testing.assert_array_almost_equal(d1[cid1, 0:1], - d2[cid2, 0:1], 3) - - # same data viewers, in the same tabs - for tab1, tab2 in zip(app.viewers, copy.viewers): - assert len(tab1) == len(tab2) - for v1, v2 in zip(tab1, tab2): - assert type(v1) == type(v2) - # same window properties - assert v1.viewer_size == v2.viewer_size - assert v1.position == v2.position - - # same viewer-level properties (axis label, scaling, etc) - # assert set(v1.properties.keys()) == set(v2.properties.keys()) - # for k in v1.properties: - # if hasattr(v1.properties[k], 'label'): - # assert v1.properties[k].label == v2.properties[k].label - # else: - # assert v1.properties[k] == v2.properties[k] or \ - # containers_equal(v1.properties[k], v2.properties[k]) - - assert len(v1.layers) == len(v2.layers) - for l1, l2 in zip(v1.layers, v2.layers): - assert l1.layer.label == l2.layer.label # same data/subset - assert l1.layer.style == l2.layer.style - - return copy - - -class TestApplicationSession(object): - - def check_clone(self, app): - return check_clone_app(app) - - def test_bare_application(self): - app = GlueApplication() - self.check_clone(app) - - def test_tab_names(self): - app = GlueApplication() - app.tab_bar.setTabText(0, 'Banana') - assert app.tab_names == ['Banana'] - app2 = self.check_clone(app) - assert app2.tab_names == ['Banana'] - - def test_data_application(self): - dc = DataCollection([Data(label='test', - x=[1, 2, 3], y=[2, 3, 4])]) - app = GlueApplication(dc) - self.check_clone(app) - - def test_links(self): - d1 = Data(label='x', x=[1, 2, 3]) - d2 = Data(label='y', y=[3, 4, 8]) - dc = DataCollection([d1, d2]) - link = ComponentLink([d1.id['x']], d2.id['y'], doubler) - dc.add_link(link) - - np.testing.assert_array_equal(d1['y'], [2, 4, 6]) - - app = GlueApplication(dc) - self.check_clone(app) - - def test_scatter_viewer(self): - d = Data(label='x', x=[1, 2, 3, 4, 5], y=[2, 3, 4, 5, 6]) - dc = DataCollection([d]) - app = GlueApplication(dc) - w = app.new_data_viewer(ScatterViewer, data=d) - copy1 = self.check_clone(app) - - dc.new_subset_group() - dc.new_subset_group() - assert len(w.layers) == 3 - l1, l2, l3 = w.layers - l1.zorder, l2.zorder = l2.zorder, l1.zorder - l3.visible = False - assert l3.visible is False - copy2 = self.check_clone(app) - assert copy2.viewers[0][0].layers[-1].visible is False - - app.close() - copy1.close() - copy2.close() - - def test_multi_tab(self): - d = Data(label='hist', x=[[1, 2], [2, 3]]) - dc = DataCollection([d]) - - app = GlueApplication(dc) - w1 = app.new_data_viewer(HistogramViewer, data=d) - app.new_tab() - w2 = app.new_data_viewer(HistogramViewer, data=d) - assert app.viewers == ((w1,), (w2,)) - - copy = self.check_clone(app) - - app.close() - copy.close() - - def test_histogram(self): - d = Data(label='hist', x=[[1, 2], [2, 3]]) - dc = DataCollection([d]) - - app = GlueApplication(dc) - w = app.new_data_viewer(HistogramViewer, data=d) - copy1 = self.check_clone(app) - - dc.new_subset_group() - assert len(w.layers) == 2 - copy2 = self.check_clone(app) - - w.nbins = 7 - copy3 = self.check_clone(app) - - app.close() - copy1.close() - copy2.close() - copy3.close() - - def test_subset_groups_remain_synced_after_restore(self): - # regrssion test for 352 - d = Data(label='hist', x=[[1, 2], [2, 3]]) - dc = DataCollection([d]) - dc.new_subset_group() - app = GlueApplication(dc) - - app2 = clone(app) - sg = app2.data_collection.subset_groups[0] - assert sg.style.parent is sg - - sg.style.color = '#112233' - assert sg.subsets[0].style.color == '#112233' - - def test_deselect_tool_on_viewer_change(self): - - d = Data(label='hist', x=[[1, 2], [2, 3]]) - dc = DataCollection([d]) - - app = GlueApplication(dc) - v1 = app.new_data_viewer(HistogramViewer, data=d) - v2 = app.new_data_viewer(HistogramViewer, data=d) - - assert v1.toolbar.active_tool is None - assert v2.toolbar.active_tool is None - - v2.toolbar.active_tool = 'select:xrange' - - assert v1.toolbar.active_tool is None - assert v2.toolbar.active_tool.tool_id == 'select:xrange' - - app.current_tab.activateNextSubWindow() - - assert v1.toolbar.active_tool is None - assert v2.toolbar.active_tool is None - - v1.toolbar.active_tool = 'select:xrange' - - # Emit a signal without changing the active subWindow to make sure that - # the tool doesn't get reset. - app.current_tab.subWindowActivated.emit(app.current_tab.activeSubWindow()) - - assert v1.toolbar.active_tool.tool_id == 'select:xrange' - assert v2.toolbar.active_tool is None - - app.current_tab.activateNextSubWindow() - - assert v1.toolbar.active_tool is None - assert v2.toolbar.active_tool is None - - app.close() - - def test_screenshot(self, tmpdir): - filename = tmpdir.join('screenshot.png').strpath - app = GlueApplication() - app.screenshot(filename) - assert os.path.exists(filename) - - -def test_logger_close(): - - # Regression test to make sure that when closing an application, sys.stderr - # no longer points to GlueLogger. - - app = GlueApplication() - app.close() - - process_events() - - assert not isinstance(sys.stderr, GlueLogger) - - -def test_reset_session_terminal(): - - # Regression test to make sure that the terminal still works when - # resetting a session - - app = GlueApplication() - app2 = app._reset_session(warn=False) - - assert app2.has_terminal() - - app.close() - app2.close() - - -def test_open_session_terminal(tmpdir): - - # Regression test to make sure that the terminal still works when - # opening a previous session - - session_file = tmpdir.join('test.glu').strpath - - app = GlueApplication() - app.save_session(session_file) - - app2 = app.restore_session(session_file) - - assert app2.has_terminal() - - app.close() - app2.close() diff --git a/glue/app/qt/tests/test_edit_subset_mode_toolbar.py b/glue/app/qt/tests/test_edit_subset_mode_toolbar.py deleted file mode 100644 index 003c231b2..000000000 --- a/glue/app/qt/tests/test_edit_subset_mode_toolbar.py +++ /dev/null @@ -1,54 +0,0 @@ -from glue.core.data import Data - -from ..application import GlueApplication - - -def combo_labels(combo): - return combo.currentText(), [combo.itemText(idx) for idx in range(combo.count())] - - -def test_edit_subset_mode_toolbar(): - - ga = GlueApplication() - dc = ga.data_collection - - tbar = ga._mode_toolbar - edit = ga.session.edit_subset_mode - - subset_combo = tbar.subset_combo - mode_label = tbar._label_subset_mode - - dc.append(Data(x=[1, 2, 3])) - - assert combo_labels(subset_combo) == ('None/Create New', ['None/Create New']) - assert mode_label.text() == '(the next selection will create a subset)' - - sg1 = dc.new_subset_group(subset_state=dc[0].id['x'] > 1, label='Subset 1') - - assert combo_labels(subset_combo) == ('Subset 1', ['Subset 1', 'None/Create New']) - assert mode_label.text() == 'Mode:' - - sg2 = dc.new_subset_group(subset_state=dc[0].id['x'] < 1, label='Subset 2') - - assert combo_labels(subset_combo) == ('Subset 2', ['Subset 1', 'Subset 2', 'None/Create New']) - assert mode_label.text() == 'Mode:' - - edit.edit_subset = [sg1, sg2] - - assert combo_labels(subset_combo) == ('Multiple subsets', ['Multiple subsets', 'Subset 1', 'Subset 2', 'None/Create New']) - assert mode_label.text() == 'Mode:' - - edit.edit_subset = [sg1] - - assert combo_labels(subset_combo) == ('Subset 1', ['Subset 1', 'Subset 2', 'None/Create New']) - assert mode_label.text() == 'Mode:' - - edit.edit_subset = [] - - assert combo_labels(subset_combo) == ('None/Create New', ['Subset 1', 'Subset 2', 'None/Create New']) - assert mode_label.text() == '(the next selection will create a subset)' - - edit.edit_subset = [sg2] - - assert combo_labels(subset_combo) == ('Subset 2', ['Subset 1', 'Subset 2', 'None/Create New']) - assert mode_label.text() == 'Mode:' diff --git a/glue/app/qt/tests/test_layer_tree_widget.py b/glue/app/qt/tests/test_layer_tree_widget.py deleted file mode 100644 index 9ab7ca4d2..000000000 --- a/glue/app/qt/tests/test_layer_tree_widget.py +++ /dev/null @@ -1,239 +0,0 @@ -# pylint: disable=I0011,W0613,W0201,W0212,E1101,E1103 - -from unittest.mock import MagicMock, patch - -from qtpy import QtWidgets -from glue import core -from glue.tests import example_data -from glue.core.session import Session - -from ..layer_tree_widget import LayerTreeWidget, Clipboard, PlotAction - - -class TestLayerTree(object): - - """ Unit tests for the layer_tree_widget class """ - - def setup_method(self, method): - self.data = example_data.test_data() - self.collect = core.data_collection.DataCollection(list(self.data)) - self.hub = self.collect.hub - self.session = Session(data_collection=self.collect, hub=self.hub) - self.widget = LayerTreeWidget(session=self.session) - self.win = QtWidgets.QMainWindow() - self.win.setCentralWidget(self.widget) - self.widget.setup(self.collect) - for key, value in self.widget._actions.items(): - self.__setattr__("%s_action" % key, value) - - def teardown_method(self, method): - self.win.close() - - def select_layers(self, *layers): - self.widget.ui.layerTree.set_selected_layers(layers) - - def remove_layer(self, layer): - """ Remove a layer via the widget remove button """ - self.select_layers(layer) - self.widget._actions['delete']._do_action() - - def add_layer(self, layer=None): - """ Add a layer through a hub message """ - layer = layer or core.Data() - self.widget.data_collection.append(layer) - return layer - - def layer_present(self, layer): - """ Test that a layer exists in the data collection """ - return layer in self.collect or \ - getattr(layer, 'data', None) in self.collect - - def test_current_layer_method_correct(self): - layer = self.add_layer() - self.select_layers(layer) - assert self.widget.current_layer() is layer - - def test_add(self): - """ Test that a layer exists in widget once added """ - data = core.Data() - assert not self.layer_present(data) - self.add_layer(data) - assert self.layer_present(data) - - def test_remove_layer(self): - """ Test that widget remove button works properly """ - layer = self.add_layer() - self.remove_layer(layer) - assert not self.layer_present(layer) - - def test_remove_subset_triggers_selection_changed(self): - layer = self.add_layer() - grp = self.collect.new_subset_group() - mock = MagicMock() - self.widget.ui.layerTree.selection_changed.connect(mock) - self.remove_layer(grp) - assert mock.call_count > 0 - - def test_remove_subset_layer(self): - """ Test that widget remove button works properly on subset groups""" - layer = self.add_layer() - grp = self.collect.new_subset_group() - assert self.layer_present(grp) - self.remove_layer(grp) - assert not self.layer_present(grp) - - def test_empty_removal_does_nothing(self): - """ Make sure widgets are only removed when selected """ - layer = self.add_layer() - self.widget.ui.layerTree.clearSelection() - self.widget._actions['delete']._do_action() - assert self.layer_present(layer) - - @patch('glue.app.qt.layer_tree_widget.LinkEditor') - def test_link_data(self, le): - layer = self.add_layer() - self.select_layers(layer) - self.link_action.trigger() - assert le.update_links.call_count == 1 - - def test_new_subset_action(self): - """ new action creates a new subset group """ - layer = self.add_layer() - self.new_action.trigger() - assert len(self.collect.subset_groups) == 1 - - def test_maskify_action(self): - d = core.Data(x=[1, 2, 3]) - s = d.new_subset() - - selected = MagicMock() - self.maskify_action.selected_layers = selected - selected.return_value = [s] - # FIXME: calling trigger does not work correctly - # self.maskify_action.trigger() - self.maskify_action._do_action() - assert isinstance(s.subset_state, core.subset.MaskSubsetState) - - def test_copy_paste_subset_action(self): - layer = self.add_layer() - grp = self.collect.new_subset_group() - self.select_layers(grp) - self.copy_action.trigger() - grp2 = self.collect.new_subset_group() - self.select_layers(grp2) - state0 = grp2.subset_state - self.paste_action.trigger() - assert grp2.subset_state is not state0 - - def setup_two_subset_selection(self): - layer = self.add_layer() - g1 = self.collect.new_subset_group() - g2 = self.collect.new_subset_group() - self.select_layers(g1, g2) - return layer - - def test_invert(self): - layer = self.add_layer() - sub = self.collect.new_subset_group() - self.select_layers(sub) - self.invert_action.trigger() - assert isinstance(sub.subset_state, core.subset.InvertState) - - def test_actions_enabled_single_subset_group_selection(self): - Clipboard().contents = None - layer = self.add_layer() - grp = self.collect.new_subset_group() - self.select_layers(grp) - - assert self.new_action.isEnabled() - assert self.copy_action.isEnabled() - assert not self.paste_action.isEnabled() - assert self.invert_action.isEnabled() - assert self.clear_action.isEnabled() - - def test_actions_enabled_single_data_selection(self): - layer = self.add_layer() - self.select_layers(layer) - - assert self.new_action.isEnabled() - assert not self.copy_action.isEnabled() - assert not self.paste_action.isEnabled() - assert not self.invert_action.isEnabled() - assert not self.clear_action.isEnabled() - - def test_actions_enabled_multi_subset_group_selection(self): - layer = self.setup_two_subset_selection() - assert self.new_action.isEnabled() - assert not self.copy_action.isEnabled() - assert not self.paste_action.isEnabled() - assert not self.invert_action.isEnabled() - assert not self.clear_action.isEnabled() - - def test_checkable_toggle(self): - self.widget.set_checkable(True) - assert self.widget.is_checkable() - self.widget.set_checkable(False) - assert not self.widget.is_checkable() - - def test_load_data(self): - with patch('glue.app.qt.layer_tree_widget.data_wizard') as wizard: - d = core.Data(x=[1]) - assert not self.layer_present(d) - wizard.return_value = [d] - self.widget._load_data() - assert self.layer_present(d) - - def test_clear_subset_group(self): - layer = self.add_layer() - sub = self.collect.new_subset_group() - self.select_layers(sub) - dummy_state = MagicMock() - sub.subset_state = dummy_state - self.clear_action.trigger() - assert sub.subset_state is not dummy_state - - def test_single_selection_updates_editable(self): - self.widget.bind_selection_to_edit_subset() - self.add_layer() - grp1 = self.collect.new_subset_group() - grp2 = self.collect.new_subset_group() - mode = self.session.edit_subset_mode - assert mode.edit_subset[0] is not grp1 - self.select_layers(grp1) - assert mode.edit_subset[0] is grp1 - - def test_multi_selection_updates_editable(self): - """Selection disables edit_subset for all other data""" - self.widget.bind_selection_to_edit_subset() - self.add_layer() - self.add_layer() - grps = [self.collect.new_subset_group() for _ in range(3)] - self.select_layers(*grps[:2]) - mode = self.session.edit_subset_mode - assert grps[0] in mode.edit_subset - assert grps[1] in mode.edit_subset - assert grps[2] not in mode.edit_subset - - def test_selection_updates_on_data_add(self): - layer = self.add_layer() - assert self.widget.selected_layers() == [layer] - - def test_selection_updates_on_subset_group_add(self): - layer = self.add_layer() - grp = self.collect.new_subset_group() - assert self.widget.selected_layers() == [grp] - - def test_plot_action(self): - # regression test for #364 - - app = MagicMock() - pa = PlotAction(self.widget, app) - - layer = self.add_layer() - grp = self.collect.new_subset_group() - - self.select_layers(grp) - assert not pa.isEnabled() - - self.select_layers(layer) - assert pa.isEnabled() diff --git a/glue/app/qt/tests/test_plugin_manager.py b/glue/app/qt/tests/test_plugin_manager.py deleted file mode 100644 index 219841753..000000000 --- a/glue/app/qt/tests/test_plugin_manager.py +++ /dev/null @@ -1,72 +0,0 @@ -from unittest.mock import patch - -from glue import _plugin_helpers as ph -from glue.main import load_plugins - -from ..plugin_manager import QtPluginManager - - -def setup_function(func): - from glue import config - func.CFG_DIR_ORIG = config.CFG_DIR - - -def teardown_function(func): - from glue import config - config.CFG_DIR = func.CFG_DIR_ORIG - - -def test_basic_empty(tmpdir): - - # Test that things work when the plugin cfg file is empty - - from glue import config - config.CFG_DIR = tmpdir.join('.glue').strpath - - w = QtPluginManager() - w.clear() - w.update_list() - w.finalize() - - -def test_basic(tmpdir): - - # Test that things work when the plugin cfg file is populated - - from glue import config - config.CFG_DIR = tmpdir.join('.glue').strpath - - load_plugins() - - config = ph.PluginConfig.load() - config.plugins['spectrum_tool'] = False - config.plugins['pv_slicer'] = False - config.save() - - w = QtPluginManager() - w.clear() - w.update_list() - w.finalize() - - config2 = ph.PluginConfig.load() - - assert config.plugins == config2.plugins - - -def test_permission_fail(tmpdir): - - from glue import config - config.CFG_DIR = tmpdir.join('.glue').strpath - - # Make a *file* at that location so that reading the plugin file will fail - - with open(config.CFG_DIR, 'w') as f: - f.write("test") - - config2 = ph.PluginConfig.load() - - with patch('qtpy.QtWidgets.QMessageBox') as qmb: - w = QtPluginManager() - w.finalize() - - assert qmb.call_count == 1 diff --git a/glue/app/qt/tests/test_preferences.py b/glue/app/qt/tests/test_preferences.py deleted file mode 100644 index 8b6cd2ac7..000000000 --- a/glue/app/qt/tests/test_preferences.py +++ /dev/null @@ -1,410 +0,0 @@ -import os - -import pytest -import numpy as np -from unittest.mock import patch, MagicMock -from matplotlib.colors import ColorConverter - -from glue import custom_viewer -from glue.tests.helpers import PYSIDE2_INSTALLED # noqa -from glue.core import HubListener, Application, Data, DataCollection -from glue.core.message import SettingsChangeMessage -from qtpy import QtWidgets -from glue.app.qt.preferences import PreferencesDialog -from glue.app.qt import GlueApplication -from glue.viewers.scatter.qt import ScatterViewer -from glue.viewers.image.qt import ImageViewer -from glue.viewers.histogram.qt import HistogramViewer -from glue.plugins.dendro_viewer.qt import DendrogramViewer - -rgb = ColorConverter().to_rgb - - -class TestPreferences(): - - def setup_method(self, method): - self.app = Application() - - def test_no_change(self): - - # If we don't change anything, settings should be preserved - - with patch('glue.config.settings') as settings: - - settings.FOREGROUND_COLOR = 'red' - settings.BACKGROUND_COLOR = (0, 0.5, 1) - settings.DATA_COLOR = (1, 0.5, 0.25) - settings.DATA_ALPHA = 0.3 - settings.FONT_SIZE = 8.0 - - dialog = PreferencesDialog(self.app) - dialog.show() - - assert dialog.theme == 'Custom' - - dialog.accept() - - assert rgb(settings.FOREGROUND_COLOR) == (1, 0, 0) - assert rgb(settings.BACKGROUND_COLOR) == (0, 0.5, 1) - assert rgb(settings.DATA_COLOR) == (1, 0.5, 0.25) - assert settings.DATA_ALPHA == 0.3 - assert settings.FONT_SIZE == 8.0 - - def test_theme_autodetect(self): - - # If we don't change anything, settings should be preserved - - with patch('glue.config.settings') as settings: - - settings.FOREGROUND_COLOR = 'white' - settings.BACKGROUND_COLOR = 'black' - settings.DATA_COLOR = '0.75' - settings.DATA_ALPHA = 0.8 - settings.FONT_SIZE = 8.0 - - dialog = PreferencesDialog(self.app) - dialog.show() - assert dialog.theme == 'White on Black' - dialog.accept() - - settings.FOREGROUND_COLOR = 'black' - settings.BACKGROUND_COLOR = 'white' - settings.DATA_COLOR = '0.35' - settings.DATA_ALPHA = 0.8 - settings.FONT_SIZE = 8.0 - - dialog = PreferencesDialog(self.app) - dialog.show() - assert dialog.theme == 'Black on White' - dialog.accept() - - def test_themes(self): - - # Check that themes work - - with patch('glue.config.settings') as settings: - - settings.FOREGROUND_COLOR = 'red' - settings.BACKGROUND_COLOR = (0, 0.5, 1) - settings.DATA_COLOR = (1, 0.5, 0.25) - settings.DATA_ALPHA = 0.3 - settings.FONT_SIZE = 8.0 - - dialog = PreferencesDialog(self.app) - dialog.show() - dialog.theme = 'White on Black' - dialog.accept() - - assert rgb(settings.FOREGROUND_COLOR) == (1, 1, 1) - assert rgb(settings.BACKGROUND_COLOR) == (0, 0, 0) - assert rgb(settings.DATA_COLOR) == (0.75, 0.75, 0.75) - assert settings.DATA_ALPHA == 0.8 - settings.FONT_SIZE = 8.0 - - dialog = PreferencesDialog(self.app) - dialog.show() - dialog.theme = 'Black on White' - dialog.accept() - - assert rgb(settings.FOREGROUND_COLOR) == (0, 0, 0) - assert rgb(settings.BACKGROUND_COLOR) == (1, 1, 1) - assert rgb(settings.DATA_COLOR) == (0.35, 0.35, 0.35) - assert settings.DATA_ALPHA == 0.8 - settings.FONT_SIZE = 8.0 - - def test_custom_changes(self): - - # Check that themes work - - with patch('glue.config.settings') as settings: - - settings.FOREGROUND_COLOR = 'red' - settings.BACKGROUND_COLOR = (0, 0.5, 1) - settings.DATA_COLOR = (1, 0.5, 0.25) - settings.DATA_ALPHA = 0.3 - settings.FONT_SIZE = 8.0 - - dialog = PreferencesDialog(self.app) - dialog.show() - dialog.foreground = (0, 1, 1) - dialog.accept() - - assert rgb(settings.FOREGROUND_COLOR) == (0, 1, 1) - assert rgb(settings.BACKGROUND_COLOR) == (0, 0.5, 1) - assert rgb(settings.DATA_COLOR) == (1, 0.5, 0.25) - assert settings.DATA_ALPHA == 0.3 - settings.FONT_SIZE = 8.0 - - dialog = PreferencesDialog(self.app) - dialog.show() - dialog.background = (1, 0, 1) - dialog.accept() - - assert rgb(settings.FOREGROUND_COLOR) == (0, 1, 1) - assert rgb(settings.BACKGROUND_COLOR) == (1, 0, 1) - assert rgb(settings.DATA_COLOR) == (1, 0.5, 0.25) - assert settings.DATA_ALPHA == 0.3 - settings.FONT_SIZE = 8.0 - - dialog = PreferencesDialog(self.app) - dialog.show() - dialog.data_color = (1, 1, 0.5) - dialog.accept() - - assert rgb(settings.FOREGROUND_COLOR) == (0, 1, 1) - assert rgb(settings.BACKGROUND_COLOR) == (1, 0, 1) - assert rgb(settings.DATA_COLOR) == (1, 1, 0.5) - assert settings.DATA_ALPHA == 0.3 - settings.FONT_SIZE = 8.0 - - dialog = PreferencesDialog(self.app) - dialog.show() - dialog.data_alpha = 0.4 - dialog.accept() - - assert rgb(settings.FOREGROUND_COLOR) == (0, 1, 1) - assert rgb(settings.BACKGROUND_COLOR) == (1, 0, 1) - assert rgb(settings.DATA_COLOR) == (1, 1, 0.5) - assert settings.DATA_ALPHA == 0.4 - settings.FONT_SIZE = 8.0 - - dialog = PreferencesDialog(self.app) - dialog.show() - dialog.font_size = 16.0 - dialog.accept() - - assert rgb(settings.FOREGROUND_COLOR) == (0, 1, 1) - assert rgb(settings.BACKGROUND_COLOR) == (1, 0, 1) - assert rgb(settings.DATA_COLOR) == (1, 1, 0.5) - assert settings.DATA_ALPHA == 0.4 - settings.FONT_SIZE = 16.0 - - def test_custom_pane(self): - - settings = MagicMock() - - class CustomPreferences(QtWidgets.QWidget): - - def __init__(self, parent=None): - - super(CustomPreferences, self).__init__(parent=parent) - - self.layout = QtWidgets.QFormLayout() - - self.option1 = QtWidgets.QLineEdit() - self.option2 = QtWidgets.QLineEdit() - - self.layout.addRow("Option 1", self.option1) - self.layout.addRow("Option 2", self.option2) - - self.setLayout(self.layout) - - def finalize(self): - settings.OPTION1 = "Monty" - settings.OPTION2 = "Python" - - preference_panes = [('Custom', CustomPreferences)] - - with patch('glue.config.preference_panes', preference_panes): - - dialog = PreferencesDialog(self.app) - dialog.show() - dialog.accept() - - assert settings.OPTION1 == "Monty" - assert settings.OPTION2 == "Python" - - def test_settings_change_message(self): - - # Make sure that a SettingsChangeMessage gets emitted when settings - # change in the dialog - - class TestListener(HubListener): - - def __init__(self, hub): - hub.subscribe(self, SettingsChangeMessage, - handler=self.receive_message) - self.received = [] - - def receive_message(self, message): - self.received.append(message) - - listener = TestListener(self.app._hub) - - with patch('glue.config.settings') as settings: - - settings.FOREGROUND_COLOR = 'red' - settings.BACKGROUND_COLOR = (0, 0.5, 1) - settings.DATA_COLOR = (1, 0.5, 0.25) - settings.DATA_ALPHA = 0.3 - settings.FONT_SIZE = 8.0 - - dialog = PreferencesDialog(self.app) - dialog.show() - dialog.foreground = (0, 1, 1) - dialog.accept() - - assert len(listener.received) == 1 - assert listener.received[0].settings == ('FOREGROUND_COLOR', 'BACKGROUND_COLOR', 'FONT_SIZE') - - def test_save_to_disk(self, tmpdir): - - with patch('glue.config.settings') as settings: - with patch('glue.config.CFG_DIR', tmpdir.strpath): - - settings.FOREGROUND_COLOR = 'red' - settings.BACKGROUND_COLOR = (0, 0.5, 1) - settings.DATA_COLOR = (1, 0.5, 0.25) - settings.DATA_ALPHA = 0.3 - settings.FONT_SIZE = 8.0 - - dialog = PreferencesDialog(self.app) - dialog.show() - dialog.save_to_disk = False - dialog.accept() - - assert not os.path.exists(os.path.join(tmpdir.strpath, 'settings.cfg')) - - dialog = PreferencesDialog(self.app) - dialog.show() - dialog.save_to_disk = True - dialog.accept() - - assert os.path.exists(os.path.join(tmpdir.strpath, 'settings.cfg')) - - -def assert_axes_background(axes, color): - assert axes.patch.get_facecolor() == color - assert axes.figure.get_facecolor() == color - - -def assert_axes_foreground(axes, color): - - if hasattr(axes, 'coords'): - # TODO: fix this in WCSAxes - assert axes.coords.frame._color == color - for coord in axes.coords: - assert coord.ticks.get_color() == color - assert coord.ticklabels.get_color() == color - assert coord.axislabels.get_color() == color - else: - for spine in axes.spines.values(): - assert spine.get_edgecolor() == color - for tick in axes.xaxis.get_ticklines() + axes.yaxis.get_ticklines(): - assert tick.get_color() == color - for label in axes.xaxis.get_ticklabels(which="both") + axes.yaxis.get_ticklabels(which="both"): - assert label.get_color() == color - assert axes.xaxis.label.get_color() == color - assert axes.yaxis.label.get_color() == color - - -def _generate_custom_viewer(): - - example = custom_viewer('Test Plot', x='att(x)', y='att(y)') - - @example.plot_data - def plot_data(axes, x, y, style): - axes.plot(x, y) - - @example.plot_subset - def plot_subset(axes, x, y, style): - axes.plot(x, y) - - @example.setup - def setup(axes): - pass - - from glue.config import qt_client - for viewer in qt_client.members: - if viewer.LABEL == 'Test Plot': - return viewer - - raise Exception("Failed to find custom viewer in qt_client") - - -@pytest.mark.skipif('PYSIDE2_INSTALLED') -def test_foreground_background_settings(): - - d_1d = Data(x=np.random.random(100), y=np.random.random(100), label='Data 1d') - d_2d = Data(x=np.random.random((100, 100)), y=np.random.random((100, 100)), label='Data 2d') - - dc = DataCollection([d_1d, d_2d]) - - app = GlueApplication(dc) - - # Make sure that settings change existing viewers, so we create a bunch of - # viewers here. - - scatter1 = app.new_data_viewer(ScatterViewer) - scatter1.add_data(d_1d) - - image1 = app.new_data_viewer(ImageViewer) - image1.add_data(d_2d) - - histogram1 = app.new_data_viewer(HistogramViewer) - histogram1.add_data(d_1d) - - dendrogram1 = app.new_data_viewer(DendrogramViewer) - - example_custom = _generate_custom_viewer() - - custom1 = app.new_data_viewer(example_custom) - - RED = (1, 0, 0, 0.5) - GREEN = (0, 1, 0, 0.6) - - app.show() - - with patch('glue.config.settings') as settings: - - settings.FOREGROUND_COLOR = 'black' - settings.BACKGROUND_COLOR = 'white' - settings.DATA_COLOR = '0.5' - settings.DATA_ALPHA = 0.5 - settings.FONT_SIZE = 8.0 - - dialog = PreferencesDialog(app) - dialog.show() - dialog.background = RED - dialog.foreground = GREEN - dialog.accept() - - assert_axes_background(scatter1.axes, RED) - assert_axes_background(image1.axes, RED) - assert_axes_background(histogram1.axes, RED) - assert_axes_background(dendrogram1.axes, RED) - assert_axes_background(custom1.axes, RED) - - assert_axes_foreground(scatter1.axes, GREEN) - assert_axes_foreground(image1.axes, GREEN) - assert_axes_foreground(histogram1.axes, GREEN) - assert_axes_foreground(dendrogram1.axes, GREEN) - assert_axes_foreground(custom1.axes, GREEN) - - # Now make sure that new viewers also inherit these settings - - scatter2 = app.new_data_viewer(ScatterViewer) - scatter2.add_data(d_1d) - - image2 = app.new_data_viewer(ImageViewer) - image2.add_data(d_2d) - - histogram2 = app.new_data_viewer(HistogramViewer) - histogram2.add_data(d_1d) - - dendrogram2 = app.new_data_viewer(DendrogramViewer) - custom2 = app.new_data_viewer(example_custom) - - assert_axes_background(scatter2.axes, RED) - assert_axes_background(image2.axes, RED) - assert_axes_background(histogram2.axes, RED) - assert_axes_background(dendrogram2.axes, RED) - assert_axes_background(custom2.axes, RED) - - assert_axes_foreground(scatter2.axes, GREEN) - assert_axes_foreground(image2.axes, GREEN) - assert_axes_foreground(histogram2.axes, GREEN) - assert_axes_foreground(dendrogram2.axes, GREEN) - assert_axes_foreground(custom2.axes, GREEN) - - app.close() diff --git a/glue/app/qt/tests/test_save_data.py b/glue/app/qt/tests/test_save_data.py deleted file mode 100644 index 03ee28c65..000000000 --- a/glue/app/qt/tests/test_save_data.py +++ /dev/null @@ -1,113 +0,0 @@ -from collections import namedtuple - -from unittest.mock import MagicMock, patch - -from qtpy.QtCore import Qt - -from glue.core import DataCollection, Data -from glue.utils.qt import get_qapp -from glue.app.qt.save_data import SaveDataDialog - - -def components(list_widget): - enabled = [] - disabled = [] - for idx in range(list_widget.count()): - item = list_widget.item(idx) - if item.flags() & Qt.ItemIsSelectable: - if item.checkState() == Qt.Checked: - enabled.append(item.text()) - else: - disabled.append(item.text()) - return disabled, enabled - - -class TestSaveDataDialog: - - def setup_method(self, method): - - self.data1 = Data(x=[1, 2, 3], y=[2, 3, 4], label='data1') - self.data2 = Data(a=[1, 2, 3], b=[2, 3, 4], label='data2') - self.dc = DataCollection([self.data1, self.data2]) - self.dc.new_subset_group(label='my subset', subset_state=self.data1.id['x'] > 1.5) - - self.x = self.data1.id['x'] - self.y = self.data1.id['y'] - self.a = self.data2.id['a'] - self.b = self.data2.id['b'] - - self.app = get_qapp() - - self.dialog = SaveDataDialog(data_collection=self.dc) - - def teardown_method(self, method): - self.app = None - - def test_defaults(self): - disabled, enabled = components(self.dialog.ui.list_component) - assert enabled == ['x', 'y'] - assert disabled == [] - - def test_defaults_derived(self): - self.data1['z'] = self.data1.id['x'] + 1 - disabled, enabled = components(self.dialog.ui.list_component) - assert enabled == ['x', 'y', 'z'] - assert disabled == [] - - def test_change_data(self): - self.dialog.ui.combosel_data.setCurrentIndex(1) - disabled, enabled = components(self.dialog.ui.list_component) - assert enabled == ['a', 'b'] - assert disabled == [] - - def test_select_buttons(self): - self.dialog.button_select_none.click() - disabled, enabled = components(self.dialog.ui.list_component) - assert enabled == [] - assert disabled == ['x', 'y'] - self.dialog.button_select_all.click() - disabled, enabled = components(self.dialog.ui.list_component) - assert enabled == ['x', 'y'] - assert disabled == [] - - def test_accept(self): - func = self._accept() - func.assert_called_once_with('test_file.fits', self.data1, components=[self.x, self.y]) - - def test_change_accept(self): - self.dialog.ui.combosel_data.setCurrentIndex(1) - func = self._accept() - func.assert_called_once_with('test_file.fits', self.data2, components=[self.a, self.b]) - - def test_change_subset_accept(self): - self.dialog.ui.combosel_subset.setCurrentIndex(1) - func = self._accept() - func.assert_called_once_with('test_file.fits', self.data1.subsets[0], components=[self.x, self.y]) - - def test_deselect_accept(self): - self.dialog.ui.list_component.item(1).setCheckState(Qt.Unchecked) - func = self._accept() - func.assert_called_once_with('test_file.fits', self.data1, components=[self.x]) - - def test_deselect_all(self): - self.dialog.select_none() - assert not self.dialog.button_ok.isEnabled() - self.dialog.select_all() - assert self.dialog.button_ok.isEnabled() - - def _accept(self): - - mock = MagicMock() - - test_exporter_cls = namedtuple('exporter', 'function label extension') - test_exporter = test_exporter_cls(function=mock, label='Test', extension='') - - with patch('qtpy.compat.getsavefilename') as dialog: - with patch('glue.config.data_exporter') as data_exporter: - def test_iter(x): - yield test_exporter - data_exporter.__iter__ = test_iter - dialog.return_value = 'test_file.fits', None - self.dialog.state._sync_data_exporters() - self.dialog.accept() - return test_exporter.function diff --git a/glue/app/qt/tests/test_terminal.py b/glue/app/qt/tests/test_terminal.py deleted file mode 100644 index eacc6d582..000000000 --- a/glue/app/qt/tests/test_terminal.py +++ /dev/null @@ -1,83 +0,0 @@ -import os - -import pytest -from unittest.mock import MagicMock, patch - -from glue.tests.helpers import requires_ipython, IPYTHON_INSTALLED - - -CIRCLECI = os.environ.get('CIRCLECI', 'false') == 'true' - -if IPYTHON_INSTALLED: - from ..terminal import glue_terminal - - -@requires_ipython -@pytest.mark.skipif(CIRCLECI, reason='IPython terminal tests tend to hang on CircleCI') -class TestTerminal(object): - def test_mpl_non_interactive(self): - """IPython v0.12 sometimes turns on mpl interactive. Ensure - we catch that""" - - import matplotlib - assert not matplotlib.is_interactive() - gt = glue_terminal() - assert not matplotlib.is_interactive() - - def test_update_namespace(self): - """Test that top level namespace API works without error""" - gt = glue_terminal() - gt.update_namespace({'x': 3}) - assert 'x' in gt.namespace - - def test_accepts_drops(self): - gt = glue_terminal() - assert gt.acceptDrops() - - def test_drops_update_namespace(self): - """DnD adds variable name to namespace""" - with patch('glue.app.qt.terminal.QtWidgets.QInputDialog') as dialog: - dialog.getText.return_value = 'accept_var', True - - gt = glue_terminal() - event = MagicMock() - event.mimeData().data.return_value = [5] - - gt.dropEvent(event) - assert gt.namespace.get('accept_var') == 5 - - def test_cancel_drop(self): - """Drop not added if user cancels dialog box""" - - with patch('glue.app.qt.terminal.QtWidgets.QInputDialog') as dialog: - dialog.getText.return_value = 'cancel_var', False - - gt = glue_terminal() - event = MagicMock() - event.mimeData().data.return_value = [5] - - gt.dropEvent(event) - assert 'cancel_var' not in gt.namespace - - def test_ignore_drag_enter(self): - event = MagicMock() - event.mimeData().hasFormat.return_value = False - - gt = glue_terminal() - gt.dragEnterEvent(event) - - event.ignore.assert_called_once_with() - - def test_accept_drag_enter(self): - event = MagicMock() - event.mimeData().hasFormat.return_value = True - - gt = glue_terminal() - gt.dragEnterEvent(event) - - event.accept.assert_called_once_with() - - -if __name__ == "__main__": - import pytest - pytest.main([__file__]) diff --git a/glue/app/qt/versions.py b/glue/app/qt/versions.py index 9e1d51fc5..cabf25c3d 100644 --- a/glue/app/qt/versions.py +++ b/glue/app/qt/versions.py @@ -1,51 +1,4 @@ -import os - -from glue import __version__ - -from qtpy import QtWidgets -from qtpy.QtCore import Qt - -from glue.utils import nonpartial -from glue.utils.qt import load_ui, CenteredDialog -from glue._deps import get_status_as_odict - -__all__ = ['QVersionsDialog'] - - -class QVersionsDialog(CenteredDialog): - - def __init__(self, *args, **kwargs): - - super(QVersionsDialog, self).__init__(*args, **kwargs) - - self.ui = load_ui('versions.ui', self, directory=os.path.dirname(__file__)) - - self.resize(400, 500) - self.setWindowFlags(Qt.Window | Qt.WindowStaysOnTopHint) - - self.center() - - self._update_deps() - - self._clipboard = QtWidgets.QApplication.clipboard() - self.ui.button_copy.clicked.connect(nonpartial(self._copy)) - - def _update_deps(self): - status = get_status_as_odict() - self._text = "" - for name, version in [('Glue', __version__)] + list(status.items()): - QtWidgets.QTreeWidgetItem(self.ui.version_tree.invisibleRootItem(), - [name, version]) - self._text += "{0}: {1}\n".format(name, version) - - def _copy(self): - self._clipboard.setText(self._text) - - -if __name__ == "__main__": - - from glue.utils.qt import get_qapp - app = get_qapp() - window = QVersionsDialog() - window.show() - window.exec_() +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.app.qt.versions is deprecated, use glue_qt.app.versions instead', GlueDeprecationWarning) +from glue_qt.app.versions import * # noqa diff --git a/glue/app/qt/versions.ui b/glue/app/qt/versions.ui deleted file mode 100644 index 8a8079c7d..000000000 --- a/glue/app/qt/versions.ui +++ /dev/null @@ -1,58 +0,0 @@ - - - Dialog - - - - 0 - 0 - 591 - 397 - - - - Version information - - - false - - - - - - - - true - - - QAbstractItemView::NoSelection - - - - Package - - - - - Version - - - - - - - - Copy information to clipboard - - - false - - - - - - - - - - diff --git a/glue/backends.py b/glue/backends.py index c72a034d0..50771e1f1 100644 --- a/glue/backends.py +++ b/glue/backends.py @@ -22,6 +22,19 @@ def start(self): pass +class SimpleTimer(TimerBase): + + def __init__(self, interval, callback): + from threading import Timer + self._timer = Timer(interval / 1000., callback) + + def start(self): + self._timer.start() + + def stop(self): + self._timer.cancel() + + class QtTimer(TimerBase): def __init__(self, interval, callback): @@ -37,12 +50,13 @@ def stop(self): self._timer.stop() -def get_timer(backend='qt'): - - if backend == 'qt': - return QtTimer +def get_timer(): + try: + from qtpy import QtCore # noqa + except ImportError: + return SimpleTimer else: - raise ValueError("Only QT Backend supported") + return QtTimer def get_backend(backend='qt'): diff --git a/glue/config.py b/glue/config.py index 8f7e99b6a..24a1129b8 100644 --- a/glue/config.py +++ b/glue/config.py @@ -12,22 +12,20 @@ """ __all__ = ['Registry', 'SettingRegistry', 'ExporterRegistry', - 'ColormapRegistry', 'DataFactoryRegistry', 'QtClientRegistry', + 'ColormapRegistry', 'DataFactoryRegistry', 'LinkFunctionRegistry', 'LinkHelperRegistry', 'ViewerToolRegistry', - 'LayerActionRegistry', 'ProfileFitterRegistry', 'qt_client', 'data_factory', + 'ProfileFitterRegistry', 'data_factory', 'link_function', 'link_helper', 'colormaps', 'exporters', 'settings', 'fit_plugin', 'auto_refresh', 'importer', 'DictRegistry', - 'preference_panes', 'PreferencePanesRegistry', - 'DataExporterRegistry', 'data_exporter', 'layer_action', + 'DataExporterRegistry', 'data_exporter', 'SubsetMaskExporterRegistry', 'SubsetMaskImporterRegistry', - 'StartupActionRegistry', 'startup_action', 'QtFixedLayoutTabRegistry', - 'qt_fixed_layout_tab', 'KeyboardShortcut', 'keyboard_shortcut', 'LayerArtistMakerRegistry', 'layer_artist_maker', 'SessionPatchRegistry', 'session_patch', 'AutoLinkerRegistry', 'autolinker', 'DataTranslatorRegistry', 'data_translator', 'SubsetDefinitionTranslatorRegistry', 'subset_state_translator', - 'UnitConverterRegistry', 'unit_converter'] + 'UnitConverterRegistry', 'unit_converter', + 'StretchRegistry', 'stretches'] CFG_DIR = os.path.join(os.path.expanduser('~'), '.glue') @@ -201,7 +199,7 @@ def is_default(self, setting): return setting in self._defaults and setting not in self._members -class QGlueParserRegistry(Registry): +class CLIParserRegistry(Registry): """ Registry for parsers that can be used to interpret arguments to the :func:`~glue.qglue` function. @@ -278,49 +276,6 @@ def adder(func): return adder -class MenubarPluginRegistry(Registry): - """ - Stores menubar plugins. - - The members property is a list of menubar plugins, each represented as a - ``(label, function)`` tuple. The ``function`` should take two items which - are a reference to the session and to the data collection respectively. - """ - - def add(self, label, function): - """ - Add a new menubar plugin - :param label: Short label for the plugin - :type label: str - - :param function: function - :type function: function() - """ - self.members.append((label, function)) - - def __call__(self, label): - def adder(func): - self.add(label, func) - return func - return adder - - -class PreferencePanesRegistry(DictRegistry): - """ - Stores preference panes - - The members property is a list of tuples of Qt widget classes that can have - their own tab in the preferences window. - """ - - def add(self, label, widget_cls): - self._members[label] = widget_cls - - def __iter__(self): - for label in self._members: - yield label, self._members[label] - - class AutoLinkerRegistry(Registry): """ Registry for auto-linking functions that given a data collection can suggest @@ -628,30 +583,30 @@ def adder(converter_cls): return adder -class QtClientRegistry(Registry): +class StretchRegistry(DictRegistry): """ - Stores QT widgets to visualize data. - - The members property is a list of Qt widget classes - - New widgets can be registered via:: - - @qt_client - class CustomWidget(QMainWindow): - ... + Stores custom stretches """ + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self._display = {} -class QtFixedLayoutTabRegistry(Registry): - """ - Stores Qt pre-defined tabs (non-MDI) + def add(self, label, stretch_cls, display=None): + if label in self.members: + raise ValueError("Stretch class '{0}' already registered".format(label)) + else: + self.members[label] = stretch_cls + self._display[label] = display or label - New widgets can be registered via:: + def __call__(self, label): + def adder(stretch_cls): + self.add(label, stretch_cls) + return stretch_cls + return adder - @qt_fixed_layout_tab - class CustomTab(QWidget): - ... - """ + def display_func(self, label): + return self._display[label] class ViewerToolRegistry(DictRegistry): @@ -672,37 +627,6 @@ def __call__(self, tool_cls): return tool_cls -class StartupActionRegistry(DictRegistry): - - def add(self, startup_name, startup_function): - """ - Add a startup function to the registry. This is a function that will - get called once glue has been started and any data loaded, and can - be used to set up specific layouts and create links. - - Startup actions are triggered by either specifying comma-separated names - of actions on the command-line:: - - glue --startup=mystartupaction - - or by passing an iterable of startup action names to the ``startup`` - keyword of ``GlueApplication``. - - The startup function will be given the session object and the data - collection object. - """ - if startup_name in self.members: - raise ValueError("A startup action with the name '{0}' already exists".format(startup_name)) - else: - self.members[startup_name] = startup_function - - def __call__(self, name): - def adder(func): - self.add(name, func) - return func - return adder - - class LinkFunctionRegistry(Registry): """Stores functions to convert between quantities @@ -733,46 +657,6 @@ def adder(func): return adder -class LayerActionRegistry(Registry): - """ - Stores custom menu actions available when the user select one or more - datasets, subset group, or subset in the data collection view. - - This members property is a list of named tuples with the following - attributes: - - * ``label``: the user-facing name of the action - * ``tooltip``: the text that appears when hovering with the mouse over the action - * ``callback``: the function to call when the action is triggered - * ``icon``: an icon image to use for the layer action - * ``single``: whether to show this action only when selecting single layers (default: `False`) - * ``data``: if ``single`` is `True` whether to only show the action when selecting a dataset - * ``subset_group``: if ``single`` is `True` whether to only show the action when selecting a subset group - * ``subset``: if ``single`` is `True` whether to only show the action when selecting a subset - - The callback function is called with two arguments. If ``single`` is - `True`, the first argument is the selected layer, otherwise it is the list - of selected layers. The second argument is the - `~glue.core.data_collection.DataCollection` object. - """ - item = namedtuple('LayerAction', 'label tooltip callback icon single data subset_group, subset') - - def __call__(self, label, callback=None, tooltip=None, icon=None, single=False, - data=False, subset_group=False, subset=False): - - # Backward-compatibility - if callback is not None: - self.add(self.item(label, tooltip, callback, icon, True, - False, False, True)) - return True - - def adder(func): - self.add(self.item(label, tooltip, func, icon, single, - data, subset_group, subset)) - return func - return adder - - class LinkHelperRegistry(Registry): """ Stores helper objects that compute many ComponentLinks at once @@ -847,52 +731,6 @@ def __call__(self, state=None): return self.state -class KeyboardShortcut(DictRegistry): - """ - Stores keyboard shortcuts. - The members property is a dictionary within a dictionary of keyboard - shortcuts, which is represented as (viewer,(keybind,function)). The - ``function`` should take one item, which is a reference to the session. - """ - - def add(self, valid_viewers, keybind, function): - """ - Add a new keyboard shortcut - - Parameters - ---------- - arg1: list - list of viewers where event can be fired - arg2: Qt.Key - type of key event - arg3: function() - function to be run that corresponds with key - """ - if valid_viewers: - for viewer in valid_viewers: - if viewer in self.members: - if keybind in self.members[viewer]: - raise ValueError("Keyboard shortcut '{0}' already registered in {1}".format(keybind, viewer)) - else: - self.members[viewer][keybind] = function - else: - self.members[viewer] = {keybind: function} - else: - if None in self.members: - if keybind in self.members[None]: - raise ValueError("Keyboard shortcut '{0}' already registered in {1}".format(keybind, None)) - else: - self.members[None][keybind] = function - else: - self.members[None] = {keybind: function} - - def __call__(self, keybind, valid_viewers): - def adder(func): - self.add(valid_viewers, keybind, func) - return func - return adder - - class LayerArtistMakerRegistry(Registry): """ A registry that allows customization of layer artists based on the data @@ -967,8 +805,6 @@ def __iter__(self): layer_artist_maker = LayerArtistMakerRegistry() -qt_client = QtClientRegistry() -qt_fixed_layout_tab = QtFixedLayoutTabRegistry() viewer_tool = ViewerToolRegistry() link_function = LinkFunctionRegistry() link_helper = LinkHelperRegistry() @@ -977,12 +813,7 @@ def __iter__(self): exporters = ExporterRegistry() settings = SettingRegistry() fit_plugin = ProfileFitterRegistry() -layer_action = LayerActionRegistry() -menubar_plugin = MenubarPluginRegistry() -preference_panes = PreferencePanesRegistry() -qglue_parser = QGlueParserRegistry() -startup_action = StartupActionRegistry() -keyboard_shortcut = KeyboardShortcut() +cli_parser = CLIParserRegistry() autolinker = AutoLinkerRegistry() session_patch = SessionPatchRegistry() @@ -1001,8 +832,17 @@ def __iter__(self): # Units unit_converter = UnitConverterRegistry() +# Stretch classes +from astropy.visualization import (LinearStretch, SqrtStretch, AsinhStretch, + LogStretch) +stretches = StretchRegistry() +stretches.add('linear', LinearStretch(), display='Linear') +stretches.add('sqrt', SqrtStretch(), display='Square Root') +stretches.add('arcsinh', AsinhStretch(), display='Arcsinh') +stretches.add('log', LogStretch(), display='Logarithmic') + # Backward-compatibility -single_subset_action = layer_action +qglue_parser = cli_parser def load_configuration(search_path=None): @@ -1099,3 +939,19 @@ def check_unit_converter(value): settings.add('UNIT_CONVERTER', 'default', validator=check_unit_converter) + +# It is difficult to emit deprecation warnigns for the following, so we just +# import these if glue-qt is installed. We can still remove these in future if +# needed but because we can't easily deprecate, we should leave them for a +# little while. + +try: + from glue_qt.config import (QtClientRegistry, qt_client, # noqa + LayerActionRegistry, layer_action, + MenubarPluginRegistry, menubar_plugin, + PreferencePanesRegistry, preference_panes, + StartupActionRegistry, startup_action, + QtFixedLayoutTabRegistry, qt_fixed_layout_tab, + KeyboardShortcut, keyboard_shortcut) +except ImportError: + pass diff --git a/glue/conftest.py b/glue/conftest.py index 92d500cf1..73a745cf3 100644 --- a/glue/conftest.py +++ b/glue/conftest.py @@ -61,28 +61,11 @@ def pytest_configure(config): from glue import config config.CFG_DIR = tempfile.mkdtemp() - # Start up QApplication, if the Qt code is present - try: - from glue.utils.qt import get_qapp - except Exception: - # Note that we catch any exception, not just ImportError, because - # QtPy can raise a PythonQtError. - pass - else: - get_qapp() - # Force loading of plugins from glue.main import load_plugins load_plugins() -def pytest_report_header(config): - from glue import __version__ - glue_version = "%20s:\t%s" % ("glue", __version__) - from glue._deps import get_status - return os.linesep + glue_version + os.linesep + os.linesep + get_status() - - def pytest_unconfigure(config): os.environ.pop('GLUE_TESTING') diff --git a/glue/core/__init__.py b/glue/core/__init__.py index f00ea34b7..42e41d298 100644 --- a/glue/core/__init__.py +++ b/glue/core/__init__.py @@ -13,4 +13,5 @@ from .visual import VisualAttributes # noqa # We import this last to avoid circular imports +from . import parsers # noqa from .application_base import Application # noqa diff --git a/glue/core/application_base.py b/glue/core/application_base.py index 164b49ad7..8c755814c 100644 --- a/glue/core/application_base.py +++ b/glue/core/application_base.py @@ -231,7 +231,7 @@ def add_data(self, *args, **kwargs): links = kwargs.pop('links', None) - from glue.qglue import parse_data, parse_links + from glue.core.parsers import parse_data, parse_links for label, data in kwargs.items(): datasets.extend(parse_data(data, label)) diff --git a/glue/core/data.py b/glue/core/data.py index dcbf71a4c..5293a488b 100644 --- a/glue/core/data.py +++ b/glue/core/data.py @@ -733,8 +733,6 @@ class Data(BaseCartesianData): data[xid, 0:2] data[xid, [True, False, True]] - See also: :ref:`data_tutorial` - Parameters ---------- label : `str` diff --git a/glue/core/data_derived.py b/glue/core/data_derived.py index f29959727..af14a1f29 100644 --- a/glue/core/data_derived.py +++ b/glue/core/data_derived.py @@ -73,7 +73,7 @@ def indices(self, value): changed = False for idim in range(self._original_data.ndim): before, after = self._indices[idim], value[idim] - if type(before) != type(after): + if type(before) is not type(after): raise TypeError("Can't change where the ``None`` values are in indices") elif before != after: changed = True diff --git a/glue/core/data_exporters/qt/__init__.py b/glue/core/data_exporters/qt/__init__.py index e69de29bb..87cca1e92 100644 --- a/glue/core/data_exporters/qt/__init__.py +++ b/glue/core/data_exporters/qt/__init__.py @@ -0,0 +1,4 @@ +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.core.data_exporters.qt is deprecated, use glue_qt.core.data_exporters instead', GlueDeprecationWarning) +from glue_qt.core.data_exporters import * # noqa diff --git a/glue/core/data_exporters/qt/dialog.py b/glue/core/data_exporters/qt/dialog.py index f73a634f8..88e4fb38f 100644 --- a/glue/core/data_exporters/qt/dialog.py +++ b/glue/core/data_exporters/qt/dialog.py @@ -1,29 +1,4 @@ -from qtpy import compat -from glue import config - - -def export_data(data, components=None, exporter=None): - - if exporter is None: - exporters = {} - for e in config.data_exporter: - if e.extension == '': - fltr = "{0} (*)".format(e.label) - else: - fltr = "{0} ({1})".format(e.label, ' '.join('*.' + ext for ext in e.extension)) - exporters[fltr] = e.function - filters = ';;'.join(sorted(exporters)) - else: - filters = None - - filename, fltr = compat.getsavefilename(caption="Choose an output filename", - filters=filters) - - filename = str(filename) - if not filename: - return - - if filters is not None: - exporter = exporters[fltr] - - exporter(filename, data, components=components) +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.core.data_exporters.qt.dialog is deprecated, use glue_qt.core.data_exporters.dialog instead', GlueDeprecationWarning) +from glue_qt.core.data_exporters.dialog import * # noqa diff --git a/glue/core/data_exporters/qt/tests/__init__.py b/glue/core/data_exporters/qt/tests/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/glue/core/data_exporters/qt/tests/test_dialog.py b/glue/core/data_exporters/qt/tests/test_dialog.py deleted file mode 100644 index c86d8c831..000000000 --- a/glue/core/data_exporters/qt/tests/test_dialog.py +++ /dev/null @@ -1,27 +0,0 @@ -from unittest.mock import patch, MagicMock -from collections import namedtuple -from glue.core import Data - -from ..dialog import export_data - - -def test_export(tmpdir): - - filename = tmpdir.join('data') - - data = Data(x=[1, 2, 3]) - - mock = MagicMock() - - test_exporter_cls = namedtuple('exporter', 'function label extension') - test_exporter = test_exporter_cls(function=mock, label='Test', extension='') - - with patch('qtpy.compat.getsavefilename') as dialog: - with patch('glue.config.data_exporter') as data_exporter: - def test_iter(x): - yield test_exporter - data_exporter.__iter__ = test_iter - dialog.return_value = filename, 'Test (*)' - export_data(data) - - assert test_exporter.function.call_args[0] == (filename, data) diff --git a/glue/core/data_factories/astropy_table.py b/glue/core/data_factories/astropy_table.py index 59a17cbd8..6ad2174c7 100644 --- a/glue/core/data_factories/astropy_table.py +++ b/glue/core/data_factories/astropy_table.py @@ -3,7 +3,7 @@ from glue.core.data_factories.helpers import has_extension from glue.core.data import Component, Data -from glue.config import data_factory, qglue_parser +from glue.config import data_factory, cli_parser __all__ = ['astropy_tabular_data', 'sextractor_factory', 'cds_factory', @@ -134,7 +134,7 @@ def factory(file, **kwargs): except ImportError: pass else: - @qglue_parser(Table) + @cli_parser(Table) def _parse_data_astropy_table(data, label): kwargs = dict((c, data[c]) for c in data.columns) return [Data(label=label, **kwargs)] diff --git a/glue/core/data_factories/fits.py b/glue/core/data_factories/fits.py index 8aa855e6a..c21a89783 100644 --- a/glue/core/data_factories/fits.py +++ b/glue/core/data_factories/fits.py @@ -5,7 +5,7 @@ from glue.core.coordinates import coordinates_from_header, WCSCoordinates from glue.core.data import Component, Data -from glue.config import data_factory, qglue_parser +from glue.config import data_factory, cli_parser __all__ = ['is_fits', 'fits_reader', 'is_casalike', 'casalike_cube'] @@ -234,7 +234,7 @@ def casalike_cube(filename, **kwargs): pass else: # Put HDUList parser before list parser - @qglue_parser(HDUList, priority=100) + @cli_parser(HDUList, priority=100) def _parse_data_hdulist(data, label): from glue.core.data_factories.fits import fits_reader return fits_reader(data, label=label) diff --git a/glue/core/data_factories/helpers.py b/glue/core/data_factories/helpers.py index b34b15a7a..332480227 100644 --- a/glue/core/data_factories/helpers.py +++ b/glue/core/data_factories/helpers.py @@ -147,7 +147,7 @@ def reload(self): mapping = dict((c, log.component(self.id(c)).data) for c in dold._components.values() if c in self.components and - type(c) == Component) + type(c) is Component) dold.coords = dnew.coords dold.update_components(mapping) @@ -254,7 +254,8 @@ def load_data(path, factory=None, **kwargs): Extra keywords are passed through to factory functions. """ - from glue.qglue import parse_data + + from glue.core.parsers import parse_data coord_first = kwargs.pop('coord_first', True) force_coords = kwargs.pop('force_coords', False) diff --git a/glue/core/data_factories/tests/test_data_factories.py b/glue/core/data_factories/tests/test_data_factories.py index 303a9d24d..9d5ddbd7e 100644 --- a/glue/core/data_factories/tests/test_data_factories.py +++ b/glue/core/data_factories/tests/test_data_factories.py @@ -8,7 +8,7 @@ from glue.core.data import BaseCartesianData, Data from glue.core import data_factories as df from glue.config import data_factory -from glue.tests.helpers import requires_astropy, make_file, requires_qt +from glue.tests.helpers import requires_astropy, make_file def test_load_data_auto_assigns_label(): @@ -213,8 +213,6 @@ def test_data_reload_shape_change(): assert d.coords is coords_old -# TODO: this doesn't belong in the core since it relies on Qt -@requires_qt def test_file_watch(): cb = MagicMock() with make_file(b'test', 'csv') as fname: @@ -232,7 +230,6 @@ def test_file_watch(): assert cb.call_count == 1 -@requires_qt @pytest.mark.skipif(sys.platform.startswith('win'), reason='file deletion doesn\'t work on Windows') def test_file_watch_os_error(): cb = MagicMock() diff --git a/glue/core/fitters.py b/glue/core/fitters.py index ef75bc8f6..75f4370b2 100644 --- a/glue/core/fitters.py +++ b/glue/core/fitters.py @@ -1,9 +1,6 @@ """ Glue's fitting classes are designed to be easily subclassed for performing custom model fitting in Glue. - -See the guide on :ref:`writing custom fit plugins ` for -help with using custom fitting utilities in Glue. """ import numpy as np diff --git a/glue/core/layer_artist.py b/glue/core/layer_artist.py index d98889291..b7b5b8756 100644 --- a/glue/core/layer_artist.py +++ b/glue/core/layer_artist.py @@ -265,7 +265,7 @@ def on_changed(self, func): def _duplicate(self, artist): for a in self.artists: - if type(a) == type(artist) and a.layer is artist.layer: + if type(a) is type(artist) and a.layer is artist.layer: return True return False diff --git a/glue/core/parsers/__init__.py b/glue/core/parsers/__init__.py new file mode 100644 index 000000000..68d9676fa --- /dev/null +++ b/glue/core/parsers/__init__.py @@ -0,0 +1 @@ +from .parsers import parse_data, parse_links # noqa diff --git a/glue/core/parsers/parsers.py b/glue/core/parsers/parsers.py new file mode 100644 index 000000000..ed68c3c96 --- /dev/null +++ b/glue/core/parsers/parsers.py @@ -0,0 +1,120 @@ +# Functions used to parse data and links from Python command-line + +import numpy as np + +from glue.config import cli_parser +from glue.core import BaseData, Data + +__all__ = [] + + +@cli_parser(dict) +def _parse_data_dict(data, label): + result = Data(label=label) + for label, component in data.items(): + result.add_component(component, label) + return [result] + + +@cli_parser(np.recarray) +def _parse_data_recarray(data, label): + kwargs = dict((n, data[n]) for n in data.dtype.names) + return [Data(label=label, **kwargs)] + + +@cli_parser(BaseData) +def _parse_data_glue_data(data, label): + if isinstance(data, Data): + data.label = label + return [data] + + +@cli_parser(np.ndarray) +def _parse_data_numpy(data, label): + return [Data(**{label: data, 'label': label})] + + +@cli_parser(list) +def _parse_data_list(data, label): + return [Data(**{label: data, 'label': label})] + + +@cli_parser(str) +def _parse_data_path(path, label): + from glue.core.data_factories import load_data, as_list + + data = load_data(path) + for d in as_list(data): + d.label = label + return as_list(data) + + +def parse_data(data, label): + + # First try new data translation layer + + from glue.config import data_translator + + try: + handler, preferred = data_translator.get_handler_for(data) + except TypeError: + pass + else: + data = handler.to_data(data) + data.label = label + data._preferred_translation = preferred + return [data] + + # Then try legacy 'cli_parser' infrastructure + + for item in cli_parser: + + data_class = item.data_class + parser = item.parser + if isinstance(data, data_class): + try: + return parser(data, label) + except Exception as e: + raise ValueError("Invalid format for data '%s'\n\n%s" % + (label, e)) + + raise TypeError("Invalid data description: %s" % data) + + +def parse_links(dc, links): + from glue.core.link_helpers import MultiLink + from glue.core import ComponentLink + + data = dict((d.label, d) for d in dc) + result = [] + + def find_cid(s): + dlabel, clabel = s.split('.') + d = data[dlabel] + c = d.find_component_id(clabel) + if c is None: + raise ValueError("Invalid link (no component named %s)" % s) + return c + + for link in links: + f, t = link[0:2] # from and to component names + u = u2 = None + if len(link) >= 3: # forward translation function + u = link[2] + if len(link) == 4: # reverse translation function + u2 = link[3] + + # component names -> component IDs + if isinstance(f, str): + f = [find_cid(f)] + else: + f = [find_cid(item) for item in f] + + if isinstance(t, str): + t = find_cid(t) + result.append(ComponentLink(f, t, u)) + else: + t = [find_cid(item) for item in t] + result += MultiLink(f, t, u, u2) + + return result diff --git a/glue/core/qt/__init__.py b/glue/core/qt/__init__.py index e69de29bb..52b7c076a 100644 --- a/glue/core/qt/__init__.py +++ b/glue/core/qt/__init__.py @@ -0,0 +1,4 @@ +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.core.qt is deprecated, use glue_qt.core instead', GlueDeprecationWarning) +from glue_qt.core import * # noqa diff --git a/glue/core/qt/data_collection_model.py b/glue/core/qt/data_collection_model.py index 43e4a8045..78300433e 100644 --- a/glue/core/qt/data_collection_model.py +++ b/glue/core/qt/data_collection_model.py @@ -1,554 +1,4 @@ -# pylint: disable=E1101,F0401 - -from qtpy import QtCore, QtGui, QtWidgets -from qtpy.QtCore import Qt -from glue.core.hub import HubListener -from glue.core import message as m -from glue.core.decorators import memoize -from glue import core -from glue.core.qt.mime import LAYERS_MIME_TYPE -from glue.icons.qt import layer_icon - -from glue.core.qt.style_dialog import StyleDialog -from glue.utils.qt import PyMimeData -from glue.core.message import Message - -DATA_IDX = 0 -SUBSET_IDX = 1 - - -def full_edit_factory(item, pos): - StyleDialog.dropdown_editor(item, pos) - - -def restricted_edit_factory(item, pos): - StyleDialog.dropdown_editor(item, pos, edit_label=False) - - -class Item(object): - edit_factory = None - glue_data = None - flags = Qt.ItemIsEnabled - tooltip = None - - def font(self): - return QtGui.QFont() - - def icon(self): - return None - - @property - def label(self): - return self._label - - -class DataCollectionItem(Item): - - def __init__(self, dc): - self.dc = dc - self.row = 0 - self.column = 0 - self.parent = None - self._label = '' - self.children_count = 2 - - @memoize - def child(self, row): - if row == DATA_IDX: - return DataListItem(self.dc, self) - if row == SUBSET_IDX: - return SubsetListItem(self.dc, self) - return None - - -class DataListItem(Item): - - def __init__(self, dc, parent): - self.dc = dc - self.parent = parent - self.row = DATA_IDX - self.column = 0 - self._label = 'Data' - - @memoize - def child(self, row): - if row < len(self.dc): - return DataItem(self.dc, row, self) - - @property - def children_count(self): - return len(self.dc) - - def font(self): - result = QtGui.QFont() - result.setBold(True) - return result - - -class DataItem(Item): - edit_factory = full_edit_factory - flags = (Qt.ItemIsSelectable | Qt.ItemIsEnabled | - Qt.ItemIsDragEnabled) - - def __init__(self, dc, row, parent): - self.dc = dc - self.row = row - self.parent = parent - self.column = 0 - self.children_count = 0 - - @property - def data(self): - return self.dc[self.row] - - @property - def glue_data(self): - return self.data - - @property - def label(self): - return self.data.label - - @label.setter - def label(self, value): - self.data.label = value - - @property - def tooltip(self): - # Return the label as the tooltip - this is useful if filenames are - # really long and don't fit in the window. - return self.label - - @property - def style(self): - return self.data.style - - def icon(self): - return layer_icon(self.data) - - -class SubsetListItem(Item): - - def __init__(self, dc, parent): - self.dc = dc - self.parent = parent - self.row = SUBSET_IDX - self._label = 'Subsets' - self.column = 0 - - @memoize - def child(self, row): - if row < len(self.dc.subset_groups): - return SubsetGroupItem(self.dc, row, self) - - @property - def children_count(self): - return len(self.dc.subset_groups) - - def font(self): - result = QtGui.QFont() - result.setBold(True) - return result - - -class SubsetGroupItem(Item): - edit_factory = full_edit_factory - flags = Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemIsEditable - - def __init__(self, dc, row, parent): - self.parent = parent - self.dc = dc - self.row = row - self.column = 0 - - @property - def subset_group(self): - return self.dc.subset_groups[self.row] - - @property - def glue_data(self): - return self.subset_group - - @property - def label(self): - return self.subset_group.label - - @label.setter - def label(self, value): - self.subset_group.label = value - - @property - def tooltip(self): - if type(self.subset_group.subset_state) == core.subset.SubsetState: - return "Empty subset" - - atts = self.subset_group.subset_state.attributes - atts = [a for a in atts if isinstance(a, core.ComponentID)] - - if len(atts) > 0: - lbl = ', '.join(a.label for a in atts) - return "Selection on %s" % lbl - - @property - def style(self): - return self.subset_group.style - - @property - def children_count(self): - return len(self.subset_group.subsets) - - @memoize - def child(self, row): - return SubsetItem(self.dc, self.subset_group, row, self) - - def icon(self): - return layer_icon(self.subset_group) - - -class SubsetItem(Item): - edit_factory = restricted_edit_factory - flags = Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled - - def __init__(self, dc, subset_group, subset_idx, parent): - self.parent = parent - self.subset_group = subset_group - self.row = subset_idx - self.parent = parent - self.children_count = 0 - self.column = 0 - - @property - def subset(self): - return self.subset_group.subsets[self.row] - - @property - def label(self): - return self.subset.verbose_label - - def icon(self): - return layer_icon(self.subset) - - @property - def style(self): - return self.subset.style - - @property - def glue_data(self): - return self.subset - - -class DataCollectionModel(QtCore.QAbstractItemModel, HubListener): - new_item = QtCore.Signal(QtCore.QModelIndex) - - def __init__(self, data_collection, parent=None): - QtCore.QAbstractItemModel.__init__(self, parent) - HubListener.__init__(self) - - self.data_collection = data_collection - self.root = DataCollectionItem(data_collection) - self._items = {} # map hashes of Model pointers to model items - # without this reference, PySide clobbers instance - # data of model items - self.register_to_hub(self.data_collection.hub) - - def supportedDragActions(self): - return Qt.CopyAction - - def index(self, row, column, parent=QtCore.QModelIndex()): - if column != 0: - return QtCore.QModelIndex() - - if not parent.isValid(): - parent_item = self.root - else: - parent_item = self._get_item(parent) - if parent_item is None: - return QtCore.QModelIndex() - - child_item = parent_item.child(row) - if child_item: - return self._make_index(row, column, child_item) - else: - return QtCore.QModelIndex() - - def _get_item(self, index): - if not index.isValid(): - return None - return self._items.get(id(index.internalPointer()), None) - - def _make_index(self, row, column, item): - if item is not None: - result = self.createIndex(row, column, item) - self._items[id(result.internalPointer())] = item - assert result.internalPointer() is item - return result - return self.createIndex(row, column) - - def to_indices(self, items): - """Translate a list of Data, Subset, or SubsetGroups - to a list of indices""" - result = [] - for item in items: - if isinstance(item, core.Data): - idx = self.data_index(list(self.data_collection).index(item)) - elif isinstance(item, core.SubsetGroup): - idx = self.subsets_index( - self.data_collection.subset_groups.index(item)) - elif isinstance(item, core.subset_group.GroupedSubset): - grp = item.group - idx = self.subsets_index( - self.data_collection.subset_groups.index(grp)) - row = list(self.data_collection).index(item.data) - idx = self.index(row, idx) - else: - raise NotImplementedError(type(item)) - result.append(idx) - return result - - def flags(self, index=QtCore.QModelIndex()): - item = self._get_item(index) - if item is None: - return Qt.NoItemFlags - else: - return item.flags - - def data(self, index, role): - if not index.isValid(): - return - - dispatch = { - Qt.DisplayRole: self._display_data, - Qt.FontRole: self._font_data, - Qt.DecorationRole: self._icon_data, - Qt.UserRole: self._user_data, - Qt.ToolTipRole: self._tooltip_data} - - if role in dispatch: - return dispatch[role](index) - - def setData(self, index, value, role=Qt.EditRole): - if role != Qt.EditRole: - return False - try: - self._get_item(index).label = value - return True - except AttributeError: - return False - - def _tooltip_data(self, index): - tooltip = self._get_item(index).tooltip - return tooltip - - def _user_data(self, index): - return self._get_item(index) - - def _display_data(self, index): - return self._get_item(index).label - - def _font_data(self, index): - item = self._get_item(index) - return item.font() - - def _icon_data(self, index): - return self._get_item(index).icon() - - def headerData(self, section, orientation, role=Qt.DisplayRole): - return '' - - def data_index(self, data_number=None): - """ - Fetch the QtCore.QModelIndex for a given data index, - or the index for the parent data item - - :param data_number: position of data set to fetch, or None - """ - base = self.index(DATA_IDX, 0) - if data_number is None: - return base - return self.index(data_number, 0, base) - - def subsets_index(self, subset_number=None): - """ - Fetch the QtCore.QModelIndex for a given subset, - or the index for the parent subset item - - :param data_number: position of subset group to fetch, or None - """ - base = self.index(SUBSET_IDX, 0) - assert isinstance(self._get_item(base), SubsetListItem) - if subset_number is None: - return base - return self.index(subset_number, 0, base) - - def rowCount(self, index=QtCore.QModelIndex()): - item = self._get_item(index) - - if item is None: - return self.root.children_count - - return item.children_count - - def parent(self, index=None): - - if index is None: # overloaded QtCore.QObject.parent() - return QtCore.QObject.parent(self) - - item = self._get_item(index) - if item is None: - return QtCore.QModelIndex() - - return self._make_index(item.row, item.column, item.parent) - - def columnCount(self, index): - return 1 - - def register_to_hub(self, hub): - for msg in [m.DataCollectionDeleteMessage, - m.SubsetDeleteMessage]: - hub.subscribe(self, msg, self.invalidate) - - hub.subscribe(self, m.DataCollectionAddMessage, self._on_add_data) - hub.subscribe(self, m.SubsetCreateMessage, self._on_add_subset) - - def _on_add_data(self, message): - self.invalidate() - idx = self.data_index(len(self.data_collection) - 1) - self.new_item.emit(idx) - - def _on_add_subset(self, message): - self.invalidate() - idx = self.subsets_index(len(self.data_collection.subset_groups) - 1) - self.new_item.emit(idx) - - def invalidate(self, *args): - self.root = DataCollectionItem(self.data_collection) - self._items.clear() - self.layoutChanged.emit() - - def glue_data(self, indices): - """ Given a list of indices, return a list of all selected - Data, Subset, and SubsetGroup objects. - """ - items = [self._get_item(idx) for idx in indices] - items = [item.glue_data for item in items] - return items - - def mimeData(self, indices): - data = self.glue_data(indices) - result = PyMimeData(data, **{LAYERS_MIME_TYPE: data}) - self._mime = result # hold reference to prevent segfault - return result - - def mimeTypes(self): - return [LAYERS_MIME_TYPE] - - -class DataCollectionView(QtWidgets.QTreeView, HubListener): - selection_changed = QtCore.Signal() - - def __init__(self, parent=None): - super(DataCollectionView, self).__init__(parent) - self.doubleClicked.connect(self._edit) - - # only edit label on model.new_item - self.setItemDelegate(LabeledDelegate()) - self.setEditTriggers(self.NoEditTriggers) - - self.setIconSize(QtCore.QSize(16, 16)) - - def selected_layers(self): - idxs = self.selectedIndexes() - return self._model.glue_data(idxs) - - def set_selected_layers(self, layers): - sm = self.selectionModel() - idxs = self._model.to_indices(layers) - self.select_indices(*idxs) - - def select_indices(self, *indices): - sm = self.selectionModel() - sm.clearSelection() - for idx in indices: - sm.select(idx, sm.Select) - - def set_data_collection(self, data_collection): - self._model = DataCollectionModel(data_collection) - self.setModel(self._model) - - sm = QtCore.QItemSelectionModel(self._model) - sm.selectionChanged.connect(lambda *args: - self.selection_changed.emit()) - self.setSelectionModel(sm) - - self.setRootIsDecorated(False) - self.setExpandsOnDoubleClick(False) - self.expandToDepth(0) - self._model.layoutChanged.connect(lambda: self.expandToDepth(0)) - self._model.layoutChanged.connect(self.selection_changed.emit) - self._model.new_item.connect(self.select_indices) - - self.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows) - self.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection) - self.setDragEnabled(True) - self.setDropIndicatorShown(True) - self.setDragDropMode(QtWidgets.QAbstractItemView.DragOnly) - - # Update when any message is emitted which would indicate a change in - # data collection content, colors, labels, etc. It's easier to simply - # listen to all events since the viewport update is fast. - data_collection.hub.subscribe(self, Message, handler=self._update_viewport) - - def _update_viewport(self, *args, **kwargs): - # This forces the widget containing the list view to update/redraw, - # reflecting any changes in color/labels/content - try: - self.viewport().update() - except RuntimeError: - pass - - def edit_label(self, index): - if not (self._model.flags(index) & Qt.ItemIsEditable): - return - self.edit(index) - - def _edit(self, index): - item = self._model.data(index, role=Qt.UserRole) - if item is None or item.edit_factory is None: - return - - rect = self.visualRect(index) - pos = self.mapToGlobal(rect.bottomLeft()) - pos.setY(pos.y() + 1) - item.edit_factory(pos) - - -class LabeledDelegate(QtWidgets.QStyledItemDelegate): - - """ Add placeholder text to default delegate """ - - def setEditorData(self, editor, index): - super(LabeledDelegate, self).setEditorData(editor, index) - label = index.model().data(index, role=Qt.DisplayRole) - editor.selectAll() - editor.setText(label) - - -if __name__ == "__main__": - - from glue.utils.qt import get_qapp - from qtpy import QtWidgets - from glue.core import Data, DataCollection - - app = get_qapp() - - dc = DataCollection() - dc.append(Data(label='w')) - - view = DataCollectionView() - view.set_data_collection(dc) - view.show() - view.raise_() - dc.extend([Data(label='x', x=[1, 2, 3]), - Data(label='y', y=[1, 2, 3]), - Data(label='z', z=[1, 2, 3])]) - app.exec_() +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.core.qt.data_collection_model is deprecated, use glue_qt.core.data_collection_model instead', GlueDeprecationWarning) +from glue_qt.core.data_collection_model import * # noqa diff --git a/glue/core/qt/dialogs.py b/glue/core/qt/dialogs.py index 990d24bbd..117ecd691 100644 --- a/glue/core/qt/dialogs.py +++ b/glue/core/qt/dialogs.py @@ -1,52 +1,4 @@ -# Infrastructure for helpful warnings/dialogs that can be hidden if needed -# (and therefore connect to the settings). - -from qtpy.QtWidgets import QMessageBox, QCheckBox - -from glue.config import settings -from glue._settings_helpers import save_settings - -__all__ = ['info', 'warn'] - - -def info(title, text, setting=None, default=None): - return dialog(title, text, QMessageBox.Information, setting=setting, default=default) - - -def warn(title, text, setting=None, default=None): - return dialog(title, text, QMessageBox.Warning, setting=setting, default=default) - - -def dialog(title, text, icon, setting=None, default=None): - - if not getattr(settings, setting.upper()): - return True - - check = QCheckBox() - check.setText('Don\'t show this message again (can be reset via the preferences)') - - info = QMessageBox() - info.setIcon(icon) - info.setText(title) - info.setInformativeText(text) - info.setCheckBox(check) - info.setStandardButtons(info.Cancel | info.Ok) - if default == 'Cancel': - info.setDefaultButton(info.Cancel) - - result = info.exec_() - - if result == info.Cancel: - return False - - if check.isChecked(): - setattr(settings, setting.upper(), False) - save_settings() - - return True - - -if __name__ == "__main__": - from glue.utils.qt import get_qapp - app = get_qapp() - info('What happens next?', 'These are instructions on what happens next', setting='show_info_profile_open') +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.core.qt.dialogs is deprecated, use glue_qt.core.dialogs instead', GlueDeprecationWarning) +from glue_qt.core.dialogs import * # noqa diff --git a/glue/core/qt/fitters.py b/glue/core/qt/fitters.py index 225fef56c..54bb772cc 100644 --- a/glue/core/qt/fitters.py +++ b/glue/core/qt/fitters.py @@ -1,158 +1,4 @@ -from qtpy import QtWidgets, QtGui - -from glue.core.qt.simpleforms import build_form_item - -__all__ = ['ConstraintsWidget', 'FitSettingsWidget'] - - -class ConstraintsWidget(QtWidgets.QWidget): - - """ - A widget to display and tweak the constraints of a :class:`~glue.core.fitters.BaseFitter1D` - """ - - def __init__(self, constraints, parent=None): - """ - Parameters - ---------- - constraints : dict - The `contstraints` property of a :class:`~glue.core.fitters.BaseFitter1D` - object - parent : QtWidgets.QWidget (optional) - The parent of this widget - """ - super(ConstraintsWidget, self).__init__(parent) - self.constraints = constraints - - self.layout = QtWidgets.QGridLayout() - self.layout.setContentsMargins(2, 2, 2, 2) - self.layout.setSpacing(4) - - self.setLayout(self.layout) - - self.layout.addWidget(QtWidgets.QLabel("Estimate"), 0, 1) - self.layout.addWidget(QtWidgets.QLabel("Fixed"), 0, 2) - self.layout.addWidget(QtWidgets.QLabel("Bounded"), 0, 3) - self.layout.addWidget(QtWidgets.QLabel("Lower Bound"), 0, 4) - self.layout.addWidget(QtWidgets.QLabel("Upper Bound"), 0, 5) - - self._widgets = {} - names = sorted(list(self.constraints.keys())) - - for k in names: - row = [] - w = QtWidgets.QLabel(k) - row.append(w) - - v = QtGui.QDoubleValidator() - e = QtWidgets.QLineEdit() - e.setValidator(v) - e.setText(str(constraints[k]['value'] or '')) - row.append(e) - - w = QtWidgets.QCheckBox() - w.setChecked(constraints[k]['fixed']) - fix = w - row.append(w) - - w = QtWidgets.QCheckBox() - limits = constraints[k]['limits'] - w.setChecked(limits is not None) - bound = w - row.append(w) - - e = QtWidgets.QLineEdit() - e.setValidator(v) - if limits is not None: - e.setText(str(limits[0])) - row.append(e) - - e = QtWidgets.QLineEdit() - e.setValidator(v) - if limits is not None: - e.setText(str(limits[1])) - row.append(e) - - def unset(w): - def result(active): - if active: - w.setChecked(False) - return result - - fix.toggled.connect(unset(bound)) - bound.toggled.connect(unset(fix)) - - self._widgets[k] = row - - for i, row in enumerate(names, 1): - for j, widget in enumerate(self._widgets[row]): - self.layout.addWidget(widget, i, j) - - def settings(self, name): - """ Return the constraints for a single model parameter """ - row = self._widgets[name] - name, value, fixed, limited, lo, hi = row - value = float(value.text()) if value.text() else None - fixed = fixed.isChecked() - limited = limited.isChecked() - lo = lo.text() - hi = hi.text() - limited = limited and not ((not lo) or (not hi)) - limits = None if not limited else [float(lo), float(hi)] - return dict(value=value, fixed=fixed, limits=limits) - - def update_constraints(self, fitter): - """ Update the constraints in a :class:`~glue.core.fitters.BaseFitter1D` - based on the settings in this widget - """ - for name in self._widgets: - s = self.settings(name) - fitter.set_constraint(name, **s) - - -class FitSettingsWidget(QtWidgets.QDialog): - - def __init__(self, fitter, parent=None): - super(FitSettingsWidget, self).__init__(parent) - self.fitter = fitter - - self._build_form() - self._connect() - self.setModal(True) - - def _build_form(self): - fitter = self.fitter - - l = QtWidgets.QFormLayout() - options = fitter.options - self.widgets = {} - self.forms = {} - - for k in sorted(options): - item = build_form_item(fitter, k) - l.addRow(item.label, item.widget) - self.widgets[k] = item.widget - self.forms[k] = item # need to prevent garbage collection - - constraints = fitter.constraints - if constraints: - self.constraints = ConstraintsWidget(constraints) - l.addRow(self.constraints) - else: - self.constraints = None - - self.okcancel = QtWidgets.QDialogButtonBox(QtWidgets.QDialogButtonBox.Ok | - QtWidgets.QDialogButtonBox.Cancel) - l.addRow(self.okcancel) - self.setLayout(l) - - def _connect(self): - self.okcancel.accepted.connect(self.accept) - self.okcancel.rejected.connect(self.reject) - self.accepted.connect(self.update_fitter_from_settings) - - def update_fitter_from_settings(self): - for k, v in self.widgets.items(): - setattr(self.fitter, k, v.value()) - if self.constraints is not None: - self.constraints.update_constraints(self.fitter) +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.core.qt.fitters is deprecated, use glue_qt.core.fitters instead', GlueDeprecationWarning) +from glue_qt.core.fitters import * # noqa diff --git a/glue/core/qt/layer_artist_model.py b/glue/core/qt/layer_artist_model.py index 6c9994fb7..af0a56455 100644 --- a/glue/core/qt/layer_artist_model.py +++ b/glue/core/qt/layer_artist_model.py @@ -1,427 +1,4 @@ -""" -This module provides two classes for managing LayerArtists with Qt. - -The LayerArtistModel implements a QtModel to interface with a list of -LayerManagers. - -The LayerArtistView is a list widget that displays -these layers, and provides GUI access to the model -""" -# pylint: disable=I0011, W0613, R0913, R0904, W0611 - -import textwrap -from weakref import WeakKeyDictionary - -from qtpy.QtCore import Qt -from qtpy import QtCore, QtWidgets, QtGui -from glue.core.layer_artist import LayerArtistBase, LayerArtistContainer -from glue.core.qt.style_dialog import StyleDialog -from glue.icons.qt import layer_artist_icon -from glue.core.qt.mime import LAYERS_MIME_TYPE -from glue.utils import nonpartial -from glue.utils.qt import PythonListModel, PyMimeData -from glue.core.hub import HubListener -from glue.core.message import (LayerArtistEnabledMessage, - LayerArtistUpdatedMessage, - LayerArtistDisabledMessage, - LayerArtistVisibilityMessage) - - -class LayerArtistModel(PythonListModel): - - """A Qt model to manage a list of LayerArtists. Multiple - views into this model should stay in sync, thanks to Qt. - - To properly maintain sync, any client that uses - this list of LayerArtists should always edit the - list in-place (so that the list managed by this model - and the client are the same object) - """ - - def __init__(self, artists, parent=None): - super(LayerArtistModel, self).__init__(artists, parent) - self.artists = artists - - def data(self, index, role): - """Retrieve data at each index""" - if not index.isValid() or index.row() >= len(self.artists): - return None - if role == Qt.DecorationRole: - art = self.artists[index.row()] - result = layer_artist_icon(art) - return result - if role == Qt.CheckStateRole: - art = self.artists[index.row()] - result = Qt.Checked if art.visible and art.enabled else Qt.Unchecked - return result - if role == Qt.ToolTipRole: - art = self.artists[index.row()] - if not art.enabled: - wrapped = textwrap.fill(art.disabled_message, break_long_words=False) - return wrapped - - return super(LayerArtistModel, self).data(index, role) - - def flags(self, index): - result = super(LayerArtistModel, self).flags(index) - if index.isValid() and index.row() < len(self.artists): - art = self.artists[index.row()] - if art.enabled: - result = (result | Qt.ItemIsEditable | Qt.ItemIsDragEnabled | - Qt.ItemIsUserCheckable) - else: - result = (result & Qt.ItemIsUserCheckable) ^ result - else: # only drop between rows, where index isn't valid - result = result | Qt.ItemIsDropEnabled - - return result - - def setData(self, index, value, role): - if not index.isValid(): - return False - if role == Qt.EditRole: - self.change_label(index.row(), str(value)) - if role == Qt.CheckStateRole: - if isinstance(value, int): - try: # Qt6 - vis = value == Qt.Checked.value # https://bugreports.qt.io/browse/QTBUG-104688 - except AttributeError: # Qt5 - vis = value == Qt.Checked - else: - vis = value == Qt.Checked - self.artists[index.row()].visible = vis - self.artists[index.row()].redraw() - self.dataChanged.emit(index, index) - return True - - def _remove_row(self, row): - art = self.artists.pop(row) - art.remove() - art.redraw() - - def mimeTypes(self): - return [PyMimeData.MIME_TYPE, LAYERS_MIME_TYPE] - - def mimeData(self, indexes): - arts = [self.artists[index.row()] for index in indexes] - layers = [a.layer for a in arts] - - if len(indexes) == 0: - return 0 - return PyMimeData(arts, **{LAYERS_MIME_TYPE: layers}) - - def supportedDropActions(self): - return Qt.MoveAction - - def dropMimeData(self, data, action, row, column, index): - data = data.data(PyMimeData.MIME_TYPE) - # list of a single artist. Move - if isinstance(data, list) and len(data) == 1 and \ - isinstance(data[0], LayerArtistBase) and \ - data[0] in self.artists: - self.move_artist(data[0], row) - return True - - return False - - def move_artist(self, artist, row): - """Move an artist before the entry in row - - Row could be the end of the list (-> put it at the end) - """ - if len(self.artists) < 2: # can't rearrange lenght 0 or 1 list - return - - try: - loc = self.artists.index(artist) - except ValueError: - return - - dest = row - if not self.beginMoveRows(QtCore.QModelIndex(), loc, loc, - QtCore.QModelIndex(), dest): - return - if dest >= loc: - row -= 1 - self.artists.pop(loc) - self.artists.insert(row, artist) - self._update_zorder() - self.endMoveRows() - - def _update_zorder(self): - """Redistribute zorders to match location in the list""" - zs = [m.zorder for m in self.artists] - zs = reversed(sorted(zs)) - for z, m in zip(zs, self.artists): - m.zorder = z - if len(self.artists) > 0: - self.artists[0].redraw() - - def row_label(self, row): - """ The textual label for the row""" - artist = self.artists[row] - if hasattr(artist, 'label'): - return artist.label - layer = artist.layer - if hasattr(layer, 'verbose_label'): - return layer.verbose_label - return layer.label - - def change_label(self, row, label): - """ Reassign the labeel for whatever layer the artist manages""" - try: - art = self.artists[row] - art.layer.label = label - except IndexError: - pass - - def add_artist(self, row, artist): - """Add a new artist""" - self.beginInsertRows(QtCore.QModelIndex(), row, row) - self.artists.insert(row, artist) - self.endInsertRows() - self.rowsInserted.emit(self.index(row), row, row) - - def row_artist(self, row): - return self.artists[row] - - def artist_row(self, artist): - return self.artists.index(artist) - - -class LayerArtistView(QtWidgets.QListView, HubListener): - - """A list view into an artist model. The zorder - of each artist can be shuffled by dragging and dropping - items. Right-clicking brings up a menu to edit style or delete""" - - def __init__(self, parent=None, hub=None): - super(LayerArtistView, self).__init__(parent) - self.setDragEnabled(True) - self.setAcceptDrops(True) - self.setDragDropMode(QtWidgets.QAbstractItemView.InternalMove) - self.setIconSize(QtCore.QSize(15, 15)) - self.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection) - self.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows) - self.setContextMenuPolicy(Qt.ActionsContextMenu) - self.setEditTriggers(self.NoEditTriggers) - - self.setMinimumSize(200, 100) - self._actions = {} - self._create_actions() - - # Update when any message is emitted which would indicate a change in - # data collection content, colors, labels, etc. It's easier to simply - # listen to all events since the viewport update is fast. - self.hub = hub - self.hub.subscribe(self, LayerArtistUpdatedMessage, self._update_viewport) - self.hub.subscribe(self, LayerArtistEnabledMessage, self._layer_enabled_or_disabled) - self.hub.subscribe(self, LayerArtistDisabledMessage, self._layer_enabled_or_disabled) - self.hub.subscribe(self, LayerArtistVisibilityMessage, self._layer_enabled_or_disabled) - - def _update_viewport(self, *args): - # This forces the widget containing the list view to update/redraw, - # reflecting any changes in color/labels/content - self.viewport().update() - - def _layer_enabled_or_disabled(self, *args): - - # This forces the widget containing the list view to update/redraw, - # reflecting any changes in disabled/enabled layers. If a layer is - # disabled, it will become unselected. - self.viewport().update() - - # If a layer artist becomes unselected as a result of the update above, - # a selection change event is not emitted for some reason, so we force - # a manual update. If a layer artist was deselected, current_artist() - # will be None and the options will be hidden for that layer. - parent = self.parent() - if parent is not None: - parent.on_selection_change(self.current_artist()) - - def rowsInserted(self, index, start, end): - super(LayerArtistView, self).rowsInserted(index, start, end) - # If no rows are currently selected, make sure we select one. We do - # this to make sure the layer style editor is visible to users straight - # away. - parent = self.parent() - if parent is not None: - parent.on_artist_add(self.model().artists) - if self.current_row() is None: - self.setCurrentIndex(self.model().index(0)) - - def selectionChanged(self, selected, deselected): - super(LayerArtistView, self).selectionChanged(selected, deselected) - parent = self.parent() - if parent is not None: - parent.on_selection_change(self.current_artist()) - - def current_artist(self): - model = self.selectionModel() - if model is None: - return - rows = model.selectedRows() - if len(rows) != 1: - return - return self.model().row_artist(rows[0].row()) - - def select_artist(self, artist): - model = self.selectionModel() - row = self.model().artist_row(artist) - model.select(model.model().index(row), model.ClearAndSelect) - - def single_selection(self): - return self.current_artist() is not None - - def current_row(self): - model = self.selectionModel() - if model is None: - return - rows = model.selectedRows() - if len(rows) != 1: - return - return rows[0].row() - - def _bottom_left_of_current_index(self): - idx = self.currentIndex() - if not idx.isValid(): - return - rect = self.visualRect(idx) - pos = self.mapToGlobal(rect.bottomLeft()) - pos.setY(pos.y() + 1) - return pos - - def _edit_style(self): - pos = self._bottom_left_of_current_index() - if pos is None: - return - item = self.current_artist().layer - StyleDialog.dropdown_editor(item, pos, edit_label=False) - - def _create_actions(self): - act = QtWidgets.QAction('Edit style', self) - act.triggered.connect(nonpartial(self._edit_style)) - self.addAction(act) - - act = QtWidgets.QAction('Remove', self) - act.setShortcut(QtGui.QKeySequence(Qt.Key_Backspace)) - act.setShortcutContext(Qt.WidgetShortcut) - act.triggered.connect( - lambda *args: self.model().removeRow(self.current_row())) - self.addAction(act) - - -class LayerArtistWidget(QtWidgets.QWidget): - """ - A widget that includes a list of artists (LayerArtistView) and the visual - options for the layer artists. - """ - - def __init__(self, parent=None, layer_style_widget_cls=None, hub=None): - - super(LayerArtistWidget, self).__init__(parent=parent) - - self.hub = None - - self.layout = QtWidgets.QVBoxLayout() - self.layout.setContentsMargins(0, 0, 0, 0) - - self.layer_style_widget_cls = layer_style_widget_cls - - self.layer_list = LayerArtistView(parent=self, hub=hub) - self.layout.addWidget(self.layer_list) - - self.layer_options = QtWidgets.QWidget() - self.layer_options_layout = QtWidgets.QStackedLayout() - self.layer_options.setLayout(self.layer_options_layout) - - self.layout.addWidget(self.layer_options) - - self.setLayout(self.layout) - - self.layout_style_widgets = WeakKeyDictionary() - - self.empty = QtWidgets.QWidget() - self.layer_options_layout.addWidget(self.empty) - - self.disabled_warning = QtWidgets.QLabel() - self.disabled_warning.setWordWrap(True) - self.padded_warning = QtWidgets.QWidget() - warning_layout = QtWidgets.QVBoxLayout() - warning_layout.setContentsMargins(20, 20, 20, 20) - warning_layout.addWidget(self.disabled_warning) - self.padded_warning.setLayout(warning_layout) - - self.layer_options_layout.addWidget(self.padded_warning) - - def on_artist_add(self, layer_artists): - - if self.layer_style_widget_cls is None: - return - - for layer_artist in layer_artists: - if layer_artist not in self.layout_style_widgets: - if isinstance(self.layer_style_widget_cls, dict): - layer_artist_cls = layer_artist.__class__ - if layer_artist_cls in self.layer_style_widget_cls: - layer_style_widget_cls = self.layer_style_widget_cls[layer_artist_cls] - else: - return - else: - layer_style_widget_cls = self.layer_style_widget_cls - self.layout_style_widgets[layer_artist] = layer_style_widget_cls(layer_artist) - self.layer_options_layout.addWidget(self.layout_style_widgets[layer_artist]) - - def on_selection_change(self, layer_artist): - - if layer_artist in self.layout_style_widgets: - if layer_artist.enabled: - if layer_artist.visible: - self.disabled_warning.setText('') - self.layer_options_layout.setCurrentWidget(self.layout_style_widgets[layer_artist]) - else: - self.disabled_warning.setText('Layer is not currently visible. ' - 'Click on the checkbox for this ' - 'layer to make it visible') - self.disabled_warning.setAlignment(Qt.AlignLeft) - self.layer_options_layout.setCurrentWidget(self.padded_warning) - - else: - self.disabled_warning.setText(layer_artist.disabled_message) - self.disabled_warning.setAlignment(Qt.AlignJustify) - self.layer_options_layout.setCurrentWidget(self.padded_warning) - else: - self.layer_options_layout.setCurrentWidget(self.empty) - self.disabled_warning.setText('') - - -class QtLayerArtistContainer(LayerArtistContainer): - - """A subclass of LayerArtistContainer that dispatches to a - LayerArtistModel""" - - def __init__(self): - super(QtLayerArtistContainer, self).__init__() - self.model = LayerArtistModel(self.artists) - self.model.rowsInserted.connect(nonpartial(self._notify)) - self.model.rowsRemoved.connect(nonpartial(self._notify)) - self.model.modelReset.connect(nonpartial(self._notify)) - - def append(self, artist): - self.model.add_artist(0, artist) - artist.zorder = max(a.zorder for a in self.artists) + 1 - assert self.artists[0] is artist - self._notify() - - def remove(self, artist): - if artist in self.artists: - index = self.artists.index(artist) - self.model.removeRow(index) - assert artist not in self.artists - artist.remove() - self._notify() - - def __nonzero__(self): - return True - - __bool__ = __nonzero__ +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.core.qt.layer_artist_model is deprecated, use glue_qt.core.layer_artist_model instead', GlueDeprecationWarning) +from glue_qt.core.layer_artist_model import * # noqa diff --git a/glue/core/qt/message_widget.py b/glue/core/qt/message_widget.py index 69fe8c37e..2fe5fc3dd 100644 --- a/glue/core/qt/message_widget.py +++ b/glue/core/qt/message_widget.py @@ -1,38 +1,4 @@ -import os -from time import ctime - -from qtpy import QtWidgets -from glue import core -from glue.utils.qt import load_ui - - -class MessageWidget(QtWidgets.QWidget, core.hub.HubListener): - """ This simple class displays all messages broadcast - by a hub. It is mainly intended for debugging """ - - def __init__(self): - QtWidgets.QWidget.__init__(self) - self.ui = load_ui('message_widget.ui', self, - directory=os.path.dirname(__file__)) - self.ui.messageTable.setColumnCount(3) - labels = ['Time', 'Message', 'Sender'] - self.ui.messageTable.setHorizontalHeaderLabels(labels) - - def register_to_hub(self, hub): - # catch all messages - hub.subscribe(self, core.message.Message, - handler=self.process_message, - filter=lambda x: True) - - def process_message(self, message): - row = self.ui.messageTable.rowCount() * 0 - self.ui.messageTable.insertRow(0) - tm = QtWidgets.QTableWidgetItem(ctime().split()[3]) - typ = str(type(message)).split("'")[-2].split('.')[-1] - mtyp = QtWidgets.QTableWidgetItem(typ) - typ = str(type(message.sender)).split("'")[-2].split('.')[-1] - sender = QtWidgets.QTableWidgetItem(typ) - self.ui.messageTable.setItem(row, 0, tm) - self.ui.messageTable.setItem(row, 1, mtyp) - self.ui.messageTable.setItem(row, 2, sender) - self.ui.messageTable.resizeColumnsToContents() +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.core.qt.message_widget is deprecated, use glue_qt.core.message_widget instead', GlueDeprecationWarning) +from glue_qt.core.message_widget import * # noqa diff --git a/glue/core/qt/message_widget.ui b/glue/core/qt/message_widget.ui deleted file mode 100644 index c3c909316..000000000 --- a/glue/core/qt/message_widget.ui +++ /dev/null @@ -1,25 +0,0 @@ - - - MessageWidget - - - - 0 - 0 - 700 - 400 - - - - Message Widget - - - - - - - - - - - diff --git a/glue/core/qt/mime.py b/glue/core/qt/mime.py index d3530dde0..4909c48f4 100644 --- a/glue/core/qt/mime.py +++ b/glue/core/qt/mime.py @@ -1,11 +1,4 @@ -from qtpy import QtWidgets -from glue.utils.qt import PyMimeData, GlueItemWidget - -# some standard glue mime types -LAYER_MIME_TYPE = 'glue/layer' -LAYERS_MIME_TYPE = 'glue/layers' -INSTANCE_MIME_TYPE = PyMimeData.MIME_TYPE - - -class GlueMimeListWidget(GlueItemWidget, QtWidgets.QListWidget): - SUPPORTED_MIME_TYPE = LAYERS_MIME_TYPE +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.core.qt.mime is deprecated, use glue_qt.core.mime instead', GlueDeprecationWarning) +from glue_qt.core.mime import * # noqa diff --git a/glue/core/qt/simpleforms.py b/glue/core/qt/simpleforms.py index 021381edd..e4be6effa 100644 --- a/glue/core/qt/simpleforms.py +++ b/glue/core/qt/simpleforms.py @@ -1,75 +1,4 @@ -from qtpy import QtCore, QtWidgets -from glue.core.simpleforms import IntOption, FloatOption, BoolOption -from glue.utils import nonpartial - -_dispatch = {} - - -class FormItem(QtCore.QObject): - changed = QtCore.Signal() - - def __init__(self, instance, option): - super(FormItem, self).__init__() - self.option = option - self.instance = instance - - @property - def label(self): - return self.option.label - - -class NumberFormItem(FormItem): - widget_cls = None - - def __init__(self, instance, option): - super(NumberFormItem, self).__init__(instance, option) - - value = option.__get__(instance) - - w = self.widget_cls() - w.setRange(option.min, option.max) - w.setValue(value) - w.valueChanged.connect(nonpartial(self.changed.emit)) - self.widget = w - - @property - def value(self): - return self.widget.value() - - -class IntFormItem(NumberFormItem): - widget_cls = QtWidgets.QSpinBox - - -class FloatFormItem(NumberFormItem): - widget_cls = QtWidgets.QDoubleSpinBox - - -class BoolFormItem(FormItem): - - def __init__(self, instance, option): - super(BoolFormItem, self).__init__(instance, option) - - value = option.__get__(instance) - self.widget = QtWidgets.QCheckBox() - self.widget.setChecked(value) - self.widget.clicked.connect(nonpartial(self.changed.emit)) - - @property - def value(self): - return self.widget.isChecked() - - -def build_form_item(instance, option_name): - option = getattr(type(instance), option_name) - option_type = type(option) - return _dispatch[option_type](instance, option) - - -def register(option_cls, form_cls): - _dispatch[option_cls] = form_cls - - -register(IntOption, IntFormItem) -register(FloatOption, FloatFormItem) -register(BoolOption, BoolFormItem) +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.core.qt.simpleforms is deprecated, use glue_qt.core.simpleforms instead', GlueDeprecationWarning) +from glue_qt.core.simpleforms import * # noqa diff --git a/glue/core/qt/style_dialog.py b/glue/core/qt/style_dialog.py index 21224c333..71e6c438a 100644 --- a/glue/core/qt/style_dialog.py +++ b/glue/core/qt/style_dialog.py @@ -1,138 +1,4 @@ -from qtpy.QtCore import Qt -from qtpy import QtCore, QtWidgets -from glue.icons.qt import POINT_ICONS, symbol_icon -from glue.utils.qt import mpl_to_qt_color, qt_to_mpl_color - - -class ColorWidget(QtWidgets.QLabel): - mousePressed = QtCore.Signal() - - def mousePressEvent(self, event): - self.mousePressed.emit() - event.accept() - - -class StyleDialog(QtWidgets.QDialog): - - """Dialog which edits the style of a layer (Data or Subset) - - Use via StyleDialog.edit_style(layer) - """ - - def __init__(self, layer, parent=None, edit_label=True): - super(StyleDialog, self).__init__(parent) - self.setWindowTitle("Style Editor") - self.layer = layer - self._edit_label = edit_label - self._symbols = list(POINT_ICONS.keys()) - - self._setup_widgets() - self._connect() - - def _setup_widgets(self): - self.layout = QtWidgets.QFormLayout() - - self.size_widget = QtWidgets.QSpinBox() - self.size_widget.setMinimum(1) - self.size_widget.setMaximum(40) - self.size_widget.setValue(self.layer.style.markersize) - - self.label_widget = QtWidgets.QLineEdit() - self.label_widget.setText(self.layer.label) - self.label_widget.selectAll() - - self.color_widget = ColorWidget() - self.color_widget.setStyleSheet('ColorWidget {border: 1px solid;}') - color = self.layer.style.color - color = mpl_to_qt_color(color, alpha=self.layer.style.alpha) - self.set_color(color) - - self.okcancel = QtWidgets.QDialogButtonBox(QtWidgets.QDialogButtonBox.Ok | - QtWidgets.QDialogButtonBox.Cancel) - - if self._edit_label: - self.layout.addRow("Label", self.label_widget) - self.layout.addRow("Color", self.color_widget) - self.layout.addRow("Size", self.size_widget) - - self.layout.addWidget(self.okcancel) - - self.setLayout(self.layout) - self.layout.setContentsMargins(6, 6, 6, 6) - - def _connect(self): - self.color_widget.mousePressed.connect(self.query_color) - self.okcancel.accepted.connect(self.accept) - self.okcancel.rejected.connect(self.reject) - self.setFocusPolicy(Qt.StrongFocus) - - def query_color(self, *args): - color = QtWidgets.QColorDialog.getColor(self._color, self.color_widget, - "", - QtWidgets.QColorDialog.ShowAlphaChannel) - if color.isValid(): - self.set_color(color) - - def color(self): - return self._color - - def set_color(self, color): - self._color = color - pm = symbol_icon('o', color).pixmap(30, 30) - self.color_widget.setPixmap(pm) - - def size(self): - return self.size_widget.value() - - def label(self): - return str(self.label_widget.text()) - - def update_style(self): - if self._edit_label: - self.layer.label = self.label() - self.layer.style.color = qt_to_mpl_color(self.color()) - self.layer.style.alpha = self.color().alpha() / 255. - self.layer.style.markersize = self.size() - - @classmethod - def edit_style(cls, layer): - self = cls(layer) - result = self.exec_() - - if result == self.Accepted: - self.update_style() - - @classmethod - def dropdown_editor(cls, item, pos, **kwargs): - """ - Create a dropdown-style modal editor to edit the style of a - given item - - :param item: Item with a .label and .style to edit - :param pos: A QPoint to anchor the top-left corner of the dropdown at - :param kwargs: Extra keywords to pass to StyleDialogs's constructor - """ - self = cls(item, **kwargs) - self.setWindowFlags(Qt.Tool | Qt.FramelessWindowHint) - - pos = self.mapFromGlobal(pos) - self.move(pos) - if self.exec_() == self.Accepted: - self.update_style() - - -if __name__ == "__main__": - - from glue.utils.qt import get_qapp - from glue.core import Data - - app = get_qapp() - - d = Data(label='data label', x=[1, 2, 3, 4]) - StyleDialog.edit_style(d) - - print("New layer properties") - print(d.label) - print('color: ', d.style.color) - print('marker size: ', d.style.markersize) - print('alpha ', d.style.alpha) +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.core.qt.style_dialog is deprecated, use glue_qt.core.style_dialog instead', GlueDeprecationWarning) +from glue_qt.core.style_dialog import * # noqa diff --git a/glue/core/qt/tests/__init__.py b/glue/core/qt/tests/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/glue/core/qt/tests/test_data_collection_model.py b/glue/core/qt/tests/test_data_collection_model.py deleted file mode 100644 index 51113f0ab..000000000 --- a/glue/core/qt/tests/test_data_collection_model.py +++ /dev/null @@ -1,132 +0,0 @@ -from qtpy.QtCore import Qt -from glue.core import DataCollection, Data - -from ..data_collection_model import DataCollectionModel -from glue.core.qt.mime import LAYERS_MIME_TYPE - - -class TestDataCollectionModel(object): - - def make_model(self, n_data=1, n_subsets=0): - dc = DataCollection([Data(x=[1, 2, 3]) for _ in range(n_data)]) - for _ in range(n_subsets): - dc.new_subset_group() - return DataCollectionModel(dc) - - def test_row_count_empty_index(self): - model = self.make_model(1, 0) - assert model.rowCount() == 2 - - def test_row_count_data_row(self): - model = self.make_model(1, 0) - assert model.rowCount(model.data_index()) == 1 - - model = self.make_model(2, 0) - assert model.rowCount(model.data_index()) == 2 - - def test_row_count_subset_row(self): - model = self.make_model(1, 0) - assert model.rowCount(model.subsets_index()) == 0 - - model = self.make_model(1, 5) - assert model.rowCount(model.subsets_index()) == 5 - - def test_row_count_single_subset1(self): - model = self.make_model(2, 1) - assert model.rowCount(model.subsets_index(0)) == 2 - - def test_row_count_single_subset2(self): - model = self.make_model(2, 1) - s = model.subsets_index(0) - - idx = model.index(0, 0, s) - assert model.rowCount(idx) == 0 - - idx = model.index(1, 0, s) - assert model.rowCount(s) == 2 - - def test_invalid_indices(self): - model = self.make_model(1, 2) - - index = model.index(0, 1) - assert not index.isValid() - - index = model.index(2, 0) - assert not index.isValid() - - index = model.index(2, 0, model.index(0, 0)) - assert not index.isValid() - - def test_heading_labels(self): - model = self.make_model() - assert model.data(model.data_index(), Qt.DisplayRole) == 'Data' - assert model.data(model.subsets_index(), Qt.DisplayRole) == 'Subsets' - - def test_dc_labels(self): - model = self.make_model(1, 2) - dc = model.data_collection - dc[0].label = 'test1' - dc[0].subsets[0].label = 'subset1' - dc[0].subsets[1].label = 'subset2' - - assert model.data(model.data_index(0), Qt.DisplayRole) == 'test1' - assert model.data(model.subsets_index(0), Qt.DisplayRole) == 'subset1' - assert model.data(model.subsets_index(1), Qt.DisplayRole) == 'subset2' - assert model.data(model.index(0, 0, model.subsets_index(0)), - Qt.DisplayRole) == 'subset1 (test1)' - - def test_column_count(self): - model = self.make_model(1, 2) - - assert model.columnCount(model.data_index()) == 1 - assert model.columnCount(model.data_index(0)) == 1 - assert model.columnCount(model.subsets_index()) == 1 - assert model.columnCount(model.subsets_index(0)) == 1 - assert model.columnCount(model.subsets_index(1)) == 1 - - def test_header_data(self): - model = self.make_model() - - assert model.headerData(0, Qt.Vertical) == '' - assert model.headerData(0, Qt.Horizontal) == '' - - def test_font_role(self): - model = self.make_model(1, 2) - - assert model.data(model.data_index(), Qt.FontRole).bold() - assert model.data(model.subsets_index(), Qt.FontRole).bold() - - def test_drag_flags(self): - model = self.make_model(1, 2) - - sg = model.subsets_index(0) - subset = model.index(0, 0, sg) - assert model.flags(model.data_index(0)) & Qt.ItemIsDragEnabled - assert model.flags(subset) & Qt.ItemIsDragEnabled - - assert not model.flags(model.data_index()) & Qt.ItemIsDragEnabled - assert not model.flags(model.subsets_index()) & Qt.ItemIsDragEnabled - assert not model.flags(sg) & Qt.ItemIsDragEnabled - - def test_selectable_flags(self): - model = self.make_model(1, 2) - - assert not model.flags(model.data_index()) & Qt.ItemIsSelectable - assert not model.flags(model.subsets_index()) & Qt.ItemIsSelectable - - def test_layers_mime_type_data(self): - model = self.make_model(1, 2) - index = model.data_index(0) - - expected = [model.data_collection[0]] - assert model.mimeData([index]).data(LAYERS_MIME_TYPE) == expected - - def test_layers_mime_type_multiselection(self): - model = self.make_model(1, 2) - idxs = [model.data_index(0), - model.subsets_index(0), - model.index(0, 0, model.subsets_index(0))] - - dc = model.data_collection - expected = [dc[0], dc.subset_groups[0], dc.subset_groups[0].subsets[0]] - assert model.mimeData(idxs).data(LAYERS_MIME_TYPE) == expected diff --git a/glue/core/qt/tests/test_fitters.py b/glue/core/qt/tests/test_fitters.py deleted file mode 100644 index 461a1c110..000000000 --- a/glue/core/qt/tests/test_fitters.py +++ /dev/null @@ -1,46 +0,0 @@ -from unittest.mock import MagicMock - -from glue.core.fitters import SimpleAstropyGaussianFitter, PolynomialFitter - -from ..fitters import ConstraintsWidget, FitSettingsWidget - - -class TestConstraintsWidget(object): - - def setup_method(self, method): - self.constraints = dict(a=dict(fixed=True, value=1, limits=None)) - self.widget = ConstraintsWidget(self.constraints) - - def test_settings(self): - assert self.widget.settings('a') == dict(fixed=True, value=1, - limits=None) - - def test_update_settings(self): - self.widget._widgets['a'][2].setChecked(False) - assert self.widget.settings('a')['fixed'] is False - - def test_update_constraints(self): - self.widget._widgets['a'][2].setChecked(False) - fitter = MagicMock() - self.widget.update_constraints(fitter) - fitter.set_constraint.assert_called_once_with('a', - fixed=False, value=1, - limits=None) - - -class TestFitSettingsWidget(object): - - def test_option(self): - f = PolynomialFitter() - f.degree = 1 - w = FitSettingsWidget(f) - w.widgets['degree'].setValue(5) - w.update_fitter_from_settings() - assert f.degree == 5 - - def test_set_constraints(self): - f = SimpleAstropyGaussianFitter() - w = FitSettingsWidget(f) - w.constraints._widgets['amplitude'][2].setChecked(True) - w.update_fitter_from_settings() - assert f.constraints['amplitude']['fixed'] diff --git a/glue/core/qt/tests/test_layer_artist_model.py b/glue/core/qt/tests/test_layer_artist_model.py deleted file mode 100644 index 1bee8a90e..000000000 --- a/glue/core/qt/tests/test_layer_artist_model.py +++ /dev/null @@ -1,250 +0,0 @@ -from unittest.mock import MagicMock - -from qtpy.QtCore import Qt -from glue.core import Data, Hub -from glue.core.layer_artist import LayerArtistBase as _LayerArtist - -from ..layer_artist_model import LayerArtistModel, LayerArtistView - - -class LayerArtist(_LayerArtist): - - update_count = 0 - clear_count = 0 - redraw_count = 0 - - def update(self): - self.update_count += 1 - - def clear(self): - self.clear_count += 1 - - def redraw(self): - self.redraw_count += 1 - - -def setup_model(num): - mgrs = [LayerArtist(Data(label=str(i))) for i in range(num)] - model = LayerArtistModel(mgrs) - return model, mgrs - - -def test_row_count(): - for n in range(4): - assert setup_model(n)[0].rowCount() == n - - -def test_row_label(): - model, mgrs = setup_model(5) - for i in range(5): - assert model.row_label(i) == mgrs[i].layer.label - - -def test_add_artist_updates_row_count(): - mgrs = [LayerArtist(Data(label='A'))] - model = LayerArtistModel(mgrs) - model.add_artist(0, LayerArtist(Data(label='B'))) - assert model.rowCount() == 2 - - -def test_add_artist_updates_artist_list(): - mgrs = [LayerArtist(Data(label='A'))] - model = LayerArtistModel(mgrs) - model.add_artist(0, LayerArtist(Data(label='B'))) - assert len(mgrs) == 2 - - -def test_valid_remove(): - mgr = MagicMock(spec_set=LayerArtist) - mgrs = [mgr] - model = LayerArtistModel(mgrs) - remove = model.removeRow(0) - assert remove - assert mgr not in mgrs - - -def test_invalid_remove(): - mgr = MagicMock(spec_set=LayerArtist) - mgrs = [mgr] - model = LayerArtistModel(mgrs) - remove = model.removeRow(1) - assert not remove - assert mgr in mgrs - - -def test_artist_cleared_on_remove(): - mgr = LayerArtist(None) - mgrs = [mgr] - model = LayerArtistModel(mgrs) - model.removeRow(0) - assert mgr.clear_count == 1 - - -def test_change_label(): - model, (mgr,) = setup_model(1) - lbl = mgr.layer.label - model.change_label(0, 'new label') - assert mgr.layer.label != lbl - - -def test_change_label_invalid_row(): - model, (mgr,) = setup_model(1) - lbl = mgr.layer.label - model.change_label(1, 'new label') - assert mgr.layer.label == lbl - - -def test_flags(): - model, layer_artists = setup_model(1) - - expected = (Qt.ItemIsEditable | - Qt.ItemIsDragEnabled | - Qt.ItemIsEnabled | - Qt.ItemIsSelectable | - Qt.ItemIsUserCheckable | - Qt.ItemNeverHasChildren) - - assert model.flags(model.index(0)) == expected - - -def test_move_artist_empty(): - mgrs = [] - model = LayerArtistModel(mgrs) - model.move_artist(None, 0) - - assert mgrs == [] - - -def test_move_artist_single(): - m0 = LayerArtist(Data(label="test 0")) - mgrs = [m0] - - model = LayerArtistModel(mgrs) - model.move_artist(m0, 0) - assert mgrs == [m0] - model.move_artist(m0, -1) - assert mgrs == [m0] - model.move_artist(m0, 1) - assert mgrs == [m0] - model.move_artist(m0, 2) - assert mgrs == [m0] - - -def test_move_artist_two(): - model, mgrs = setup_model(2) - m0, m1 = mgrs - - model.move_artist(m0, 0) - assert mgrs == [m0, m1] - - model.move_artist(m0, 1) - assert mgrs == [m0, m1] - - model.move_artist(m0, 2) - assert mgrs == [m1, m0] - - model.move_artist(m0, 0) - assert mgrs == [m0, m1] - - -def test_move_artist_three(): - model, mgrs = setup_model(3) - m0, m1, m2 = mgrs - - model.move_artist(m0, 0) - assert mgrs == [m0, m1, m2] - - model.move_artist(m0, 1) - assert mgrs == [m0, m1, m2] - - model.move_artist(m0, 2) - assert mgrs == [m1, m0, m2] - model.move_artist(m0, 0) - - model.move_artist(m0, 3) - assert mgrs == [m1, m2, m0] - model.move_artist(m0, 0) - - model.move_artist(m2, 0) - assert mgrs == [m2, m0, m1] - - -def test_move_updates_zorder(): - m0 = LayerArtist(Data(label='test 0')) - m1 = LayerArtist(Data(label='test 1')) - m2 = LayerArtist(Data(label='test 2')) - m0.zorder = 10 - m1.zorder = 20 - m2.zorder = 30 - - mgrs = [m0, m1, m2] - model = LayerArtistModel(mgrs) - - model.move_artist(m2, 0) - assert m2.zorder == 30 - assert m0.zorder == 20 - assert m1.zorder == 10 - - -def test_check_syncs_to_visible(): - m0 = LayerArtist(Data(label='test 0')) - m0.artists = [MagicMock()] - mgrs = [m0] - model = LayerArtistModel(mgrs) - - m0.visible = True - assert m0.visible - assert model.data(model.index(0), Qt.CheckStateRole) == Qt.CheckState.Checked - m0.visible = False - assert not m0.visible - assert model.data(model.index(0), Qt.CheckStateRole) == Qt.CheckState.Unchecked - - model.setData(model.index(0), Qt.CheckState.Checked, Qt.CheckStateRole) - assert m0.visible - - -def test_artist_check_uncheck_works(): - """ - Because of https://bugreports.qt.io/browse/QTBUG-104688, under Qt6 - the checkbox in the UI actually sends a bare integer (0 for unchecked, - 2 for checked) so we have to check this against Qt.CheckState.Checked.value - in setData. This is a regression test for this behavior. - """ - mgrs = [LayerArtist(Data(label='A'))] - mgrs[0].artists = [MagicMock()] - - model = LayerArtistModel(mgrs) - mgrs[0].visible = True - assert model.data(model.index(0), Qt.CheckStateRole) == Qt.CheckState.Checked - - model.setData(model.index(0), 0, Qt.CheckStateRole) - assert model.data(model.index(0), Qt.CheckStateRole) == Qt.CheckState.Unchecked - - model.setData(model.index(0), 2, Qt.CheckStateRole) - assert model.data(model.index(0), Qt.CheckStateRole) == Qt.CheckState.Checked - - -def test_data(): - - model, mgrs = setup_model(3) - idx = model.index(3) - assert model.data(idx, Qt.DisplayRole) is None - idx = model.index(1) - assert model.data(idx, Qt.DisplayRole) == model.row_label(1) - assert model.data(idx, Qt.EditRole) == model.row_label(1) - - -class TestLayerArtistView(object): - - def setup_method(self, method): - self.model, self.artists = setup_model(2) - self.hub = Hub() - self.view = LayerArtistView(hub=self.hub) - self.view.setModel(self.model) - - def test_current_row(self): - for row in [0, 1]: - idx = self.model.index(row) - self.view.setCurrentIndex(idx) - self.view.current_row() == row - assert self.view.current_artist() is self.artists[row] diff --git a/glue/core/qt/tests/test_message_widget.py b/glue/core/qt/tests/test_message_widget.py deleted file mode 100644 index c49db6c6d..000000000 --- a/glue/core/qt/tests/test_message_widget.py +++ /dev/null @@ -1,19 +0,0 @@ -from glue.core.hub import Hub -from glue.core.message import Message - -from ..message_widget import MessageWidget - - -def test_message_widget_runs(): - - hub = Hub() - - widget = MessageWidget() - widget.register_to_hub(hub) - widget.show() - - message = Message('test_message_widget_runs', tag='1234') - - hub.broadcast(message) - - # TODO: check content of widget window diff --git a/glue/core/qt/tests/test_mime.py b/glue/core/qt/tests/test_mime.py deleted file mode 100644 index b9508d1bf..000000000 --- a/glue/core/qt/tests/test_mime.py +++ /dev/null @@ -1,22 +0,0 @@ -from ..mime import GlueMimeListWidget, LAYERS_MIME_TYPE - - -class TestGlueMimeListWidget(object): - - def setup_method(self, method): - self.w = GlueMimeListWidget() - - def test_mime_type(self): - assert self.w.mimeTypes() == [LAYERS_MIME_TYPE] - - def test_mime_data(self): - self.w.set_data(3, 'test data') - self.w.set_data(4, 'do not pick') - mime = self.w.mimeData([3]) - mime.data(LAYERS_MIME_TYPE) == ['test data'] - - def test_mime_data_multiselect(self): - self.w.set_data(3, 'test data') - self.w.set_data(4, 'also pick') - mime = self.w.mimeData([3, 4]) - mime.data(LAYERS_MIME_TYPE) == ['test data', 'also pick'] diff --git a/glue/core/qt/tests/test_simpleforms.py b/glue/core/qt/tests/test_simpleforms.py deleted file mode 100644 index 52292266f..000000000 --- a/glue/core/qt/tests/test_simpleforms.py +++ /dev/null @@ -1,35 +0,0 @@ -from ..simpleforms import build_form_item, FloatOption, IntOption, BoolOption - - -class Stub(object): - i = IntOption(label="int", min=0, max=3, default=2) - f = FloatOption(label="x", min=0, max=1, default=0.5) - b = BoolOption(label="y", default=True) - - -class TestBuildFormItem(object): - - def test_int(self): - s = Stub() - w = build_form_item(s, 'i') - assert w.label == "int" - assert w.widget.value() == 2 - assert w.widget.minimum() == 0 - assert w.widget.maximum() == 3 - assert w.value == 2 - - def test_float(self): - s = Stub() - w = build_form_item(s, 'f') - assert w.label == "x" - assert w.value == 0.5 - assert w.widget.minimum() == 0 - assert w.widget.maximum() == 1 - - def test_bool(self): - s = Stub() - w = build_form_item(s, 'b') - - assert w.label == 'y' - assert w.value is True - assert w.widget.isChecked() diff --git a/glue/core/qt/tests/test_style_dialog.py b/glue/core/qt/tests/test_style_dialog.py deleted file mode 100644 index 1cd53a716..000000000 --- a/glue/core/qt/tests/test_style_dialog.py +++ /dev/null @@ -1,31 +0,0 @@ -import time - -from qtpy import QtCore -from glue.core import Data -from glue.core.tests.util import simple_session - -from ..style_dialog import StyleDialog - - -class NonBlockingStyleDialog(StyleDialog): - def exec_(self, *args): - self.show() - time.sleep(0.1) - self.accept() - - -def test_style_dialog(): - - # This is in part a regression test for a bug in Python 3. It is not a - # full test of StyleDialog. - - session = simple_session() - hub = session.hub - collect = session.data_collection - - image = Data(label='im', - x=[[1, 2], [3, 4]], - y=[[2, 3], [4, 5]]) - - pos = QtCore.QPoint(10, 10) - st = NonBlockingStyleDialog.dropdown_editor(image, pos) diff --git a/glue/core/simpleforms.py b/glue/core/simpleforms.py index 6dfde8310..5b29e75de 100644 --- a/glue/core/simpleforms.py +++ b/glue/core/simpleforms.py @@ -5,8 +5,6 @@ :class:`Option` objects are defined at the class-level. To instances of these classes, an :class:`Option` behaves like a normal instance attribute. - -See :ref:`custom-fitting` for example usage. """ diff --git a/glue/core/state_path_patches.txt b/glue/core/state_path_patches.txt index 77270c998..da83f880d 100644 --- a/glue/core/state_path_patches.txt +++ b/glue/core/state_path_patches.txt @@ -30,3 +30,60 @@ glue.core.component_link.identity -> glue.core.link_helpers.identity builtins.builtin_function_or_method -> types.BuiltinFunctionType __builtin__.builtin_function_or_method -> types.BuiltinFunctionType glue.core.coordinates.Coordinates -> glue.core.coordinates.LegacyCoordinates +glue.app.qt.application.DataViewer -> glue_qt.app.application.DataViewer +glue.app.qt.application.GlueApplication -> glue_qt.app.application.GlueApplication +glue.app.qt.application.ImageViewer -> glue_qt.app.application.ImageViewer +glue.app.qt.application.ScatterViewer -> glue_qt.app.application.ScatterViewer +glue.app.qt.GlueApplication -> glue_qt.app.GlueApplication +glue.app.qt.keyboard_shortcuts.HistogramViewer -> glue_qt.app.keyboard_shortcuts.HistogramViewer +glue.app.qt.keyboard_shortcuts.ImageViewer -> glue_qt.app.keyboard_shortcuts.ImageViewer +glue.app.qt.keyboard_shortcuts.ScatterViewer -> glue_qt.app.keyboard_shortcuts.ScatterViewer +glue.app.qt.save_data.SaveDataState -> glue_qt.app.save_data.SaveDataState +glue.dialogs.link_editor.state.EditableLinkFunctionState -> glue_qt.dialogs.link_editor.state.EditableLinkFunctionState +glue.dialogs.link_editor.state.LinkEditorState -> glue_qt.dialogs.link_editor.state.LinkEditorState +glue.dialogs.subset_facet.qt.subset_facet.SubsetFacetState -> glue_qt.dialogs.subset_facet.subset_facet.SubsetFacetState +glue.plugins.dendro_viewer.qt.data_viewer.DendrogramViewer -> glue_qt.plugins.dendro_viewer.data_viewer.DendrogramViewer +glue.plugins.dendro_viewer.qt.data_viewer.MatplotlibDataViewer -> glue_qt.plugins.dendro_viewer.data_viewer.MatplotlibDataViewer +glue.plugins.dendro_viewer.qt.DendrogramViewer -> glue_qt.plugins.dendro_viewer.DendrogramViewer +glue.viewers.common.qt.data_slice_widget.SliceState -> glue_qt.viewers.common.data_slice_widget.SliceState +glue.viewers.common.qt.data_viewer.DataViewer -> glue_qt.viewers.common.data_viewer.DataViewer +glue.viewers.common.qt.data_viewer_with_state.DataViewer -> glue_qt.viewers.common.data_viewer_with_state.DataViewer +glue.viewers.common.qt.data_viewer_with_state.DataViewerWithState -> glue_qt.viewers.common.data_viewer_with_state.DataViewerWithState +glue.viewers.custom.qt.AttributeWithInfo -> glue_qt.viewers.custom.AttributeWithInfo +glue.viewers.custom.qt.CustomLayerArtist -> glue_qt.viewers.custom.CustomLayerArtist +glue.viewers.custom.qt.CustomMatplotlibDataViewer -> glue_qt.viewers.custom.CustomMatplotlibDataViewer +glue.viewers.custom.qt.CustomSubsetState -> glue_qt.viewers.custom.CustomSubsetState +glue.viewers.custom.qt.custom_viewer.AttributeWithInfo -> glue_qt.viewers.custom.custom_viewer.AttributeWithInfo +glue.viewers.custom.qt.custom_viewer.CustomLayerArtist -> glue_qt.viewers.custom.custom_viewer.CustomLayerArtist +glue.viewers.custom.qt.custom_viewer.CustomMatplotlibDataViewer -> glue_qt.viewers.custom.custom_viewer.CustomMatplotlibDataViewer +glue.viewers.custom.qt.custom_viewer.CustomMatplotlibViewerState -> glue_qt.viewers.custom.custom_viewer.CustomMatplotlibViewerState +glue.viewers.custom.qt.custom_viewer.CustomSubsetState -> glue_qt.viewers.custom.custom_viewer.CustomSubsetState +glue.viewers.custom.qt.custom_viewer.MatplotlibDataViewer -> glue_qt.viewers.custom.custom_viewer.MatplotlibDataViewer +glue.viewers.custom.qt.custom_viewer.ViewerUserState -> glue_qt.viewers.custom.custom_viewer.ViewerUserState +glue.viewers.custom.qt.ViewerUserState -> glue_qt.viewers.custom.ViewerUserState +glue.viewers.histogram.qt.data_viewer.HistogramViewer -> glue_qt.viewers.histogram.data_viewer.HistogramViewer +glue.viewers.histogram.qt.data_viewer.MatplotlibDataViewer -> glue_qt.viewers.histogram.data_viewer.MatplotlibDataViewer +glue.viewers.histogram.qt.data_viewer.QThreadedHistogramLayerArtist -> glue_qt.viewers.histogram.data_viewer.QThreadedHistogramLayerArtist +glue.viewers.histogram.qt.HistogramViewer -> glue_qt.viewers.histogram.HistogramViewer +glue.viewers.histogram.qt.layer_artist.QThreadedHistogramLayerArtist -> glue_qt.viewers.histogram.layer_artist.QThreadedHistogramLayerArtist +glue.viewers.image.qt.data_viewer.ImageViewer -> glue_qt.viewers.image.data_viewer.ImageViewer +glue.viewers.image.qt.data_viewer.MatplotlibDataViewer -> glue_qt.viewers.image.data_viewer.MatplotlibDataViewer +glue.viewers.image.qt.ImageViewer -> glue_qt.viewers.image.ImageViewer +glue.viewers.matplotlib.qt.data_viewer.DataViewer -> glue_qt.viewers.matplotlib.data_viewer.DataViewer +glue.viewers.matplotlib.qt.data_viewer.MatplotlibDataViewer -> glue_qt.viewers.matplotlib.data_viewer.MatplotlibDataViewer +glue.viewers.profile.qt.data_viewer.MatplotlibDataViewer -> glue_qt.viewers.profile.data_viewer.MatplotlibDataViewer +glue.viewers.profile.qt.data_viewer.ProfileViewer -> glue_qt.viewers.profile.data_viewer.ProfileViewer +glue.viewers.profile.qt.data_viewer.QThreadedProfileLayerArtist -> glue_qt.viewers.profile.data_viewer.QThreadedProfileLayerArtist +glue.viewers.profile.qt.layer_artist.QThreadedProfileLayerArtist -> glue_qt.viewers.profile.layer_artist.QThreadedProfileLayerArtist +glue.viewers.profile.qt.mouse_mode.NavigationModeState -> glue_qt.viewers.profile.mouse_mode.NavigationModeState +glue.viewers.profile.qt.mouse_mode.RangeModeState -> glue_qt.viewers.profile.mouse_mode.RangeModeState +glue.viewers.profile.qt.profile_tools.ImageViewer -> glue_qt.viewers.profile.profile_tools.ImageViewer +glue.viewers.profile.qt.ProfileViewer -> glue_qt.viewers.profile.ProfileViewer +glue.viewers.scatter.qt.data_viewer.MatplotlibDataViewer -> glue_qt.viewers.scatter.data_viewer.MatplotlibDataViewer +glue.viewers.scatter.qt.data_viewer.ScatterViewer -> glue_qt.viewers.scatter.data_viewer.ScatterViewer +glue.viewers.scatter.qt.ScatterViewer -> glue_qt.viewers.scatter.ScatterViewer +glue.viewers.table.qt.data_viewer.DataViewer -> glue_qt.viewers.table.data_viewer.DataViewer +glue.viewers.table.qt.data_viewer.TableLayerArtist -> glue_qt.viewers.table.data_viewer.TableLayerArtist +glue.viewers.table.qt.data_viewer.TableViewer -> glue_qt.viewers.table.data_viewer.TableViewer +glue.viewers.table.qt.TableLayerArtist -> glue_qt.viewers.table.TableLayerArtist +glue.viewers.table.qt.TableViewer -> glue_qt.viewers.table.TableViewer diff --git a/glue/core/tests/test_coordinates.py b/glue/core/tests/test_coordinates.py index 5f650cb73..8843d063d 100644 --- a/glue/core/tests/test_coordinates.py +++ b/glue/core/tests/test_coordinates.py @@ -170,31 +170,31 @@ class TestCoordinatesFromHeader(object): def test_2d_nowcs(self): hdr = {"NAXIS": 2} coord = coordinates_from_header(hdr) - assert type(coord) == IdentityCoordinates + assert type(coord) is IdentityCoordinates assert coord.pixel_n_dim == 2 assert coord.world_n_dim == 2 def test_2d(self): hdr = header_from_string(HDR_2D_VALID) coord = coordinates_from_header(hdr) - assert type(coord) == WCSCoordinates + assert type(coord) is WCSCoordinates def test_3d_nowcs(self): hdr = HDR_3D_VALID_NOWCS coord = coordinates_from_header(header_from_string(hdr)) - assert type(coord) == IdentityCoordinates + assert type(coord) is IdentityCoordinates assert coord.pixel_n_dim == 3 assert coord.world_n_dim == 3 def test_3d(self): hdr = header_from_string(HDR_3D_VALID_WCS) coord = coordinates_from_header(hdr) - assert type(coord) == WCSCoordinates + assert type(coord) is WCSCoordinates def test_nod(self): hdr = 0 coord = coordinates_from_header(hdr) - assert type(coord) == IdentityCoordinates + assert type(coord) is IdentityCoordinates HDR_2D_VALID = """ diff --git a/glue/core/tests/test_link_manager.py b/glue/core/tests/test_link_manager.py index efab59023..e187e7241 100644 --- a/glue/core/tests/test_link_manager.py +++ b/glue/core/tests/test_link_manager.py @@ -36,10 +36,10 @@ def example_components(self, add_derived=True): dummy_using = lambda x, y: (x, y) self.cs = [c1, c2, c3, c4, c5, c6, c7, c8] - self.links = [ComponentLink([c1], c3, lambda x:x), - ComponentLink([c2], c4, lambda x:x), - ComponentLink([c3], c1, lambda x:x), - ComponentLink([c4], c2, lambda x:x), + self.links = [ComponentLink([c1], c3, lambda x: x), + ComponentLink([c2], c4, lambda x: x), + ComponentLink([c3], c1, lambda x: x), + ComponentLink([c4], c2, lambda x: x), ComponentLink([c3, c4], c5, dummy_using), ComponentLink([c3, c4], c6, dummy_using)] diff --git a/glue/core/tests/test_subset.py b/glue/core/tests/test_subset.py index 45356072a..4f6bd81a7 100644 --- a/glue/core/tests/test_subset.py +++ b/glue/core/tests/test_subset.py @@ -440,7 +440,7 @@ def assert_composite_copy(self, cls): s1 = cls(state1, state2) s2 = s1.copy() - assert type(s1) == type(s2) + assert type(s1) is type(s2) assert s1.state1.copy() is s2.state1 assert s1.state2.copy() is s2.state2 diff --git a/glue/default_config.py b/glue/default_config.py index 62265d3a4..5dd688a96 100644 --- a/glue/default_config.py +++ b/glue/default_config.py @@ -10,7 +10,3 @@ # def jpeg_reader(file_name): # ... # return data - - -"""Extra qt clients""" -# qt_client(ClientClass) diff --git a/glue/dialogs/autolinker/qt/__init__.py b/glue/dialogs/autolinker/qt/__init__.py index 737e55775..7c5364e0d 100644 --- a/glue/dialogs/autolinker/qt/__init__.py +++ b/glue/dialogs/autolinker/qt/__init__.py @@ -1 +1,4 @@ -from glue.dialogs.autolinker.qt.autolinker import run_autolinker # noqa +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.dialogs.autolinker.qt is deprecated, use glue_qt.dialogs.autolinker instead', GlueDeprecationWarning) +from glue_qt.dialogs.autolinker import * # noqa diff --git a/glue/dialogs/autolinker/qt/autolinker.py b/glue/dialogs/autolinker/qt/autolinker.py index cd0a7d633..bbe79aeb2 100644 --- a/glue/dialogs/autolinker/qt/autolinker.py +++ b/glue/dialogs/autolinker/qt/autolinker.py @@ -1,94 +1,4 @@ -import os - -from qtpy import QtWidgets - -from glue.config import settings -from glue.utils.qt import load_ui -from glue.core.autolinking import find_possible_links -from glue.dialogs.link_editor.qt.link_editor import LinkEditorWidget - -__all__ = ['run_autolinker'] - -DESCRIPTION = "The auto-linking plugin '{0}' has identified {1} links between your datasets - click on 'More Details' below to find out more about the suggested links." - - -class AutoLinkPreview(QtWidgets.QDialog): - - def __init__(self, autolinker_name, data_collection, suggested_links, parent=None): - - super(AutoLinkPreview, self).__init__(parent=parent) - - self._data_collection = data_collection - - self._ui = load_ui('autolinker.ui', self, - directory=os.path.dirname(__file__)) - - self._autolinker_name = autolinker_name - - self.link_widget = LinkEditorWidget(data_collection, - suggested_links=suggested_links, - parent=self) - - self._ui.layout().insertWidget(2, self.link_widget) - - self._ui.label.setText(DESCRIPTION.format(autolinker_name, len(suggested_links))) - - self._ui.button_apply.clicked.connect(self.accept) - self._ui.button_ignore.clicked.connect(self.reject) - - self._ui.button_details.clicked.connect(self._toggle_details) - - self._set_details_visibility(False) - - def _toggle_details(self, *args): - self._set_details_visibility(not self._details_visible) - - def _set_details_visibility(self, visible): - - self._details_visible = visible - - self.link_widget.setVisible(visible) - self.label_viz.setVisible(visible) - - if visible: - self._ui.button_details.setText('Hide Details') - self.setFixedHeight(800) - else: - self._ui.button_details.setText('Show Details') - self.setFixedHeight(100) - - # Make sure the dialog is centered on the screen - try: - screen = QtWidgets.QApplication.desktop().screenGeometry(0) - self.move(screen.center() - self.rect().center()) - except AttributeError: # PySide6 - self.move(QtWidgets.QApplication.primaryScreen().geometry().center()) - - def accept(self): - # Check what we need to do here to apply links - if self._ui.checkbox_apply_future.isChecked(): - settings.AUTOLINK[self._autolinker_name] = 'always_accept' - self.link_widget.state.update_links_in_collection() - super(AutoLinkPreview, self).accept() - - def reject(self): - if self._ui.checkbox_apply_future.isChecked(): - settings.AUTOLINK[self._autolinker_name] = 'always_ignore' - super(AutoLinkPreview, self).reject() - - @classmethod - def suggest_links(cls, autolinker_name, data_collection, links, parent=None): - mode = settings.AUTOLINK.get(autolinker_name, 'always_show') - if mode == 'always_show': - widget = cls(autolinker_name, data_collection, links) - widget._ui.exec_() - elif mode == 'always_accept': - data_collection.add_link(links) - else: - pass - - -def run_autolinker(data_collection): - suggestions = find_possible_links(data_collection) - for autolinker_name, links in suggestions.items(): - AutoLinkPreview.suggest_links(autolinker_name, data_collection, links) +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.dialogs.autolinker.qt.autolinker is deprecated, use glue_qt.dialogs.autolinker.autolinker instead', GlueDeprecationWarning) +from glue_qt.dialogs.autolinker.autolinker import * # noqa diff --git a/glue/dialogs/autolinker/qt/autolinker.ui b/glue/dialogs/autolinker/qt/autolinker.ui deleted file mode 100644 index f02a8c1be..000000000 --- a/glue/dialogs/autolinker/qt/autolinker.ui +++ /dev/null @@ -1,104 +0,0 @@ - - - LinkEditor - - - - 0 - 0 - 900 - 700 - - - - Link Suggestions - - - true - - - - QLayout::SetDefaultConstraint - - - 5 - - - 5 - - - - - Description of results - - - true - - - - - - - The suggested links are shown as lines in the following visualization. Hover over the lines to see more details about the links, then decide whether or not to proceed with the link suggestions. - - - true - - - - - - - - - Show Details - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Apply to future suggestions (can be changed in Preferences) - - - - - - - Ignore - - - - - - - Apply - - - true - - - false - - - - - - - - - - diff --git a/glue/dialogs/autolinker/qt/tests/__init__.py b/glue/dialogs/autolinker/qt/tests/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/glue/dialogs/autolinker/qt/tests/test_autolinker.py b/glue/dialogs/autolinker/qt/tests/test_autolinker.py deleted file mode 100644 index 621b1c45a..000000000 --- a/glue/dialogs/autolinker/qt/tests/test_autolinker.py +++ /dev/null @@ -1,95 +0,0 @@ -import pytest - -from glue.core import Data, DataCollection -from glue.core.link_helpers import identity -from glue.dialogs.autolinker.qt.autolinker import AutoLinkPreview -from glue.core.component_link import ComponentLink - -# NOTE: the autolinker re-uses the main widget from the link editor so we don't -# need to test all the functionality - just things that are specific to the -# autolink preview. - - -class TestLinkEditor: - - def setup_method(self, method): - - self.data1 = Data(x=[1, 2, 3], y=[2, 3, 4], z=[6, 5, 4], label='data1') - self.data2 = Data(a=[2, 3, 4], b=[4, 5, 4], c=[3, 4, 1], label='data2') - self.data3 = Data(i=[5, 4, 3], j=[2, 2, 1], label='data3') - - self.data_collection = DataCollection([self.data1, self.data2, self.data3]) - - @pytest.mark.parametrize('accept', [False, True]) - def test_basic(self, accept): - - # Set up an existing link - link1 = ComponentLink([self.data1.id['x']], self.data2.id['c']) - self.data_collection.add_link(link1) - - # Set up two suggested links - - def add(x, y): - return x + y - - def double(x): - return x * 2 - - def halve(x): - return x / 2 - - link2 = ComponentLink([self.data2.id['a'], self.data2.id['b']], self.data3.id['j'], using=add) - link3 = ComponentLink([self.data3.id['i']], self.data2.id['c'], using=double, inverse=halve) - - suggested_links = [link2, link3] - - dialog = AutoLinkPreview('test autolinker', self.data_collection, suggested_links) - dialog.show() - link_widget = dialog.link_widget - - link_widget.state.data1 = self.data1 - link_widget.state.data2 = self.data2 - - assert link_widget.listsel_current_link.count() == 1 - - link_widget.state.data1 = self.data3 - - assert link_widget.listsel_current_link.count() == 2 - - if accept: - - dialog.accept() - - links = self.data_collection.external_links - - assert len(links) == 3 - - assert isinstance(links[0], ComponentLink) - assert links[0].get_from_ids()[0] is self.data1.id['x'] - assert links[0].get_to_id() is self.data2.id['c'] - assert links[0].get_using() is identity - - assert isinstance(links[1], ComponentLink) - assert links[1].get_from_ids()[0] is self.data2.id['a'] - assert links[1].get_from_ids()[1] is self.data2.id['b'] - assert links[1].get_to_id() is self.data3.id['j'] - assert links[1].get_using() is add - - assert isinstance(links[2], ComponentLink) - assert links[2].get_from_ids()[0] is self.data3.id['i'] - assert links[2].get_to_id() is self.data2.id['c'] - assert links[2].get_using() is double - assert links[2].get_inverse() is halve - - else: - - dialog.reject() - - links = self.data_collection.external_links - - assert len(links) == 1 - - assert isinstance(links[0], ComponentLink) - assert links[0].get_from_ids()[0] is self.data1.id['x'] - assert links[0].get_to_id() is self.data2.id['c'] - assert links[0].get_using() is identity diff --git a/glue/dialogs/common/qt/__init__.py b/glue/dialogs/common/qt/__init__.py index e69de29bb..1326b992f 100644 --- a/glue/dialogs/common/qt/__init__.py +++ b/glue/dialogs/common/qt/__init__.py @@ -0,0 +1,4 @@ +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.dialogs.common.qt is deprecated, use glue_qt.dialogs.common instead', GlueDeprecationWarning) +from glue_qt.dialogs.common import * # noqa diff --git a/glue/dialogs/common/qt/component_tree_widget.py b/glue/dialogs/common/qt/component_tree_widget.py index 8e14a0883..29e288f19 100644 --- a/glue/dialogs/common/qt/component_tree_widget.py +++ b/glue/dialogs/common/qt/component_tree_widget.py @@ -1,57 +1,4 @@ -from qtpy import QtWidgets, QtCore -from qtpy.QtCore import Qt - -__all__ = ['ComponentTreeWidget'] - - -class ComponentTreeWidget(QtWidgets.QTreeWidget): - - order_changed = QtCore.Signal() - - def select_cid(self, cid): - for item in self: - if item.data(0, Qt.UserRole) is cid: - self.select_item(item) - return - raise ValueError("Could not find find cid {0} in list".format(cid)) - - def select_item(self, item): - self.selection = self.selectionModel() - self.selection.select(QtCore.QItemSelection(self.indexFromItem(item, 0), - self.indexFromItem(item, self.columnCount() - 1)), - QtCore.QItemSelectionModel.ClearAndSelect) - - @property - def selected_item(self): - items = self.selectedItems() - return items[0] if len(items) == 1 else None - - @property - def selected_cid(self): - selected = self.selected_item - return None if selected is None else selected.data(0, Qt.UserRole) - - def add_cid_and_label(self, cid, columns, editable=True): - item = QtWidgets.QTreeWidgetItem(self.invisibleRootItem(), columns) - item.setData(0, Qt.UserRole, cid) - if editable: - item.setFlags(item.flags() | Qt.ItemIsEditable) - item.setFlags(item.flags() ^ Qt.ItemIsDropEnabled) - - def __iter__(self): - root = self.invisibleRootItem() - for idx in range(root.childCount()): - yield root.child(idx) - - def __len__(self): - return self.invisibleRootItem().childCount() - - def dropEvent(self, event): - selected = self.selected_item - super(ComponentTreeWidget, self).dropEvent(event) - self.select_item(selected) - self.order_changed.emit() - - def mousePressEvent(self, event): - self.clearSelection() - super(ComponentTreeWidget, self).mousePressEvent(event) +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.dialogs.common.qt.component_tree_widget is deprecated, use glue_qt.dialogs.common.component_tree_widget instead', GlueDeprecationWarning) +from glue_qt.dialogs.common.component_tree_widget import * # noqa diff --git a/glue/dialogs/common/qt/tests/__init__.py b/glue/dialogs/common/qt/tests/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/glue/dialogs/component_arithmetic/qt/__init__.py b/glue/dialogs/component_arithmetic/qt/__init__.py index 40d38165f..8a23b3bc1 100644 --- a/glue/dialogs/component_arithmetic/qt/__init__.py +++ b/glue/dialogs/component_arithmetic/qt/__init__.py @@ -1 +1,4 @@ -from .component_arithmetic import ArithmeticEditorWidget # noqa +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.dialogs.component_arithmetic.qt is deprecated, use glue_qt.dialogs.component_arithmetic instead', GlueDeprecationWarning) +from glue_qt.dialogs.component_arithmetic import * # noqa diff --git a/glue/dialogs/component_arithmetic/qt/component_arithmetic.py b/glue/dialogs/component_arithmetic/qt/component_arithmetic.py index 836e6a31a..7b9b34340 100644 --- a/glue/dialogs/component_arithmetic/qt/component_arithmetic.py +++ b/glue/dialogs/component_arithmetic/qt/component_arithmetic.py @@ -1,308 +1,4 @@ -import os -from collections import defaultdict, Counter - -from qtpy import QtWidgets, QtGui -from qtpy.QtCore import Qt - -from echo import SelectionCallbackProperty -from echo.qt import connect_combo_selection -from glue.core import ComponentID -from glue.core.parse import ParsedComponentLink, ParsedCommand -from glue.utils.qt import load_ui -from glue.core.message import NumericalDataChangedMessage - -from glue.dialogs.component_arithmetic.qt.equation_editor import EquationEditorDialog - -__all__ = ['ArithmeticEditorWidget'] - - -class ArithmeticEditorWidget(QtWidgets.QDialog): - - data = SelectionCallbackProperty() - - def __init__(self, data_collection=None, initial_data=None, parent=None): - - super(ArithmeticEditorWidget, self).__init__(parent=parent) - - self.ui = load_ui('component_arithmetic.ui', self, - directory=os.path.dirname(__file__)) - - self.list = self.ui.list_derived_components - - self.data_collection = data_collection - - self._components_derived = defaultdict(list) - self._components_other = defaultdict(list) - self._state = defaultdict(dict) - - for data in data_collection: - - # First find all derived components (only ones based on arithmetic - # expressions) - - self._components_derived[data] = [] - - for cid in data.derived_components: - comp = data.get_component(cid) - if isinstance(comp.link, ParsedComponentLink): - comp_state = {} - comp_state['cid'] = cid - comp_state['label'] = cid.label - comp_state['equation'] = comp.link._parsed - self._state[data][cid] = comp_state - self._components_derived[data].append(cid) - - # Keep track of all other components - - self._components_other[data] = [] - - for cid in data.components: - if cid not in self._components_derived[data]: - self._components_other[data].append(cid) - - # Populate data combo - ArithmeticEditorWidget.data.set_choices(self, list(self.data_collection)) - ArithmeticEditorWidget.data.set_display_func(self, lambda x: x.label) - self._connection = connect_combo_selection(self, 'data', self.ui.combosel_data) - - if initial_data is None: - self.ui.combosel_data.setCurrentIndex(0) - else: - self.data = initial_data - - self.ui.combosel_data.currentIndexChanged.connect(self._update_component_lists) - self._update_component_lists() - - self.ui.button_add_derived.clicked.connect(self._add_derived_component) - self.ui.button_edit_derived.clicked.connect(self._edit_derived_component) - self.ui.button_remove_derived.clicked.connect(self._remove_derived_component) - - self.ui.list_derived_components.itemSelectionChanged.connect(self._update_selection_derived) - - self._update_selection_derived() - - self.ui.list_derived_components.itemChanged.connect(self._update_state) - self.ui.list_derived_components.order_changed.connect(self._update_state) - self.ui.list_derived_components.itemDoubleClicked.connect(self._edit_derived_component) - - self.ui.button_ok.clicked.connect(self.accept) - self.ui.button_cancel.clicked.connect(self.reject) - - def _update_selection_derived(self): - enabled = self.list.selected_cid is not None - self.button_edit_derived.setEnabled(enabled) - self.button_remove_derived.setEnabled(enabled) - - def _update_component_lists(self, *args): - - # This gets called when the data is changed and we need to update the - # components shown in the lists. - - self.list.blockSignals(True) - - mapping = {} - for cid in self.data.components: - mapping[cid] = cid.label - - self.list.clear() - for cid in self._components_derived[self.data]: - label = self._state[self.data][cid]['label'] - if self._state[self.data][cid]['equation'] is None: - expression = '' - else: - expression = self._state[self.data][cid]['equation'].render(mapping) - self.list.add_cid_and_label(cid, [label, expression], editable=False) - - self.list.blockSignals(False) - - self._validate() - - def _validate(self): - - # Construct a list of all labels for the current dataset so that - # we can check which ones are duplicates - labels = [c.label for c in self._components_other[self.data]] - labels.extend([c['label'] for c in self._state[self.data].values()]) - if len(labels) == 0: - return - label_count = Counter(labels) - - # It's possible that the duplicates are entirely for components not - # shown in this editor, so we keep track here of whether an invalid - # component has been found. - invalid = False - - if label_count.most_common(1)[0][1] > 1: - - # If we are here, there are duplicates somewhere in the list - # of components. - - brush_red = QtGui.QBrush(Qt.red) - brush_black = QtGui.QBrush(Qt.black) - - self.list.blockSignals(True) - - for item in self.list: - label = item.text(0) - if label_count[label] > 1: - item.setForeground(0, brush_red) - invalid = True - else: - item.setForeground(0, brush_black) - - self.list.blockSignals(False) - - if invalid: - self.ui.label_status.setStyleSheet('color: red') - self.ui.label_status.setText('Error: some components have duplicate names') - self.ui.button_ok.setEnabled(False) - self.ui.combosel_data.setEnabled(False) - else: - self.ui.label_status.setStyleSheet('') - self.ui.label_status.setText('') - self.ui.button_ok.setEnabled(True) - self.ui.combosel_data.setEnabled(True) - - def _update_state(self, *args): - self._components_derived[self.data] = [] - for item in self.list: - cid = item.data(0, Qt.UserRole) - self._state[self.data][cid]['label'] = item.text(0) - self._components_derived[self.data].append(cid) - self._update_component_lists() - - def _remove_derived_component(self, *args): - cid = self.list.selected_cid - if cid is not None: - self._components_derived[self.data].remove(cid) - self._state[self.data].pop(cid) - self._update_component_lists() - - def _add_derived_component(self, *args): - - comp_state = {} - comp_state['cid'] = ComponentID('') - comp_state['label'] = '' - comp_state['equation'] = None - - self._components_derived[self.data].append(comp_state['cid']) - self._state[self.data][comp_state['cid']] = comp_state - - self._update_component_lists() - - self.list.select_cid(comp_state['cid']) - - result = self._edit_derived_component() - - if not result: # user cancelled - self._components_derived[self.data].remove(comp_state['cid']) - self._state[self.data].pop(comp_state['cid']) - self._update_component_lists() - - def _edit_derived_component(self, event=None): - - derived_item = self.list.selected_item - - if derived_item is None: - return False - - derived_cid = self.list.selected_cid - - # Note, we put the pixel/world components last as it's most likely the - # user wants to use one of the main components. - mapping = {} - references = {} - for cid in (self.data.main_components + - self.data.pixel_component_ids + - self.data.world_component_ids): - if cid is not derived_cid: - mapping[cid] = cid.label - references[cid.label] = cid - - label = self._state[self.data][derived_cid]['label'] - - if self._state[self.data][derived_cid]['equation'] is None: - equation = None - else: - equation = self._state[self.data][derived_cid]['equation'].render(mapping) - - dialog = EquationEditorDialog(label=label, equation=equation, references=references, parent=self) - dialog.setWindowFlags(self.windowFlags() | Qt.Window) - dialog.setFocus() - dialog.raise_() - dialog.exec_() - - if dialog.final_expression is None: - return False - - name, equation = dialog.get_final_label_and_parsed_command() - self._state[self.data][derived_cid]['label'] = name - self._state[self.data][derived_cid]['equation'] = equation - derived_item.setText(0, name) - - # Make sure we update the component list here since the equation may - # have changed and we need to update the preview - self._update_component_lists() - - return True - - def accept(self): - - for data in self._components_derived: - - cids_derived = self._components_derived[data] - cids_other = self._components_other[data] - cids_all = cids_other + cids_derived - cids_existing = data.components - components = dict((cid.uuid, cid) for cid in data.components) - - # First deal with renaming of components - for cid_new in cids_derived: - label = self._state[data][cid_new]['label'] - if label != cid_new.label: - cid_new.label = label - - # Second deal with the removal of components - for cid_old in cids_existing: - if not any(cid_old is cid_new for cid_new in cids_all): - data.remove_component(cid_old) - - # Third, update/add arithmetic expressions as needed - for cid_new in cids_derived: - if any(cid_new is cid_old for cid_old in cids_existing): - comp = data.get_component(cid_new) - if comp.link._parsed._cmd != self._state[data][cid_new]['equation']._cmd: - comp.link._parsed._cmd = self._state[data][cid_new]['equation']._cmd - comp.link._parsed._references = components - if data.hub: - msg = NumericalDataChangedMessage(data) - data.hub.broadcast(msg) - else: - pc = ParsedCommand(self._state[data][cid_new]['equation']._cmd, components) - link = ParsedComponentLink(cid_new, pc) - data.add_component_link(link) - - # Findally, reorder components as needed - data.reorder_components(cids_all) - - super(ArithmeticEditorWidget, self).accept() - - -if __name__ == "__main__": # pragma: nocover - - from glue.utils.qt import get_qapp - app = get_qapp() - - import numpy as np - - from glue.core.data import Data - from glue.core.data_collection import DataCollection - - x = np.random.random((5, 5)) - y = x * 3 - dc = DataCollection() - dc.append(Data(label='test1', x=x, y=y)) - dc.append(Data(label='test2', a=x, b=y)) - - widget = ArithmeticEditorWidget(dc) - widget.exec_() +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.dialogs.component_arithmetic.qt.component_arithmetic is deprecated, use glue_qt.dialogs.component_arithmetic.component_arithmetic instead', GlueDeprecationWarning) +from glue_qt.dialogs.component_arithmetic.component_arithmetic import * # noqa diff --git a/glue/dialogs/component_arithmetic/qt/component_arithmetic.ui b/glue/dialogs/component_arithmetic/qt/component_arithmetic.ui deleted file mode 100644 index e7d74c37e..000000000 --- a/glue/dialogs/component_arithmetic/qt/component_arithmetic.ui +++ /dev/null @@ -1,262 +0,0 @@ - - - Form - - - - 0 - 0 - 478 - 371 - - - - Arithmetic editor - - - - 5 - - - 10 - - - 5 - - - 10 - - - 5 - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 50 - false - - - - Dataset: - - - - - - - - 200 - 0 - - - - QComboBox::AdjustToMinimumContentsLengthWithIcon - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - Qt::Vertical - - - QSizePolicy::Fixed - - - - 10 - 5 - - - - - - - - - 50 - false - - - - In this dialog you can define data attributes based on other attributes using arithmetic operations. First, select the dataset above, then you will be able to create/edit arithmetic attributes for that dataset below. - - - Qt::AlignJustify|Qt::AlignVCenter - - - true - - - - - - - Qt::Vertical - - - QSizePolicy::Fixed - - - - 10 - 5 - - - - - - - - - - New arithmetic attribute - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Edit selected attribute - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Remove selected - - - - - - - - - QAbstractItemView::InternalMove - - - - Name - - - - - Expression - - - - - - - - - - - - - Qt::AlignCenter - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Cancel - - - - - - - OK - - - true - - - - - - - - - - ComponentTreeWidget - QTreeWidget -
glue.dialogs.common.qt.component_tree_widget
-
-
- - -
diff --git a/glue/dialogs/component_arithmetic/qt/equation_editor.py b/glue/dialogs/component_arithmetic/qt/equation_editor.py index 9767663c7..7138cd6ef 100644 --- a/glue/dialogs/component_arithmetic/qt/equation_editor.py +++ b/glue/dialogs/component_arithmetic/qt/equation_editor.py @@ -1,228 +1,4 @@ -import os -from collections import deque, OrderedDict - -from qtpy import QtWidgets, QtCore -from qtpy.QtCore import Qt - -from echo import SelectionCallbackProperty -from echo.qt import connect_combo_selection -from glue.core.parse import InvalidTagError, ParsedCommand, TAG_RE -from glue.utils.qt import load_ui, CompletionTextEdit - -__all__ = ['EquationEditorDialog'] - - -class ColorizedCompletionTextEdit(CompletionTextEdit): - - updated = QtCore.Signal() - - def __init__(self, *args, **kwargs): - super(ColorizedCompletionTextEdit, self).__init__(*args, **kwargs) - self.setAlignment(Qt.AlignLeft) - self.setUndoRedoEnabled(False) - self._undo_stack = deque(maxlen=100) - self._redo_stack = deque(maxlen=100) - - def insertPlainText(self, *args): - super(ColorizedCompletionTextEdit, self).insertPlainText(*args) - self.reformat_text() - self.updated.emit() - self.setAlignment(Qt.AlignLeft) - - def keyReleaseEvent(self, event): - super(ColorizedCompletionTextEdit, self).keyReleaseEvent(event) - self.reformat_text() - self.updated.emit() - - def keyPressEvent(self, event): - super(ColorizedCompletionTextEdit, self).keyPressEvent(event) - # NOTE: We use == here instead of & for the modifiers because we don't - # want to catch e.g. control-shift-z or other combinations. - if event.modifiers() == Qt.ControlModifier and event.key() == Qt.Key_Z: - if len(self._undo_stack) > 1: - self._undo_stack.pop() - self.setHtml(self._undo_stack[-1]) - text = self.toPlainText() - tc = self.textCursor() - tc.setPosition(len(text)) - self.setTextCursor(tc) - self._cache = self._undo_stack[-1] - self.updated.emit() - - def reformat_text(self): - - # If the text hasn't changed, no need to reformat - if self.toPlainText() == getattr(self, '_cache', None): - return - - # Here every time a key is released, we re-colorize the expression. - # We show valid components in blue, and invalid ones in red. We - # recognized components because they contain a ":" which is not valid - # Python syntax (except if one considers lambda functions, but we can - # probably ignore that here) - text = self.toPlainText() - - # We need to be careful with < and > otherwise they get erased - text = text.replace('<', '<').replace('>', '>') - - def format_components(m): - component = m.group(0) - if component in self.word_list: - return "" + component + "" - else: - return "" + component + "" - - html = TAG_RE.sub(format_components, text) - - tc = self.textCursor() - pos = tc.position() - - self._undo_stack.append(html) - self.setHtml(html) - - # Sometimes the HTML gets rid of double spaces so we have to make - # sure the position isn't greater than the text length. - text = self.toPlainText() - pos = min(pos, len(text)) - - tc.setPosition(pos) - self.setTextCursor(tc) - - self._cache = self.toPlainText() - - -class EquationEditorDialog(QtWidgets.QDialog): - - tip_text = ("Note: Attribute names in the expression should be surrounded " - "by {{ }} brackets (e.g. {{{example}}}), and you can use " - "Numpy functions using np.<function>, as well as any " - "other function defined in your config.py file.

" - "Example expressions:

" - " - Subtract 10 from '{example}': {{{example}}} - 10
" - " - Scale '{example}' to [0:1]: ({{{example}}} - np.min({{{example}}})) / np.ptp({{{example}}})
" - " - Multiply '{example}' by pi: {{{example}}} * np.pi
" - " - Use masking: {{{example}}} * ({{{example}}} < 1)
") - - placeholder_text = ("Type any mathematical expression here - " - "you can include attribute names from the " - "drop-down below by selecting them and " - "clicking 'Insert'. See below for examples " - "of valid expressions") - - attribute = SelectionCallbackProperty() - - def __init__(self, label=None, data=None, equation=None, references=None, parent=None): - - super(EquationEditorDialog, self).__init__(parent=parent) - - self.ui = load_ui('equation_editor.ui', self, - directory=os.path.dirname(__file__)) - - # Get mapping from label to component ID - if references is not None: - self.references = references - elif data is not None: - self.references = OrderedDict() - for cid in data.coordinate_components + data.main_components: - self.references[cid.label] = cid - - example = sorted(self.references, key=len)[0] - - self.ui.text_label.setPlaceholderText("New attribute name") - self.ui.expression.setPlaceholderText(self.placeholder_text.format(example=example)) - - self.ui.label.setText(self.tip_text.format(example=example)) - - if label is not None: - self.ui.text_label.setText(label) - - self.ui.text_label.textChanged.connect(self._update_status) - - # Populate component combo - EquationEditorDialog.attribute.set_choices(self, list(self.references)) - self._connection = connect_combo_selection(self, 'attribute', self.ui.combosel_component) - - # Set up labels for auto-completion - labels = ['{' + l + '}' for l in self.references] - self.ui.expression.set_word_list(labels) - - if equation is not None: - self.ui.expression.insertPlainText(equation) - - self.ui.button_ok.clicked.connect(self.accept) - self.ui.button_cancel.clicked.connect(self.reject) - - self.ui.button_insert.clicked.connect(self._insert_component) - - self.ui.expression.updated.connect(self._update_status) - self._update_status() - - def _insert_component(self): - label = self.attribute - self.expression.insertPlainText('{' + label + '}') - - def _update_status(self): - - # If the text hasn't changed, no need to check again - if hasattr(self, '_cache') and self._cache == (self.ui.text_label.text(), self._get_raw_command()): - return - - if self.ui.text_label.text() == "": - self.ui.label_status.setStyleSheet('color: red') - self.ui.label_status.setText("Attribute name not set") - self.ui.button_ok.setEnabled(False) - elif self._get_raw_command() == "": - self.ui.label_status.setText("") - self.ui.button_ok.setEnabled(False) - else: - try: - pc = self._get_parsed_command() - pc.evaluate_test() - except SyntaxError: - self.ui.label_status.setStyleSheet('color: red') - self.ui.label_status.setText("Incomplete or invalid syntax") - self.ui.button_ok.setEnabled(False) - except InvalidTagError as exc: - self.ui.label_status.setStyleSheet('color: red') - self.ui.label_status.setText("Invalid component: {0}".format(exc.tag)) - self.ui.button_ok.setEnabled(False) - except Exception as exc: - self.ui.label_status.setStyleSheet('color: red') - self.ui.label_status.setText(str(exc)) - self.ui.button_ok.setEnabled(False) - else: - self.ui.label_status.setStyleSheet('color: green') - self.ui.label_status.setText("Valid expression") - self.ui.button_ok.setEnabled(True) - - self._cache = self.ui.text_label.text(), self._get_raw_command() - - def _get_raw_command(self): - return str(self.ui.expression.toPlainText()) - - def _get_parsed_command(self): - expression = self._get_raw_command() - return ParsedCommand(expression, self.references) - - def get_final_label_and_parsed_command(self): - return self.ui.text_label.text(), self._get_parsed_command() - - def accept(self): - self.final_expression = self._get_parsed_command()._cmd - super(EquationEditorDialog, self).accept() - - def reject(self): - self.final_expression = None - super(EquationEditorDialog, self).reject() - - -if __name__ == "__main__": # pragma: nocover - - from glue.utils.qt import get_qapp - - app = get_qapp() - - from glue.core.data import Data - d = Data(label='test1', x=[1, 2, 3], y=[2, 3, 4], z=[3, 4, 5]) - widget = EquationEditorDialog(data=d, equation='') - widget.exec_() +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.dialogs.component_arithmetic.qt.equation_editor is deprecated, use glue_qt.dialogs.component_arithmetic.equation_editor instead', GlueDeprecationWarning) +from glue_qt.dialogs.component_arithmetic.equation_editor import * # noqa diff --git a/glue/dialogs/component_arithmetic/qt/equation_editor.ui b/glue/dialogs/component_arithmetic/qt/equation_editor.ui deleted file mode 100644 index 32e1f54c4..000000000 --- a/glue/dialogs/component_arithmetic/qt/equation_editor.ui +++ /dev/null @@ -1,206 +0,0 @@ - - - Dialog - - - - 0 - 0 - 477 - 400 - - - - Equation Editor - - - - 5 - - - 10 - - - 10 - - - 10 - - - 5 - - - - - 5 - - - - - - - - = - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - 0 - 0 - - - - - 16777215 - 16777215 - - - - - - - - - - - 0 - 0 - - - - - 75 - true - - - - Available attributes: - - - - - - - - 0 - 0 - - - - QComboBox::AdjustToMinimumContentsLengthWithIcon - - - - - - - - 100 - 16777215 - - - - Insert - - - - - - - - - - 0 - 0 - - - - - false - - - - - - - Qt::AlignJustify|Qt::AlignVCenter - - - true - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - - - - - - - status - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Cancel - - - - - - - OK - - - true - - - - - - - - - - ColorizedCompletionTextEdit - QTextEdit -
glue.dialogs.component_arithmetic.qt.equation_editor
-
-
- - -
diff --git a/glue/dialogs/component_arithmetic/qt/tests/__init__.py b/glue/dialogs/component_arithmetic/qt/tests/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/glue/dialogs/component_arithmetic/qt/tests/test_component_arithmetic.py b/glue/dialogs/component_arithmetic/qt/tests/test_component_arithmetic.py deleted file mode 100644 index 01ddc7663..000000000 --- a/glue/dialogs/component_arithmetic/qt/tests/test_component_arithmetic.py +++ /dev/null @@ -1,172 +0,0 @@ -from unittest.mock import patch - -from numpy.testing import assert_equal - -from glue.core import Data, DataCollection, HubListener, ComponentID -from glue.core import message as msg -from glue.core.component_link import ComponentLink -from glue.core.parse import ParsedCommand, ParsedComponentLink - -from ..component_arithmetic import ArithmeticEditorWidget, EquationEditorDialog - - -def auto_accept(equation): - def exec_replacement(self): - self.ui.expression.clear() - self.ui.expression.insertPlainText(equation) - self.accept() - return exec_replacement - - -def auto_reject(): - def exec_replacement(self): - self.ui.expression.clear() - self.reject() - return exec_replacement - - -class ChangeListener(HubListener): - - def __init__(self, data, *args, **kwargs): - - super(ChangeListener, self).__init__(*args, **kwargs) - - self.data = data - self.removed = [] - self.added = [] - self.renamed = [] - self.reordered = [] - self.numeric = False - - self.register_to_hub(data.hub) - - def _has_data(self, message): - return message.sender is self.data - - def register_to_hub(self, hub): - - hub.subscribe(self, msg.DataAddComponentMessage, - handler=self._component_added, - filter=self._has_data) - - hub.subscribe(self, msg.DataRemoveComponentMessage, - handler=self._component_removed, - filter=self._has_data) - - hub.subscribe(self, msg.DataRenameComponentMessage, - handler=self._component_renamed, - filter=self._has_data) - - hub.subscribe(self, msg.DataReorderComponentMessage, - handler=self._components_reordered, - filter=self._has_data) - - hub.subscribe(self, msg.NumericalDataChangedMessage, - handler=self._numerical_changed, - filter=self._has_data) - - def _component_added(self, message): - self.added.append(message.component_id) - - def _component_removed(self, message): - self.removed.append(message.component_id) - - def _component_renamed(self, message): - self.renamed.append(message.component_id) - - def _components_reordered(self, message): - self.reordered = message.component_ids - - def _numerical_changed(self, message): - self.numeric = True - - def assert_exact_changes(self, added=[], removed=[], renamed=[], reordered=[], numerical=False): - assert set(added) == set(self.added) - assert set(removed) == set(self.removed) - assert set(renamed) == set(self.renamed) - assert reordered == self.reordered - assert numerical is self.numeric - - -class TestArithmeticEditorWidget: - - def setup_method(self): - - self.data1 = Data(x=[1, 2, 3], y=[3.5, 4.5, -1.0], z=['a', 'r', 'w']) - self.data2 = Data(a=[3, 4, 1], b=[1.5, -2.0, 3.5], c=['y', 'e', 'r']) - - # Add a derived component so that we can test how we deal with existing ones - components = dict((cid.label, cid) for cid in self.data2.components) - pc = ParsedCommand('{a}', components) - link = ParsedComponentLink(ComponentID('d'), pc) - self.data2.add_component_link(link) - - self.data_collection = DataCollection([self.data1, self.data2]) - - link = ComponentLink([self.data1.id['x']], self.data2.id['a']) - self.data_collection.add_link(link) - - self.listener1 = ChangeListener(self.data1) - self.listener2 = ChangeListener(self.data2) - - def test_nochanges(self): - editor = ArithmeticEditorWidget(self.data_collection) - editor.show() - editor.button_ok.click() - self.listener1.assert_exact_changes() - self.listener2.assert_exact_changes() - editor.close() - - def test_add_derived_and_rename(self): - editor = ArithmeticEditorWidget(self.data_collection) - editor.show() - with patch.object(EquationEditorDialog, 'exec_', auto_accept('{x} + {y}')): - editor.button_add_derived.click() - item = list(editor.list)[0] - item.setText(0, 'new') - editor.button_ok.click() - self.listener1.assert_exact_changes(added=[self.data1.id['new']]) - self.listener2.assert_exact_changes() - assert_equal(self.data1['new'], [4.5, 6.5, 2.0]) - editor.close() - - def test_add_derived_and_cancel(self): - editor = ArithmeticEditorWidget(self.data_collection) - editor.show() - with patch.object(EquationEditorDialog, 'exec_', auto_reject()): - editor.button_add_derived.click() - assert len(editor.list) == 0 - editor.close() - - def test_edit_existing_equation(self): - assert_equal(self.data2['d'], [3, 4, 1]) - editor = ArithmeticEditorWidget(self.data_collection) - editor.show() - assert len(editor.list) == 0 - editor.combosel_data.setCurrentIndex(1) - assert len(editor.list) == 1 - editor.list.select_cid(self.data2.id['d']) - with patch.object(EquationEditorDialog, 'exec_', auto_accept('{a} + {b}')): - editor.button_edit_derived.click() - editor.button_ok.click() - self.listener1.assert_exact_changes() - self.listener2.assert_exact_changes(numerical=True) - assert_equal(self.data2['d'], [4.5, 2.0, 4.5]) - editor.close() - - # TODO: add an updated version of the following test back once we add - # support for using derived components in derived components. - # - # def test_edit_equation_after_rename(self): - # editor = ArithmeticEditorWidget(self.data_collection) - # editor.show() - # editor.combosel_data.setCurrentIndex(1) - # editor.list['main'].select_cid(self.data2.id['a']) - # editor.list['main'].selected_item.setText(0, 'renamed') - # editor.list.select_cid(self.data2.id['d']) - # with patch.object(EquationEditorDialog, 'exec_', auto_accept('{renamed} + 1')): - # editor.button_edit_derived.click() - # editor.button_ok.click() - # self.listener1.assert_exact_changes() - # self.listener2.assert_exact_changes(renamed=[self.data2.id['renamed']], numerical=True) - # assert_equal(self.data2['d'], [4, 5, 2]) diff --git a/glue/dialogs/component_arithmetic/qt/tests/test_equation_editor.py b/glue/dialogs/component_arithmetic/qt/tests/test_equation_editor.py deleted file mode 100644 index 2da0116af..000000000 --- a/glue/dialogs/component_arithmetic/qt/tests/test_equation_editor.py +++ /dev/null @@ -1,82 +0,0 @@ -import pytest - -from qtpy import QtTest -from qtpy.QtCore import Qt -from glue.core import Data - -from ..equation_editor import EquationEditorDialog - - -class TestEquationEditor: - - def setup_method(self, method): - self.data = Data(x=[1, 2, 3], y=[3, 4, 5]) - self.dialog = EquationEditorDialog(label='z', data=self.data, equation='') - - def test_empty(self): - assert not self.dialog.ui.button_ok.isEnabled() - assert self.dialog.ui.label_status.text() == '' - - @pytest.mark.parametrize('expression', ['1', '1 + {x}', '1 * np.sin({y}) + {x}']) - def test_valid_cases(self, expression): - self.dialog.expression.insertPlainText(expression) - assert self.dialog.ui.button_ok.isEnabled() - assert self.dialog.ui.label_status.text() == 'Valid expression' - self.dialog.ui.button_ok.click() - assert self.dialog._get_raw_command() == expression - - def test_invalid_syntax(self): - self.dialog.expression.insertPlainText('1 + {x') - assert not self.dialog.ui.button_ok.isEnabled() - assert self.dialog.ui.label_status.text() == 'Incomplete or invalid syntax' - - def test_unknown_component(self): - self.dialog.expression.insertPlainText('1 + {z}') - assert not self.dialog.ui.button_ok.isEnabled() - assert self.dialog.ui.label_status.text() == 'Invalid component: z' - - def test_undefined_name(self): - self.dialog.expression.insertPlainText('1 + {x} + abc') - assert not self.dialog.ui.button_ok.isEnabled() - assert self.dialog.ui.label_status.text() == "name 'abc' is not defined" - - def test_insert_component(self): - self.dialog.expression.insertPlainText('1 + ') - self.dialog.button_insert.click() - assert self.dialog.ui.label_status.text() == 'Valid expression' - self.dialog.ui.button_ok.click() - assert self.dialog._get_raw_command() == '1 + {Pixel Axis 0 [x]}' - - def test_nolabel(self): - self.dialog.ui.text_label.setText('') - self.dialog.expression.insertPlainText('1 + {x}') - assert not self.dialog.ui.button_ok.isEnabled() - assert self.dialog.ui.label_status.text() == 'Attribute name not set' - - def test_typing(self): - - # This ensures that the code that highlights syntax gets called, - # and also ensures we can test undoing. - - chars = (Qt.Key_1, Qt.Key_Space, Qt.Key_Plus, Qt.Key_Space, - Qt.Key_BraceLeft, Qt.Key_X, Qt.Key_BraceRight) - - for char in chars: - QtTest.QTest.keyClick(self.dialog.expression, char) - - assert self.dialog.expression.toPlainText() == '1 + {x}' - - QtTest.QTest.keyClick(self.dialog.expression, Qt.Key_Z, Qt.ControlModifier) - - assert self.dialog.expression.toPlainText() == '1 + {x' - - for i in range(4): - QtTest.QTest.keyClick(self.dialog.expression, Qt.Key_Z, Qt.ControlModifier) - - assert self.dialog.expression.toPlainText() == '1 ' - - def test_cancel(self): - self.dialog.expression.insertPlainText('1 + {x}') - assert self.dialog.ui.label_status.text() == 'Valid expression' - self.dialog.ui.button_cancel.click() - assert self.dialog.final_expression is None diff --git a/glue/dialogs/component_manager/qt/__init__.py b/glue/dialogs/component_manager/qt/__init__.py index 31d0ae699..50c6717e4 100644 --- a/glue/dialogs/component_manager/qt/__init__.py +++ b/glue/dialogs/component_manager/qt/__init__.py @@ -1 +1,4 @@ -from .component_manager import ComponentManagerWidget # noqa +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.dialogs.component_manager.qt is deprecated, use glue_qt.dialogs.component_manager instead', GlueDeprecationWarning) +from glue_qt.dialogs.component_manager import * # noqa diff --git a/glue/dialogs/component_manager/qt/component_manager.py b/glue/dialogs/component_manager/qt/component_manager.py index 145fe2f39..68e398a90 100644 --- a/glue/dialogs/component_manager/qt/component_manager.py +++ b/glue/dialogs/component_manager/qt/component_manager.py @@ -1,200 +1,4 @@ -import os -from collections import defaultdict, Counter - -from qtpy import QtWidgets, QtGui -from qtpy.QtCore import Qt - -from echo import SelectionCallbackProperty -from echo.qt import connect_combo_selection -from glue.utils.qt import load_ui - -__all__ = ['ComponentManagerWidget'] - - -class ComponentManagerWidget(QtWidgets.QDialog): - - data = SelectionCallbackProperty() - - def __init__(self, data_collection=None, initial_data=None, parent=None): - - super(ComponentManagerWidget, self).__init__(parent=parent) - - self.ui = load_ui('component_manager.ui', self, - directory=os.path.dirname(__file__)) - - self.list = {} - self.list = self.ui.list_main_components - - self.data_collection = data_collection - - self._components_main = defaultdict(list) - self._components_other = defaultdict(list) - self._state = defaultdict(dict) - - for data in data_collection: - - for cid in data.main_components: - comp_state = {} - comp_state['cid'] = cid - comp_state['label'] = cid.label - self._state[data][cid] = comp_state - self._components_main[data].append(cid) - - # Keep track of all other components - - self._components_other[data] = [] - - for cid in data.components: - if cid not in self._components_main[data]: - self._components_other[data].append(cid) - - # Populate data combo - ComponentManagerWidget.data.set_choices(self, list(self.data_collection)) - ComponentManagerWidget.data.set_display_func(self, lambda x: x.label) - connect_combo_selection(self, 'data', self.ui.combosel_data) - - if initial_data is None: - self.ui.combosel_data.setCurrentIndex(0) - else: - self.data = initial_data - - self.ui.combosel_data.currentIndexChanged.connect(self._update_component_lists) - self._update_component_lists() - - self.ui.button_remove_main.clicked.connect(self._remove_main_component) - - self.ui.list_main_components.itemSelectionChanged.connect(self._update_selection_main) - - self._update_selection_main() - - self.ui.list_main_components.itemChanged.connect(self._update_state) - self.ui.list_main_components.order_changed.connect(self._update_state) - - self.ui.button_ok.clicked.connect(self.accept) - self.ui.button_cancel.clicked.connect(self.reject) - - def _update_selection_main(self): - enabled = self.list.selected_cid is not None - self.button_remove_main.setEnabled(enabled) - - def _update_component_lists(self, *args): - - # This gets called when the data is changed and we need to update the - # components shown in the lists. - - self.list.blockSignals(True) - - self.list.clear() - for cid in self._components_main[self.data]: - self.list.add_cid_and_label(cid, [self._state[self.data][cid]['label']]) - - self.list.blockSignals(False) - - self._validate() - - def _validate(self): - - # Construct a list of all labels for the current dataset so that - # we can check which ones are duplicates - labels = [c.label for c in self._components_other[self.data]] - labels.extend([c['label'] for c in self._state[self.data].values()]) - if len(labels) == 0: - return - label_count = Counter(labels) - - # It's possible that the duplicates are entirely for components not - # shown in this editor, so we keep track here of whether an invalid - # component has been found. - invalid = False - - if label_count.most_common(1)[0][1] > 1: - - # If we are here, there are duplicates somewhere in the list - # of components. - - brush_red = QtGui.QBrush(Qt.red) - brush_black = QtGui.QBrush(Qt.black) - - self.list.blockSignals(True) - - for item in self.list: - label = item.text(0) - if label_count[label] > 1: - item.setForeground(0, brush_red) - invalid = True - else: - item.setForeground(0, brush_black) - - self.list.blockSignals(False) - - if invalid: - self.ui.label_status.setStyleSheet('color: red') - self.ui.label_status.setText('Error: some components have duplicate names') - self.ui.button_ok.setEnabled(False) - self.ui.combosel_data.setEnabled(False) - else: - self.ui.label_status.setStyleSheet('') - self.ui.label_status.setText('') - self.ui.button_ok.setEnabled(True) - self.ui.combosel_data.setEnabled(True) - - def _update_state(self, *args): - - self._components_main[self.data] = [] - for item in self.list: - cid = item.data(0, Qt.UserRole) - self._state[self.data][cid]['label'] = item.text(0) - self._components_main[self.data].append(cid) - - self._update_component_lists() - - def _remove_main_component(self, *args): - cid = self.list.selected_cid - if cid is not None: - self._components_main[self.data].remove(cid) - self._state[self.data].pop(cid) - self._update_component_lists() - - def accept(self): - - for data in self._components_main: - - cids_main = self._components_main[data] - cids_existing = data.components - cids_all = data.pixel_component_ids + data.world_component_ids + cids_main + data.derived_components - - # First deal with renaming of components - for cid_new in cids_main: - label = self._state[data][cid_new]['label'] - if label != cid_new.label: - cid_new.label = label - - # Second deal with the removal of components - for cid_old in cids_existing: - if not any(cid_old is cid_new for cid_new in cids_all): - data.remove_component(cid_old) - - # Findally, reorder components as needed - data.reorder_components(cids_all) - - super(ComponentManagerWidget, self).accept() - - -if __name__ == "__main__": # pragma: nocover - - from glue.utils.qt import get_qapp - app = get_qapp() - - import numpy as np - - from glue.core.data import Data - from glue.core.data_collection import DataCollection - - x = np.random.random((5, 5)) - y = x * 3 - dc = DataCollection() - dc.append(Data(label='test1', x=x, y=y)) - dc.append(Data(label='test2', a=x, b=y)) - - widget = ComponentManagerWidget(dc) - widget.exec_() +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.dialogs.component_manager.qt.component_manager is deprecated, use glue_qt.dialogs.component_manager.component_manager instead', GlueDeprecationWarning) +from glue_qt.dialogs.component_manager.component_manager import * # noqa diff --git a/glue/dialogs/component_manager/qt/component_manager.ui b/glue/dialogs/component_manager/qt/component_manager.ui deleted file mode 100644 index 3be5eeb41..000000000 --- a/glue/dialogs/component_manager/qt/component_manager.ui +++ /dev/null @@ -1,205 +0,0 @@ - - - Form - - - - 0 - 0 - 534 - 306 - - - - Organize data attributes - - - - 5 - - - 10 - - - 5 - - - 10 - - - 5 - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 50 - false - - - - Dataset: - - - - - - - - 200 - 0 - - - - QComboBox::AdjustToMinimumContentsLengthWithIcon - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - Qt::Vertical - - - QSizePolicy::Fixed - - - - 10 - 5 - - - - - - - - QAbstractItemView::InternalMove - - - - Name - - - - - - - - - - Tip: Drag and drop to re-order, and double-click to rename - - - Qt::AlignCenter - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Remove selected - - - - - - - - - - - - - - Qt::AlignCenter - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Cancel - - - - - - - OK - - - true - - - - - - - - - - ComponentTreeWidget - QTreeWidget -
glue.dialogs.common.qt.component_tree_widget
-
-
- - -
diff --git a/glue/dialogs/component_manager/qt/tests/__init__.py b/glue/dialogs/component_manager/qt/tests/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/glue/dialogs/component_manager/qt/tests/test_component_manager.py b/glue/dialogs/component_manager/qt/tests/test_component_manager.py deleted file mode 100644 index 9cccff89d..000000000 --- a/glue/dialogs/component_manager/qt/tests/test_component_manager.py +++ /dev/null @@ -1,79 +0,0 @@ -from numpy.testing import assert_equal - -from glue.core import Data, DataCollection, ComponentID -from glue.core.component_link import ComponentLink -from glue.core.parse import ParsedCommand, ParsedComponentLink - -from ..component_manager import ComponentManagerWidget -from glue.dialogs.component_arithmetic.qt.tests.test_component_arithmetic import ChangeListener - - -class TestComponentManagerWidget: - - def setup_method(self): - - self.data1 = Data(x=[1, 2, 3], y=[3.5, 4.5, -1.0], z=['a', 'r', 'w']) - self.data2 = Data(a=[3, 4, 1], b=[1.5, -2.0, 3.5], c=['y', 'e', 'r']) - - # Add a derived component so that we can test how we deal with existing ones - components = dict((cid.label, cid) for cid in self.data2.components) - pc = ParsedCommand('{a}', components) - link = ParsedComponentLink(ComponentID('d'), pc) - self.data2.add_component_link(link) - - self.data_collection = DataCollection([self.data1, self.data2]) - - link = ComponentLink([self.data1.id['x']], self.data2.id['a']) - self.data_collection.add_link(link) - - self.listener1 = ChangeListener(self.data1) - self.listener2 = ChangeListener(self.data2) - - def test_nochanges(self): - self.manager = ComponentManagerWidget(self.data_collection) - self.manager.show() - self.manager.button_ok.click() - self.listener1.assert_exact_changes() - self.listener2.assert_exact_changes() - - def test_remove(self): - x_cid = self.data1.id['x'] - self.manager = ComponentManagerWidget(self.data_collection) - self.manager.show() - item = list(self.manager.list)[0] - self.manager.list.select_item(item) - self.manager.button_remove_main.click() - self.manager.button_ok.click() - self.listener1.assert_exact_changes(removed=[x_cid]) - self.listener2.assert_exact_changes() - - def test_rename_valid(self): - x_cid = self.data1.id['x'] - self.manager = ComponentManagerWidget(self.data_collection) - self.manager.show() - item = list(self.manager.list)[0] - item.setText(0, 'newname') - self.manager.button_ok.click() - assert self.manager.result() == 1 - self.listener1.assert_exact_changes(renamed=[x_cid]) - self.listener2.assert_exact_changes() - assert x_cid.label == 'newname' - assert_equal(self.data1['newname'], [1, 2, 3]) - - def test_rename_invalid(self): - x_cid = self.data1.id['x'] - self.manager = ComponentManagerWidget(self.data_collection) - self.manager.show() - item = list(self.manager.list)[0] - item.setText(0, 'y') - assert not self.manager.button_ok.isEnabled() - assert self.manager.ui.label_status.text() == 'Error: some components have duplicate names' - item = list(self.manager.list)[0] - item.setText(0, 'a') - assert self.manager.button_ok.isEnabled() - assert self.manager.ui.label_status.text() == '' - self.manager.button_ok.click() - self.listener1.assert_exact_changes(renamed=[x_cid]) - self.listener2.assert_exact_changes() - assert x_cid.label == 'a' - assert_equal(self.data1['a'], [1, 2, 3]) diff --git a/glue/dialogs/data_wizard/qt/__init__.py b/glue/dialogs/data_wizard/qt/__init__.py index baab97ed7..108d86cab 100644 --- a/glue/dialogs/data_wizard/qt/__init__.py +++ b/glue/dialogs/data_wizard/qt/__init__.py @@ -1 +1,4 @@ -from .data_wizard_dialog import * # noqa +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.dialogs.data_wizard.qt is deprecated, use glue_qt.dialogs.data_wizard instead', GlueDeprecationWarning) +from glue_qt.dialogs.data_wizard import * # noqa diff --git a/glue/dialogs/data_wizard/qt/data_wizard_dialog.py b/glue/dialogs/data_wizard/qt/data_wizard_dialog.py index 765bff67f..9b1789158 100644 --- a/glue/dialogs/data_wizard/qt/data_wizard_dialog.py +++ b/glue/dialogs/data_wizard/qt/data_wizard_dialog.py @@ -1,136 +1,4 @@ -from qtpy.QtCore import Qt -from qtpy import QtWidgets -from glue.utils.qt import set_cursor_cm - -__all__ = ['data_wizard', 'GlueDataDialog'] - - -def data_wizard(mode='files'): - """ - Qt Dialog to load a file into a new data object - - Parameters - ---------- - mode : {'files', 'directories'} - Whether to allow the user to select files or directories. - - Returns - ------- - A list of new data objects. Returns an empty list if - selection is canceled. - """ - def report_error(error, factory, curfile): - import traceback - retry = QtWidgets.QMessageBox.Retry - cancel = QtWidgets.QMessageBox.Cancel - buttons = retry | cancel - detail = traceback.format_exc() - msg = "\n".join(["Could not load %s (wrong load method?)" % curfile, - "File load method: %s" % factory.label]) - detail = "\n\n".join(["Error message: %s" % error, detail]) - mb = QtWidgets.QMessageBox(QtWidgets.QMessageBox.Critical, "Data Load Error", msg) - mb.setDetailedText(detail) - mb.setDefaultButton(cancel) - mb.setStandardButtons(buttons) - ok = mb.exec_() - return ok == retry - - while True: - gdd = GlueDataDialog(mode=mode) - try: - result = gdd.load_data() - break - except Exception as e: - decision = report_error(e, gdd.factory(), gdd._curfile) - if not decision: - return [] - return result - - -class GlueDataDialog(object): - - def __init__(self, mode='files', parent=None): - self._fd = QtWidgets.QFileDialog(parent) - from glue.config import data_factory - self.filters = [(f, self._filter(f)) - for f in data_factory.members if not f.deprecated] - self.setNameFilter() - if mode == 'files': - self._fd.setFileMode(QtWidgets.QFileDialog.ExistingFiles) - else: - self._fd.setFileMode(QtWidgets.QFileDialog.Directory) - self._fd.setOption(QtWidgets.QFileDialog.ShowDirsOnly, True) - - self._curfile = '' - try: - self._fd.setOption( - QtWidgets.QFileDialog.Option.HideNameFilterDetails, True) - except AttributeError: # HideNameFilterDetails not present - pass - - def factory(self): - fltr = self._fd.selectedNameFilter() - for k, v in self.filters: - if v.startswith(fltr): - return k - - def setNameFilter(self): - fltr = ";;".join([flt for fac, flt in self.filters]) - self._fd.setNameFilter(fltr) - - def _filter(self, factory): - return "%s (*)" % factory.label - - def paths(self): - """ - Return all selected paths, as a list of unicode strings - """ - return self._fd.selectedFiles() - - def _get_paths_and_factory(self): - """Show dialog to get a file path and data factory - - :rtype: tuple of (list-of-strings, func) - giving the path and data factory. - returns ([], None) if user cancels dialog - """ - result = self._fd.exec_() - if result == QtWidgets.QDialog.Rejected: - return [], None - # path = list(map(str, self.paths())) # cast out of unicode - path = list(self.paths()) - factory = self.factory() - return path, factory - - def load_data(self): - """Highest level method to interactively load a data set. - - :rtype: A list of constructed data objects - """ - from glue.core.data_factories import data_label, load_data - paths, fac = self._get_paths_and_factory() - result = [] - - # Check that the user didn't select a .glu file by mistake - for path in paths: - if path.endswith('.glu'): - mb = QtWidgets.QMessageBox(QtWidgets.QMessageBox.Critical, - "Error loading data", - "It looks like you have selected " - "a .glu session file. You should open " - "this using 'Open Session' under the " - "'File' menu instead") - mb.exec_() - return [] - - with set_cursor_cm(Qt.WaitCursor): - for path in paths: - self._curfile = path - d = load_data(path, factory=fac.function) - if not isinstance(d, list): - if not d.label: - d.label = data_label(path) - d = [d] - result.extend(d) - - return result +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.dialogs.data_wizard.qt.data_wizard_dialog is deprecated, use glue_qt.dialogs.data_wizard.data_wizard_dialog instead', GlueDeprecationWarning) +from glue_qt.dialogs.data_wizard.data_wizard_dialog import * # noqa diff --git a/glue/dialogs/data_wizard/qt/tests/__init__.py b/glue/dialogs/data_wizard/qt/tests/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/glue/dialogs/data_wizard/qt/tests/test_data_wizard.py b/glue/dialogs/data_wizard/qt/tests/test_data_wizard.py deleted file mode 100644 index 9103c12dc..000000000 --- a/glue/dialogs/data_wizard/qt/tests/test_data_wizard.py +++ /dev/null @@ -1,100 +0,0 @@ -from unittest.mock import MagicMock, patch - -from glue.core import Data -from glue.config import data_factory -from ..data_wizard_dialog import GlueDataDialog, data_wizard - - -@data_factory('testing_factory', identifier=lambda *args: True, priority=-999) -def dummy_factory(filename): - result = Data() - result.made_with_dummy_factory = True - return result - - -dummy_factory_member = [f for f in data_factory.members - if f[0] is dummy_factory][0] - - -class TestGlueDataDialog(object): - - def test_factory(self): - """Factory method should always match with filter""" - fd = GlueDataDialog() - fd._fd.show() - assert len(fd.filters) > 0 - for k, v in fd.filters: - fd._fd.selectNameFilter(v) - assert fd.factory() is k - - def test_load_data_cancel(self): - """Return None if user cancels operation""" - fd = GlueDataDialog() - mock_file_exec(fd, cancel=True) - assert fd.load_data() == [] - - def test_load_data_normal(self): - """normal load_data dispatches path to factory""" - fd = GlueDataDialog() - mock_file_exec(fd, cancel=False, path='ld_data_nrml', - factory=dummy_factory_member) - d = fd.load_data() - assert len(d) == 1 - d = d[0] - assert d.label == 'ld_data_nrml' - assert d.made_with_dummy_factory is True - - def test_filters(self): - """Should build filter list from data_factories env var""" - fd = GlueDataDialog() - assert len(fd.filters) == len([x for x in data_factory.members if not x.deprecated]) - - def test_load_multiple(self): - fd = GlueDataDialog() - mock_file_exec(fd, cancel=False, path=['a.fits', 'b.fits'], - factory=dummy_factory_member) - ds = fd.load_data() - assert len(ds) == 2 - for d, label in zip(ds, 'ab'): - assert d.label == label - assert d.made_with_dummy_factory is True - - def test_directories(self): - fd = GlueDataDialog(mode='directories') - fd._fd.show() - - -def mock_file_exec(fd, cancel=False, path='junk', - factory=dummy_factory_member): - if not isinstance(path, list): - path = [path] - - fd._fd.exec_ = MagicMock() - fd._fd.exec_.return_value = 1 - cancel - fd.factory = MagicMock() - fd.factory.return_value = factory - fd.paths = MagicMock() - fd.paths.return_value = path - - -def test_data_wizard_cancel(): - """Returns empty list if user cancel's dialog""" - with patch('glue.dialogs.data_wizard.qt.data_wizard_dialog.GlueDataDialog') as mock: - mock().load_data.return_value = [] - assert data_wizard() == [] - - -def test_data_wizard_normal(): - """Returns data list if successful""" - with patch('glue.dialogs.data_wizard.qt.data_wizard_dialog.GlueDataDialog') as mock: - mock().load_data.return_value = [1] - assert data_wizard() == [1] - - -def test_data_wizard_error_cancel(): - """Returns empty list of error generated and then canceled""" - with patch('glue.dialogs.data_wizard.qt.data_wizard_dialog.GlueDataDialog') as mock: - mock().load_data.side_effect = Exception - with patch('qtpy.QtWidgets.QMessageBox') as qmb: - qmb().exec_.return_value = 0 - assert data_wizard() == [] diff --git a/glue/dialogs/link_editor/qt/__init__.py b/glue/dialogs/link_editor/qt/__init__.py index e3a7d82d5..558162ed5 100644 --- a/glue/dialogs/link_editor/qt/__init__.py +++ b/glue/dialogs/link_editor/qt/__init__.py @@ -1 +1,4 @@ -from .link_editor import * # noqa +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.dialogs.link_editor.qt is deprecated, use glue_qt.dialogs.link_editor instead', GlueDeprecationWarning) +from glue_qt.dialogs.link_editor import * # noqa diff --git a/glue/dialogs/link_editor/qt/data_graph.py b/glue/dialogs/link_editor/qt/data_graph.py index a3c49ddf1..e4d3e0378 100644 --- a/glue/dialogs/link_editor/qt/data_graph.py +++ b/glue/dialogs/link_editor/qt/data_graph.py @@ -1,548 +1,4 @@ -from collections import OrderedDict -import numpy as np - -from qtpy.QtCore import Qt, Signal -from qtpy.QtGui import QPainter, QTransform, QPen -from qtpy.QtWidgets import (QGraphicsView, QGraphicsScene, QApplication, - QGraphicsTextItem, QGraphicsEllipseItem, - QGraphicsLineItem) - -from glue.utils.qt import mpl_to_qt_color, qt_to_mpl_color - -COLOR_SELECTED = (0.2, 0.9, 0.2) -COLOR_CONNECTED = (0.6, 0.9, 0.9) -COLOR_DISCONNECTED = (0.9, 0.6, 0.6) - - -def get_pen(color, linewidth=1, linestyle=Qt.SolidLine): - color = mpl_to_qt_color(color) - return QPen(color, linewidth, linestyle, Qt.RoundCap, Qt.RoundJoin) - - -class Edge(QGraphicsLineItem): - - def __init__(self, node_source, node_dest, linewidth=3, zindex=5, link_type="value"): - self.linewidth = linewidth - self.node_source = node_source - self.node_dest = node_dest - if link_type == 'join': - self.linestyle = Qt.DashLine - else: - self.linestyle = Qt.SolidLine - super(Edge, self).__init__(0, 0, 1, 1) - self.setZValue(zindex) - self.color = '0.5' - - def update_position(self): - x0, y0 = self.node_source.node_position - x1, y1 = self.node_dest.node_position - self.setLine(x0, y0, x1, y1) - - @property - def color(self): - return qt_to_mpl_color(self.pen().color()) - - @color.setter - def color(self, value): - self.setPen(get_pen(value, self.linewidth, self.linestyle)) - - def add_to_scene(self, scene): - scene.addItem(self) - - def remove_from_scene(self, scene): - scene.removeItem(self) - - def contains(self, point): - return super(Edge, self).contains(self.mapFromScene(point)) - - -class DataNode: - - def __init__(self, data, radius=15): - - self.data = data - - # Add circular node - self.node = QGraphicsEllipseItem(0, 0, 1, 1) - - # Set radius - self.radius = radius - - # Add text label - self.label = QGraphicsTextItem(data.label) - font = self.label.font() - font.setPointSize(10) - self.label.setFont(font) - - # Add line between label and node - self.line1 = QGraphicsLineItem(0, 0, 1, 1) - self.line2 = QGraphicsLineItem(0, 0, 1, 1) - - self.node.setZValue(20) - self.label.setZValue(10) - self.line1.setZValue(10) - self.line2.setZValue(10) - - self.line1.setPen(get_pen('0.5')) - self.line2.setPen(get_pen('0.5')) - - self.color = '0.8' - - @property - def radius(self): - return self._radius - - @radius.setter - def radius(self, value): - self._radius = value - self.node.setRect(-value, -value, 2 * value, 2 * value) - - def contains(self, point): - - # Check label - if self.label.contains(self.label.mapFromScene(point)): - return True - - # Check node - if self.node.contains(self.node.mapFromScene(point)): - return True - - return False - - def update(self): - self.node.update() - - def add_to_scene(self, scene): - scene.addItem(self.node) - scene.addItem(self.label) - scene.addItem(self.line1) - scene.addItem(self.line2) - - def remove_from_scene(self, scene): - scene.removeItem(self.node) - scene.removeItem(self.label) - scene.removeItem(self.line1) - scene.removeItem(self.line2) - - @property - def node_position(self): - pos = self.node.pos() - return pos.x(), pos.y() - - @node_position.setter - def node_position(self, value): - self.node.setPos(value[0], value[1]) - self.update_lines() - - @property - def label_position(self): - pos = self.label.pos() - return pos.x(), pos.y() - - @label_position.setter - def label_position(self, value): - self.label.setPos(value[0], value[1]) - self.update_lines() - - def update_lines(self): - x0, y0 = self.label_position - x2, y2 = self.node_position - x1 = 0.5 * (x0 + x2) - y1 = y0 - self.line1.setLine(x0, y0, x1, y1) - self.line2.setLine(x1, y1, x2, y2) - - @property - def color(self): - return qt_to_mpl_color(self.node.brush().color()) - - @color.setter - def color(self, value): - self.node.setBrush(mpl_to_qt_color(value)) - - -def get_connections(dc_links): - links = [] - for link in dc_links: - data1 = link.data1 - data2 = link.data2 - if (data1, data2) not in links and (data2, data1) not in links: - links.append((data1, data2, link.link_type)) - - return links - - -def layout_simple_circle(nodes, edges, center=None, radius=None, reorder=True): - - # Place nodes around a circle - - if reorder: - nodes[:] = order_nodes_by_connections(nodes, edges) - - for i, node in enumerate(nodes): - angle = 2 * np.pi * i / len(nodes) - nx = radius * np.cos(angle) + center[0] - ny = radius * np.sin(angle) + center[1] - node.node_position = nx, ny - - -def order_nodes_by_connections(nodes, edges): - - search_nodes = list(nodes) - sorted_nodes = [] - - while len(search_nodes) > 0: - - lengths = [] - connections = [] - - for node in search_nodes: - direct, indirect = find_connections(node, search_nodes, edges) - connections.append((indirect, direct)) - lengths.append((len(indirect), len(direct))) - - m = max(lengths) - - for i in range(len(lengths)): - if lengths[i] == m: - for node in connections[i][0] + connections[i][1]: - if node not in sorted_nodes: - sorted_nodes.append(node) - - search_nodes = [node for node in nodes if node not in sorted_nodes] - - return sorted_nodes - - -class DataGraphWidget(QGraphicsView): - - selection_changed = Signal() - - def __init__(self, parent=None): - - super(DataGraphWidget, self).__init__(parent=parent) - - # Set up scene - - self.scene = QGraphicsScene(self) - self.scene.setItemIndexMethod(QGraphicsScene.NoIndex) - self.scene.setSceneRect(0, 0, 800, 300) - - self.setScene(self.scene) - - self.setWindowTitle("Glue data graph") - - self.setRenderHint(QPainter.Antialiasing) - self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse) - self.setResizeAnchor(QGraphicsView.AnchorViewCenter) - - self.selection_level = 0 - - def resizeEvent(self, event): - self.scene.setSceneRect(0, 0, self.width(), self.height()) - self.relayout(reorder=False) - - def relayout(self, reorder=True): - - # Update radius - for node in self.nodes: - node.radius = self.height() / 30. - - layout_simple_circle(self.nodes, self.edges, - center=(self.width() / 2, self.height() / 2), - radius=self.height() / 3, reorder=reorder) - - # Update edge positions - for edge in self.background_edges + self.edges: - edge.update_position() - - # Set up labels - self.left_nodes = [node for node in self.nodes if node.node_position[0] < self.width() / 2] - self.left_nodes = sorted(self.left_nodes, key=lambda x: x.node_position[1], reverse=True) - - self.right_nodes = [node for node in self.nodes if node not in self.left_nodes] - self.right_nodes = sorted(self.right_nodes, key=lambda x: x.node_position[1], reverse=True) - - for i, node in enumerate(self.left_nodes): - y = self.height() - (i + 1) / (len(self.left_nodes) + 1) * self.height() - node.label_position = self.width() / 2 - self.height() / 2, y - - for i, node in enumerate(self.right_nodes): - y = self.height() - (i + 1) / (len(self.right_nodes) + 1) * self.height() - node.label_position = self.width() / 2 + self.height() / 2, y - - def set_data_collection(self, data_collection, old_links=None, new_links=None): - - # Get data and initialize nodes - self.data_to_nodes = OrderedDict((data, DataNode(data)) for data in data_collection) - self.nodes = list(self.data_to_nodes.values()) - - # Get links and set up edges - - if old_links: - self.background_edges = [Edge(self.data_to_nodes[data1], self.data_to_nodes[data2], linewidth=1, zindex=1, link_type=link_type) - for data1, data2, link_type in get_connections(data_collection.external_links)] - else: - self.background_edges = [] - - if new_links: - self.edges = [Edge(self.data_to_nodes[data1], self.data_to_nodes[data2], link_type=link_type) - for data1, data2, link_type in get_connections(new_links)] - else: - self.edges = [] - - # Figure out positions - self.relayout() - - # Add nodes and edges to graph - - for node in self.nodes: - node.add_to_scene(self.scene) - - for edge in self.background_edges + self.edges: - edge.add_to_scene(self.scene) - - self.text_adjusted = False - - self.selected_edge = None - self.selected_node1 = None - self.selected_node2 = None - - def set_links(self, links): - - for edge in self.edges: - edge.remove_from_scene(self.scene) - - self.edges = [Edge(self.data_to_nodes[data1], self.data_to_nodes[data2], link_type=link_type) - for data1, data2, link_type in get_connections(links)] - - for edge in self.edges: - edge.update_position() - - for edge in self.edges: - edge.add_to_scene(self.scene) - - self._update_selected_edge() - - self._update_selected_colors() - - def paintEvent(self, event): - - super(DataGraphWidget, self).paintEvent(event) - - if not self.text_adjusted: - - for node in self.nodes: - - width = node.label.boundingRect().width() - height = node.label.boundingRect().height() - - transform = QTransform() - if node in self.left_nodes: - transform.translate(-width, -height / 2) - else: - transform.translate(0, -height / 2) - - node.label.setTransform(transform) - - self.text_adjusted = True - - def manual_select(self, data1=None, data2=None): - if data1 is None and data2 is not None: - data1, data2 = data2, data1 - if data2 is not None: - self.selection_level = 2 - elif data1 is not None: - self.selection_level = 1 - else: - self.selection_level = 0 - self.selected_node1 = self.data_to_nodes.get(data1, None) - self.selected_node2 = self.data_to_nodes.get(data2, None) - self._update_selected_edge() - self._update_selected_colors() - - def find_object(self, event): - for obj in list(self.nodes) + self.edges: - try: # event.position() is Qt6 - if obj.contains(event.position()): - return obj - except AttributeError: # event.pos() is Qt5 - if obj.contains(event.pos()): - return obj - - def mouseMoveEvent(self, event): - - # TODO: Don't update until the end - # TODO: Only select object on top - - selected = self.find_object(event) - - if selected is None: - - if self.selection_level == 0: - self.selected_node1 = None - self.selected_node2 = None - self._update_selected_edge() - elif self.selection_level == 1: - self.selected_node2 = None - self._update_selected_edge() - - elif isinstance(selected, DataNode): - - if self.selection_level == 0: - self.selected_node1 = selected - self.selected_node2 = None - elif self.selection_level == 1: - if selected is not self.selected_node1: - self.selected_node2 = selected - self._update_selected_edge() - - elif isinstance(selected, Edge): - - if self.selection_level == 0: - self.selected_edge = selected - self.selected_node1 = selected.node_source - self.selected_node2 = selected.node_dest - - self._update_selected_colors() - - self.selection_changed.emit() - - def mousePressEvent(self, event): - - # TODO: Don't update until the end - # TODO: Only select object on top - - selected = self.find_object(event) - - if selected is None: - - self.selection_level = 0 - self.selected_node1 = None - self.selected_node2 = None - - self._update_selected_edge() - - elif isinstance(selected, DataNode): - - if self.selection_level == 0: - self.selected_node1 = selected - self.selection_level += 1 - elif self.selection_level == 1: - if selected is self.selected_node1: - self.selected_node1 = None - self.selection_level = 0 - else: - self.selected_node2 = selected - self.selection_level = 2 - elif self.selection_level == 2: - if selected is self.selected_node2: - self.selected_node2 = None - self.selection_level = 1 - else: - self.selected_node1 = selected - self.selected_node2 = None - self.selection_level = 1 - - self._update_selected_edge() - - elif isinstance(selected, Edge): - - if self.selected_edge is selected and self.selection_level == 2: - self.selected_edge = None - self.selected_node1 = None - self.selected_node2 = None - self.selection_level = 0 - else: - self.selected_edge = selected - self.selected_node1 = selected.node_source - self.selected_node2 = selected.node_dest - self.selection_level = 2 - - self.mouseMoveEvent(event) - - def _update_selected_edge(self): - for edge in self.edges: - if (edge.node_source is self.selected_node1 and edge.node_dest is self.selected_node2 or - edge.node_source is self.selected_node2 and edge.node_dest is self.selected_node1): - self.selected_edge = edge - break - else: - self.selected_edge = None - - def _update_selected_colors(self): - - colors = {} - - if self.selected_node1 is not None and self.selection_level < 2: - - direct, indirect = find_connections(self.selected_node1, self.nodes, self.edges) - - for node in self.nodes: - if node in direct or node in indirect: - colors[node] = COLOR_CONNECTED - else: - colors[node] = COLOR_DISCONNECTED - - for edge in self.edges: - if (edge.node_source is self.selected_node1 or - edge.node_dest is self.selected_node1): - colors[edge] = COLOR_CONNECTED - - if self.selected_edge is not None: - colors[self.selected_edge] = COLOR_SELECTED - - if self.selected_node1 is not None: - colors[self.selected_node1] = COLOR_SELECTED - - if self.selected_node2 is not None: - colors[self.selected_node2] = COLOR_SELECTED - - self.set_colors(colors) - - def set_colors(self, colors): - - for obj in list(self.nodes) + self.edges: - default_color = '0.8' if isinstance(obj, DataNode) else '0.5' - obj.color = colors.get(obj, default_color) - obj.update() - - -def find_connections(node, nodes, edges): - - direct = [node] - indirect = [] - current = direct - connected = [node] - - changed = True - while changed: - changed = False - for edge in edges: - source = edge.node_source - dest = edge.node_dest - if source in connected and dest not in connected: - current.append(dest) - changed = True - if dest in connected and source not in connected: - current.append(source) - changed = True - current = indirect - connected.extend(current) - return direct, indirect - - -if __name__ == '__main__': - - import sys - - app = QApplication(sys.argv) - app.setAttribute(Qt.AA_UseHighDpiPixmaps) - - from glue.core.state import load - dc = load('links.glu') - - widget = DataGraphWidget(dc) - widget.show() - - sys.exit(app.exec_()) +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.dialogs.link_editor.qt.data_graph is deprecated, use glue_qt.dialogs.link_editor.data_graph instead', GlueDeprecationWarning) +from glue_qt.dialogs.link_editor.data_graph import * # noqa diff --git a/glue/dialogs/link_editor/qt/link_editor.py b/glue/dialogs/link_editor/qt/link_editor.py index 1926bd4f9..39f5690e2 100644 --- a/glue/dialogs/link_editor/qt/link_editor.py +++ b/glue/dialogs/link_editor/qt/link_editor.py @@ -1,261 +1,4 @@ -import os - -from qtpy import QtWidgets - -from glue.config import link_function, link_helper -from glue.utils.decorators import avoid_circular -from glue.utils.qt import load_ui -from echo.qt import autoconnect_callbacks_to_qt -from echo.qt.connect import UserDataWrapper, connect_combo_selection -from glue.dialogs.link_editor.state import LinkEditorState - -__all__ = ['LinkEditor', 'main'] - -N_COMBO_MAX = 10 - - -def get_function_name(info): - item = info[0] - if hasattr(item, 'display') and item.display is not None: - return item.display - else: - return item.__name__ - - -class LinkMenu(QtWidgets.QMenu): - - def __init__(self, parent=None): - - super(LinkMenu, self).__init__(parent=parent) - - categories = [] - for function in link_function.members: - if len(function.output_labels) == 1: - categories.append(function.category) - for helper in link_helper.members: - categories.append(helper.category) - categories = ['General'] + sorted(set(categories) - set(['General'])) - - for category in categories: - submenu = self.addMenu(category) - for function in link_function.members: - if function.category == category and len(function.output_labels) == 1: - action = submenu.addAction(get_function_name(function)) - action.setData(UserDataWrapper(function)) - for helper in link_helper.members: - if helper.category == category: - action = submenu.addAction(get_function_name(helper)) - action.setData(UserDataWrapper(helper)) - - -class LinkEditorWidget(QtWidgets.QWidget): - - def __init__(self, data_collection, suggested_links=None, parent=None): - - super(LinkEditorWidget, self).__init__(parent=parent) - - self._data_collection = data_collection - - self.state = LinkEditorState(data_collection, suggested_links=suggested_links) - - self._connections = [] - - self._ui = load_ui('link_editor_widget.ui', self, - directory=os.path.dirname(__file__)) - self._handlers = autoconnect_callbacks_to_qt(self.state, self._ui) - - self._set_up_combos() - - self._ui.graph_widget.set_data_collection(data_collection, new_links=self.state.links) - self._ui.graph_widget.selection_changed.connect(self._on_data_change_graph) - - self._menu = LinkMenu(parent=self._ui.button_add_link) - self._menu.triggered.connect(self._add_link) - self._ui.button_add_link.setMenu(self._menu) - - self._watched_links = [] - - self.state.add_callback('data1', self._on_data_change) - self.state.add_callback('data2', self._on_data_change) - self._on_data_change() - - self.state.add_callback('data1', self._on_data_change_always) - self.state.add_callback('data2', self._on_data_change_always) - self._on_data_change_always() - - self.state.add_callback('current_link', self._on_current_link_change) - self._on_current_link_change() - - def _add_link(self, action): - self.state.new_link(action.data().data) - - def _set_up_combos(self): - - # Set up combo boxes - for now we hard-code the maximum number, but - # we could do this more smartly by checking existing links and all - # possible links in registry to figure out max number needed. - - self.att_names1 = [] - self.att_combos1 = [] - - for combo_idx in range(N_COMBO_MAX): - label_widget = QtWidgets.QLabel() - combo_widget = QtWidgets.QComboBox(parent=self._ui) - self.att_names1.append(label_widget) - self.att_combos1.append(combo_widget) - self._ui.combos1.addWidget(label_widget, combo_idx, 0) - self._ui.combos1.addWidget(combo_widget, combo_idx, 1) - - self.att_names2 = [] - self.att_combos2 = [] - - for combo_idx in range(N_COMBO_MAX): - label_widget = QtWidgets.QLabel() - combo_widget = QtWidgets.QComboBox(parent=self._ui) - self.att_names2.append(label_widget) - self.att_combos2.append(combo_widget) - self._ui.combos2.addWidget(label_widget, combo_idx, 0) - self._ui.combos2.addWidget(combo_widget, combo_idx, 1) - - @avoid_circular - def _on_attribute_combo_change(self, *args, **kwargs): - # Force a re-sync of the choices - self._handlers['listsel_current_link'].update_widget(self.state.current_link, force=True) - - @avoid_circular - def _on_data_change_graph(self): - self.state.data1 = getattr(self._ui.graph_widget.selected_node1, 'data', None) - self.state.data2 = getattr(self._ui.graph_widget.selected_node2, 'data', None) - - @avoid_circular - def _on_data_change(self, *args): - self._ui.graph_widget.manual_select(self.state.data1, self.state.data2) - - def _on_data_change_always(self, *args): - # This should always run even when the change comes from the graph - enabled = self.state.data1 is not None and self.state.data2 is not None - self._ui.button_add_link.setEnabled(enabled) - self._ui.button_simple_link.setEnabled(enabled) - - def _on_current_link_change(self, *args): - - # We update the link details panel on the right - - for connnection in self._connections: - connnection.disconnect() - - self._connections = [] - - link = self.state.current_link - - if link is None: - self._ui.button_remove_link.setEnabled(False) - self._ui.link_details.setText('') - self._ui.combos1_header.hide() - self._ui.combos2_header.hide() - for widget in self.att_combos1 + self.att_names1 + self.att_combos2 + self.att_names2: - widget.hide() - return - - self._ui.button_remove_link.setEnabled(True) - - self._ui.link_details.setText(link.description) - - if link.data1 is self.state.data1: - data1_names = link.names1 - else: - data1_names = link.names2 - - for idx, (label, combo) in enumerate(zip(self.att_names1, self.att_combos1)): - if idx < len(data1_names): - combo.show() - label.show() - label.setText(data1_names[idx]) - disconnector = connect_combo_selection(link, data1_names[idx], combo) - self._connections.append(disconnector) - else: - label.hide() - combo.hide() - - if link.data1 is self.state.data2: - data2_names = link.names1 - else: - data2_names = link.names2 - - for idx, (label, combo) in enumerate(zip(self.att_names2, self.att_combos2)): - if idx < len(data2_names): - combo.show() - label.show() - label.setText(data2_names[idx]) - disconnector = connect_combo_selection(link, data2_names[idx], combo) - self._connections.append(disconnector) - else: - label.hide() - combo.hide() - - # Headers aren't needed if data2_names is 0 (legacy mode for old link - # helpers where all attributes are 'inputs') - if len(data2_names) == 0: - self._ui.combos1_header.hide() - self._ui.combos2_header.hide() - else: - self._ui.combos1_header.show() - self._ui.combos2_header.show() - - self._ui.graph_widget.set_links(self.state.links) - - # When the user changes one of the attributes, we need to update the - # main list of links to show the attributes being linked. This is - # actually tricker than it sounds, and to solve this we listen for - # changes in any of the link properties. We don't need to unsubscribe - # since there's no harm in keeping them connected, and we only need to - # subscribe to links that have been shown at least once as the other - # ones can't be changed by users. - if link not in self._watched_links: - link.add_global_callback(self._on_attribute_combo_change) - self._watched_links.append(link) - - -class LinkEditor(QtWidgets.QDialog): - - def __init__(self, data_collection, suggested_links=None, parent=None): - - super(LinkEditor, self).__init__(parent=parent) - - self._ui = load_ui('link_editor_dialog.ui', self, - directory=os.path.dirname(__file__)) - - self.link_widget = LinkEditorWidget(data_collection, - suggested_links=suggested_links, - parent=self) - - self._ui.layout().insertWidget(1, self.link_widget) - - def accept(self, *args): - self.link_widget.state.update_links_in_collection() - super(LinkEditor, self).accept(*args) - - @classmethod - def update_links(cls, collection, suggested_links=None): - widget = cls(collection, suggested_links=suggested_links) - widget._ui.exec_() - - -def main(): # pragma: no cover - import numpy as np - from glue.main import load_plugins - from glue.utils.qt import get_qapp - from glue.core import Data, DataCollection - - load_plugins() - - app = get_qapp() - - dc = DataCollection() - - for i in range(10): - x = np.array([1, 2, 3]) - d = Data(label='data_{0:02d}'.format(i), x=x, y=x * 2) - dc.append(d) - - LinkEditor.update_links(dc) +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.dialogs.link_editor.qt.link_editor is deprecated, use glue_qt.dialogs.link_editor.link_editor instead', GlueDeprecationWarning) +from glue_qt.dialogs.link_editor.link_editor import * # noqa diff --git a/glue/dialogs/link_editor/qt/link_editor_dialog.ui b/glue/dialogs/link_editor/qt/link_editor_dialog.ui deleted file mode 100644 index cbe4ca955..000000000 --- a/glue/dialogs/link_editor/qt/link_editor_dialog.ui +++ /dev/null @@ -1,100 +0,0 @@ - - - LinkEditor - - - - 0 - 0 - 900 - 700 - - - - Link Editor - - - true - - - - QLayout::SetDefaultConstraint - - - - - Click on two datasets to set up links or click on an existing connection to edit links. Selected datasets are shown in green. When one dataset is selected, the colors show directly and indirectly linked (blue) and inaccessible (red) datasets. - - - true - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - false - - - - - - - - - - - buttonBox - accepted() - LinkEditor - accept() - - - 248 - 254 - - - 157 - 274 - - - - - buttonBox - rejected() - LinkEditor - reject() - - - 316 - 260 - - - 286 - 274 - - - - - diff --git a/glue/dialogs/link_editor/qt/link_editor_widget.ui b/glue/dialogs/link_editor/qt/link_editor_widget.ui deleted file mode 100644 index 2b0e81e28..000000000 --- a/glue/dialogs/link_editor/qt/link_editor_widget.ui +++ /dev/null @@ -1,330 +0,0 @@ - - - LinkEditorWidget - - - - 0 - 0 - 627 - 648 - - - - - 0 - 0 - - - - Link Editor - - - - - - - 0 - 0 - - - - - 0 - 300 - - - - - 0 - 300 - - - - Qt::ScrollBarAlwaysOff - - - Qt::ScrollBarAlwaysOff - - - QAbstractScrollArea::AdjustToContents - - - - - - - Qt::Vertical - - - QSizePolicy::Fixed - - - - 20 - 20 - - - - - - - - 10 - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 75 - true - - - - Link details - - - Qt::AlignCenter - - - - - - - - 75 - true - - - - Links between Dataset 1 and Dataset 2 - - - Qt::AlignCenter - - - - - - - - - Glue attributes - - - - - - - - - - - - - 75 - true - - - - Dataset 2 - - - Qt::AlignCenter - - - - - - - - 0 - 0 - - - - - - - - QComboBox::AdjustToMinimumContentsLengthWithIcon - - - - - - - QComboBox::AdjustToMinimumContentsLengthWithIcon - - - - - - - - 75 - true - - - - Dataset 1 - - - Qt::AlignCenter - - - - - - - - - Create advanced link - - - QToolButton::InstantPopup - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Remove link - - - - - - - - - padding: 0px - - - ⇄ - - - - - - - QLayout::SetDefaultConstraint - - - - - Details about the link - - - Qt::AlignJustify - - - true - - - - - - - - 75 - true - - - - Dataset 1 attributes - - - - - - - 10 - - - 5 - - - - - - - - 75 - true - - - - Dataset 2 attributes - - - - - - - 10 - - - 5 - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - - DataGraphWidget - QGraphicsView -
glue.dialogs.link_editor.qt.data_graph
- 1 -
-
- - -
diff --git a/glue/dialogs/link_editor/qt/tests/__init__.py b/glue/dialogs/link_editor/qt/tests/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/glue/dialogs/link_editor/qt/tests/test_link_editor.py b/glue/dialogs/link_editor/qt/tests/test_link_editor.py deleted file mode 100644 index a868dbe88..000000000 --- a/glue/dialogs/link_editor/qt/tests/test_link_editor.py +++ /dev/null @@ -1,725 +0,0 @@ -from unittest.mock import patch - -from qtpy import QtWidgets -from glue.utils.qt import process_events -from glue.core import Data, DataCollection -from glue.dialogs.link_editor.qt import LinkEditor -from glue.core.component_link import ComponentLink -from glue.plugins.coordinate_helpers.link_helpers import Galactic_to_FK5, ICRS_to_Galactic -from glue.core.link_helpers import identity, functional_link_collection, LinkSame - - -def non_empty_rows_count(layout): - """ - Determine how many rows of the QGridLayout are not empty - """ - count = 0 - for row in range(layout.rowCount()): - for col in range(layout.columnCount()): - item = layout.itemAtPosition(row, col) - if item is not None and item.widget() is not None and item.widget().isVisible(): - count += 1 - break - return count - - -def get_action(link_widget, text): - for submenu in link_widget._menu.children(): - if isinstance(submenu, QtWidgets.QMenu): - for action in submenu.actions(): - if action.text() == text: - return action - raise ValueError("Action '{0}' not found".format(text)) - - -class TestLinkEditor: - - def setup_method(self, method): - - self.data1 = Data(x=[1, 2, 3], y=[2, 3, 4], z=[6, 5, 4], label='data1') - self.data2 = Data(a=[2, 3, 4], b=[4, 5, 4], c=[3, 4, 1], label='data2') - self.data3 = Data(i=[5, 4, 3], j=[2, 2, 1], label='data3') - - self.data_collection = DataCollection([self.data1, self.data2, self.data3]) - - def test_defaults(self): - # Make sure the dialog opens and closes and check default settings. - dialog = LinkEditor(self.data_collection) - dialog.show() - link_widget = dialog.link_widget - - assert link_widget.state.data1 is None - assert link_widget.state.data2 is None - assert not link_widget.button_add_link.isEnabled() - assert not link_widget.button_remove_link.isEnabled() - - link_widget.state.data1 = self.data2 - - assert not link_widget.button_add_link.isEnabled() - assert not link_widget.button_remove_link.isEnabled() - - link_widget.state.data2 = self.data1 - - assert link_widget.button_add_link.isEnabled() - assert not link_widget.button_remove_link.isEnabled() - - dialog.accept() - - assert len(self.data_collection.external_links) == 0 - - def test_defaults_two(self): - # Make sure the dialog opens and closes and check default settings. With - # two datasets, the datasets should be selected by default. - self.data_collection.remove(self.data3) - dialog = LinkEditor(self.data_collection) - dialog.show() - link_widget = dialog.link_widget - assert link_widget.state.data1 is self.data1 - assert link_widget.state.data2 is self.data2 - assert link_widget.button_add_link.isEnabled() - assert not link_widget.button_remove_link.isEnabled() - dialog.accept() - assert len(self.data_collection.external_links) == 0 - - def test_ui_behavior(self): - - # This is a bit more detailed test that checks that things update - # correctly as we change various settings - - dialog = LinkEditor(self.data_collection) - dialog.show() - link_widget = dialog.link_widget - - link_widget.state.data1 = self.data1 - link_widget.state.data2 = self.data2 - - add_identity_link = get_action(link_widget, 'identity') - add_lengths_volume_link = get_action(link_widget, 'lengths_to_volume') - - # At this point, there should be no links in the main list widget - # and nothing on the right. - assert link_widget.listsel_current_link.count() == 0 - assert link_widget.link_details.text() == '' - assert non_empty_rows_count(link_widget.combos1) == 0 - assert non_empty_rows_count(link_widget.combos2) == 0 - - # Let's add an identity link - add_identity_link.trigger() - - # Ensure that all events get processed - process_events() - - # Now there should be one link in the main list and content in the - # right hand panel. - assert link_widget.listsel_current_link.count() == 1 - assert link_widget.link_details.text() == 'Link conceptually identical components' - assert non_empty_rows_count(link_widget.combos1) == 1 - assert non_empty_rows_count(link_widget.combos2) == 1 - assert link_widget.combos1.itemAtPosition(0, 1).widget().currentText() == 'x' - assert link_widget.combos2.itemAtPosition(0, 1).widget().currentText() == 'a' - - # Let's change the current components for the link - link_widget.state.current_link.x = self.data1.id['y'] - link_widget.state.current_link.y = self.data2.id['b'] - - # and make sure the UI gets updated - assert link_widget.combos1.itemAtPosition(0, 1).widget().currentText() == 'y' - assert link_widget.combos2.itemAtPosition(0, 1).widget().currentText() == 'b' - - # We now add another link of a different type - add_lengths_volume_link.trigger() - - # Ensure that all events get processed - process_events() - - # and make sure the UI has updated - assert link_widget.listsel_current_link.count() == 2 - assert link_widget.link_details.text() == 'Convert between linear measurements and volume' - assert non_empty_rows_count(link_widget.combos1) == 3 - assert non_empty_rows_count(link_widget.combos2) == 1 - assert link_widget.combos1.itemAtPosition(0, 1).widget().currentText() == 'x' - assert link_widget.combos1.itemAtPosition(1, 1).widget().currentText() == 'y' - assert link_widget.combos1.itemAtPosition(2, 1).widget().currentText() == 'z' - assert link_widget.combos2.itemAtPosition(0, 1).widget().currentText() == 'a' - - # Try swapping the order of the data, the current link should stay the same - link_widget.state.flip_data() - assert link_widget.link_details.text() == 'Convert between linear measurements and volume' - - # And flip it back - link_widget.state.flip_data() - assert link_widget.link_details.text() == 'Convert between linear measurements and volume' - - # Now switch back to the first link - link_widget.state.current_link = type(link_widget.state).current_link.get_choices(link_widget.state)[0] - - # and make sure the UI updates and has preserved the correct settings - assert link_widget.listsel_current_link.count() == 2 - assert link_widget.link_details.text() == 'Link conceptually identical components' - assert non_empty_rows_count(link_widget.combos1) == 1 - assert non_empty_rows_count(link_widget.combos2) == 1 - assert link_widget.combos1.itemAtPosition(0, 1).widget().currentText() == 'y' - assert link_widget.combos2.itemAtPosition(0, 1).widget().currentText() == 'b' - - # Next up, we try changing the data - - link_widget.state.data1 = self.data3 - - # At this point there should be no links in the list - - assert link_widget.listsel_current_link.count() == 0 - assert link_widget.link_details.text() == '' - assert non_empty_rows_count(link_widget.combos1) == 0 - assert non_empty_rows_count(link_widget.combos2) == 0 - - # Add another identity link - add_identity_link.trigger() - - # Ensure that all events get processed - process_events() - - # Now there should be one link in the main list - assert link_widget.listsel_current_link.count() == 1 - assert link_widget.link_details.text() == 'Link conceptually identical components' - assert non_empty_rows_count(link_widget.combos1) == 1 - assert non_empty_rows_count(link_widget.combos2) == 1 - assert link_widget.combos1.itemAtPosition(0, 1).widget().currentText() == 'i' - assert link_widget.combos2.itemAtPosition(0, 1).widget().currentText() == 'a' - - # Switch back to the original data - link_widget.state.data1 = self.data1 - - # And check the output is as before - assert link_widget.listsel_current_link.count() == 2 - assert link_widget.link_details.text() == 'Link conceptually identical components' - assert non_empty_rows_count(link_widget.combos1) == 1 - assert non_empty_rows_count(link_widget.combos2) == 1 - assert link_widget.combos1.itemAtPosition(0, 1).widget().currentText() == 'y' - assert link_widget.combos2.itemAtPosition(0, 1).widget().currentText() == 'b' - - # Let's now remove this link - link_widget.button_remove_link.click() - - # Ensure that all events get processed - process_events() - - # We should now see the lengths/volume link - assert link_widget.listsel_current_link.count() == 1 - assert link_widget.link_details.text() == 'Convert between linear measurements and volume' - assert non_empty_rows_count(link_widget.combos1) == 3 - assert non_empty_rows_count(link_widget.combos2) == 1 - assert link_widget.combos1.itemAtPosition(0, 1).widget().currentText() == 'x' - assert link_widget.combos1.itemAtPosition(1, 1).widget().currentText() == 'y' - assert link_widget.combos1.itemAtPosition(2, 1).widget().currentText() == 'z' - assert link_widget.combos2.itemAtPosition(0, 1).widget().currentText() == 'a' - - dialog.accept() - - links = self.data_collection.external_links - - assert len(links) == 2 - - assert isinstance(links[0], ComponentLink) - assert links[0].get_from_ids()[0] is self.data1.id['x'] - assert links[0].get_from_ids()[1] is self.data1.id['y'] - assert links[0].get_from_ids()[2] is self.data1.id['z'] - assert links[0].get_to_id() is self.data2.id['a'] - - assert isinstance(links[1], ComponentLink) - assert links[1].get_from_ids()[0] is self.data3.id['i'] - assert links[1].get_to_id() is self.data2.id['a'] - - def test_graph(self): - - dialog = LinkEditor(self.data_collection) - dialog.show() - link_widget = dialog.link_widget - - add_identity_link = get_action(link_widget, 'identity') - - graph = link_widget.graph_widget - - def click(node_or_edge): - # We now simulate a selection - since we can't deterministically - # figure out the exact pixel coordinates to use, we patch - # 'find_object' to return the object we want to select. - with patch.object(graph, 'find_object', return_value=node_or_edge): - graph.mousePressEvent(None) - - def hover(node_or_edge): - # Same as for select, we patch find_object - with patch.object(graph, 'find_object', return_value=node_or_edge): - graph.mouseMoveEvent(None) - - # To start with, no data should be selected - assert link_widget.state.data1 is None - assert link_widget.state.data2 is None - - # and the graph should have three nodes and no edges - assert len(graph.nodes) == 3 - assert len(graph.edges) == 0 - - click(graph.nodes[0]) - - # Check that this has caused one dataset to be selected - assert link_widget.state.data1 is self.data1 - assert link_widget.state.data2 is None - - # Click on the same node again and this should deselect the data - # (but only once we move off from the node) - - click(graph.nodes[0]) - - assert link_widget.state.data1 is self.data1 - assert link_widget.state.data2 is None - - hover(None) - - assert link_widget.state.data1 is None - assert link_widget.state.data2 is None - - # Select it again - click(graph.nodes[0]) - - # and now select another node too - click(graph.nodes[1]) - - assert link_widget.state.data1 is self.data1 - assert link_widget.state.data2 is self.data2 - - assert len(graph.nodes) == 3 - assert len(graph.edges) == 0 - - add_identity_link.trigger() - - assert len(graph.nodes) == 3 - assert len(graph.edges) == 1 - - # Unselect and select another node - click(graph.nodes[1]) - click(graph.nodes[2]) - - # and check the data selections have been updated - assert link_widget.state.data1 is self.data1 - assert link_widget.state.data2 is self.data3 - - # Deselect it and move off - click(graph.nodes[2]) - hover(None) - - # and the second dataset should now once again be None - assert link_widget.state.data1 is self.data1 - assert link_widget.state.data2 is None - - # Now change the data manually - link_widget.state.data1 = self.data2 - link_widget.state.data2 = self.data3 - - # and check that if we select the edge the datasets change back - click(graph.edges[0]) - - assert link_widget.state.data1 is self.data1 - assert link_widget.state.data2 is self.data2 - - # Unselect and hover over nothing - click(graph.edges[0]) - hover(None) - assert link_widget.state.data1 is None - assert link_widget.state.data2 is None - - # Hover over the edge and the datasets should change back - hover(graph.edges[0]) - assert link_widget.state.data1 is self.data1 - assert link_widget.state.data2 is self.data2 - - # And check that clicking outside of nodes/edges deselects everything - click(None) - assert link_widget.state.data1 is None - assert link_widget.state.data2 is None - - # Select a node, select another, then make sure that selecting a third - # one will deselect the two original ones - click(graph.nodes[0]) - click(graph.nodes[1]) - click(graph.nodes[2]) - assert link_widget.state.data1 is self.data3 - assert link_widget.state.data2 is None - - dialog.accept() - - def test_preexisting_links(self): - - # Check that things work properly if there are pre-existing links - - link1 = ComponentLink([self.data1.id['x']], self.data2.id['c']) - - def add(x, y): - return x + y - - def double(x): - return x * 2 - - def halve(x): - return x / 2 - - link2 = ComponentLink([self.data2.id['a'], self.data2.id['b']], self.data3.id['j'], using=add) - link3 = ComponentLink([self.data3.id['i']], self.data2.id['c'], using=double, inverse=halve) - - # Test using a LinkHelper link since that caused a bug earlier - link4 = LinkSame(self.data1.id['z'], self.data2.id['c']) - - self.data_collection.add_link(link1) - self.data_collection.add_link(link2) - self.data_collection.add_link(link3) - self.data_collection.add_link(link4) - - dialog = LinkEditor(self.data_collection) - dialog.show() - link_widget = dialog.link_widget - - link_widget.state.data1 = self.data1 - link_widget.state.data2 = self.data2 - - assert link_widget.listsel_current_link.count() == 2 - assert link_widget.link_details.text() == '' - assert non_empty_rows_count(link_widget.combos1) == 1 - assert non_empty_rows_count(link_widget.combos2) == 1 - assert link_widget.combos1.itemAtPosition(0, 1).widget().currentText() == 'x' - assert link_widget.combos2.itemAtPosition(0, 1).widget().currentText() == 'c' - - link_widget.state.current_link = type(link_widget.state).current_link.get_choices(link_widget.state)[1] - assert link_widget.link_details.text() == '' - assert non_empty_rows_count(link_widget.combos1) == 1 - assert non_empty_rows_count(link_widget.combos2) == 1 - assert link_widget.combos1.itemAtPosition(0, 1).widget().currentText() == 'z' - assert link_widget.combos2.itemAtPosition(0, 1).widget().currentText() == 'c' - - link_widget.state.data1 = self.data3 - - assert link_widget.listsel_current_link.count() == 2 - assert link_widget.link_details.text() == '' - assert non_empty_rows_count(link_widget.combos1) == 1 - assert non_empty_rows_count(link_widget.combos2) == 2 - assert link_widget.combos1.itemAtPosition(0, 1).widget().currentText() == 'j' - assert link_widget.combos2.itemAtPosition(0, 1).widget().currentText() == 'a' - assert link_widget.combos2.itemAtPosition(1, 1).widget().currentText() == 'b' - - link_widget.state.current_link = type(link_widget.state).current_link.get_choices(link_widget.state)[1] - - assert link_widget.listsel_current_link.count() == 2 - assert link_widget.link_details.text() == '' - assert non_empty_rows_count(link_widget.combos1) == 1 - assert non_empty_rows_count(link_widget.combos2) == 1 - assert link_widget.combos1.itemAtPosition(0, 1).widget().currentText() == 'i' - assert link_widget.combos2.itemAtPosition(0, 1).widget().currentText() == 'c' - - dialog.accept() - - links = self.data_collection.external_links - - assert len(links) == 4 - - assert isinstance(links[0], ComponentLink) - assert links[0].get_from_ids()[0] is self.data1.id['x'] - assert links[0].get_to_id() is self.data2.id['c'] - assert links[0].get_using() is identity - - assert isinstance(links[1], ComponentLink) - assert links[1].get_from_ids()[0] is self.data2.id['a'] - assert links[1].get_from_ids()[1] is self.data2.id['b'] - assert links[1].get_to_id() is self.data3.id['j'] - assert links[1].get_using() is add - - assert isinstance(links[2], ComponentLink) - assert links[2].get_from_ids()[0] is self.data3.id['i'] - assert links[2].get_to_id() is self.data2.id['c'] - assert links[2].get_using() is double - assert links[2].get_inverse() is halve - - assert isinstance(links[3], LinkSame) - assert len(links[3].cids1) == 1 - assert links[3].cids1[0] is self.data1.id['z'] - assert len(links[3].cids2) == 1 - assert links[3].cids2[0] is self.data2.id['c'] - assert links[3].forwards is identity - - def test_add_helper(self): - - dialog = LinkEditor(self.data_collection) - dialog.show() - link_widget = dialog.link_widget - - link_widget.state.data1 = self.data1 - link_widget.state.data2 = self.data2 - - add_coordinate_link = get_action(link_widget, 'ICRS <-> Galactic') - - # Add a coordinate link - add_coordinate_link.trigger() - - # Ensure that all events get processed - process_events() - - assert link_widget.listsel_current_link.count() == 1 - assert link_widget.link_details.text() == 'Link ICRS and Galactic coordinates' - assert non_empty_rows_count(link_widget.combos1) == 2 - assert non_empty_rows_count(link_widget.combos2) == 2 - assert link_widget.combos1.itemAtPosition(0, 1).widget().currentText() == 'x' - assert link_widget.combos1.itemAtPosition(1, 1).widget().currentText() == 'y' - assert link_widget.combos2.itemAtPosition(0, 1).widget().currentText() == 'a' - assert link_widget.combos2.itemAtPosition(1, 1).widget().currentText() == 'b' - - dialog.accept() - - links = self.data_collection.external_links - - assert len(links) == 1 - - assert isinstance(links[0], ICRS_to_Galactic) - assert links[0].cids1[0] is self.data1.id['x'] - assert links[0].cids1[1] is self.data1.id['y'] - assert links[0].cids2[0] is self.data2.id['a'] - assert links[0].cids2[1] is self.data2.id['b'] - - def test_preexisting_helper(self): - - link1 = Galactic_to_FK5(cids1=[self.data1.id['x'], self.data1.id['y']], - cids2=[self.data2.id['c'], self.data2.id['b']]) - - self.data_collection.add_link(link1) - - dialog = LinkEditor(self.data_collection) - dialog.show() - link_widget = dialog.link_widget - - assert link_widget.listsel_current_link.count() == 0 - - link_widget.state.data1 = self.data1 - link_widget.state.data2 = self.data2 - - assert link_widget.listsel_current_link.count() == 1 - assert link_widget.link_details.text() == 'Link Galactic and FK5 (J2000) Equatorial coordinates' - assert non_empty_rows_count(link_widget.combos1) == 2 - assert non_empty_rows_count(link_widget.combos2) == 2 - assert link_widget.combos1.itemAtPosition(0, 1).widget().currentText() == 'x' - assert link_widget.combos1.itemAtPosition(1, 1).widget().currentText() == 'y' - assert link_widget.combos2.itemAtPosition(0, 1).widget().currentText() == 'c' - assert link_widget.combos2.itemAtPosition(1, 1).widget().currentText() == 'b' - - dialog.accept() - - links = self.data_collection.external_links - - assert len(links) == 1 - - assert isinstance(links[0], Galactic_to_FK5) - assert links[0].cids1[0] is self.data1.id['x'] - assert links[0].cids1[1] is self.data1.id['y'] - assert links[0].cids2[0] is self.data2.id['c'] - assert links[0].cids2[1] is self.data2.id['b'] - - def test_cancel(self): - - # Make sure that changes aren't saved if dialog is cancelled - # This is a bit more detailed test that checks that things update - # correctly as we change various settings - - link1 = ComponentLink([self.data1.id['x']], self.data2.id['c']) - - self.data_collection.add_link(link1) - - dialog = LinkEditor(self.data_collection) - dialog.show() - link_widget = dialog.link_widget - - link_widget.state.data1 = self.data1 - link_widget.state.data2 = self.data2 - - link_widget.state.current_link.x = self.data1.id['y'] - - assert link_widget.combos1.itemAtPosition(0, 1).widget().currentText() == 'y' - - add_identity_link = get_action(link_widget, 'identity') - add_identity_link.trigger() - - assert link_widget.listsel_current_link.count() == 2 - - dialog.reject() - - links = self.data_collection.external_links - - assert len(links) == 1 - - assert isinstance(links[0], ComponentLink) - assert links[0].get_from_ids()[0] is self.data1.id['x'] - assert links[0].get_to_id() is self.data2.id['c'] - - def test_functional_link_collection(self): - - # Test that if we use a @link_helper in 'legacy' mode, i.e. with only - # input labels, both datasets are available from the combos in the - # link editor dialog. Also test the new-style @link_helper. - - def deg_arcsec(degree, arcsecond): - return [ComponentLink([degree], arcsecond, using=lambda d: d * 3600), - ComponentLink([arcsecond], degree, using=lambda a: a / 3600)] - - # Old-style link helper - - helper1 = functional_link_collection(deg_arcsec, description='Legacy link', - labels1=['deg', 'arcsec'], labels2=[]) - - link1 = helper1(cids1=[self.data1.id['x'], self.data2.id['c']]) - - self.data_collection.add_link(link1) - - # New-style link helper - - helper2 = functional_link_collection(deg_arcsec, description='New-style link', - labels1=['deg'], labels2=['arcsec']) - - link2 = helper2(cids1=[self.data1.id['x']], cids2=[self.data2.id['c']]) - - self.data_collection.add_link(link2) - - dialog = LinkEditor(self.data_collection) - dialog.show() - link_widget = dialog.link_widget - - assert link_widget.listsel_current_link.count() == 0 - - link_widget.state.data1 = self.data1 - link_widget.state.data2 = self.data2 - - assert link_widget.listsel_current_link.count() == 2 - - assert not link_widget.combos1_header.isVisible() - assert not link_widget.combos2_header.isVisible() - assert link_widget.link_details.text() == 'Legacy link' - assert non_empty_rows_count(link_widget.combos1) == 2 - assert non_empty_rows_count(link_widget.combos2) == 0 - assert link_widget.combos1.itemAtPosition(0, 1).widget().currentText() == 'x' - assert link_widget.combos1.itemAtPosition(1, 1).widget().currentText() == 'c' - - link_widget.state.current_link = type(link_widget.state).current_link.get_choices(link_widget.state)[1] - - assert link_widget.combos1_header.isVisible() - assert link_widget.combos2_header.isVisible() - assert link_widget.link_details.text() == 'New-style link' - assert non_empty_rows_count(link_widget.combos1) == 1 - assert non_empty_rows_count(link_widget.combos2) == 1 - assert link_widget.combos1.itemAtPosition(0, 1).widget().currentText() == 'x' - assert link_widget.combos2.itemAtPosition(0, 1).widget().currentText() == 'c' - - dialog.accept() - - links = self.data_collection.external_links - - assert len(links) == 2 - - assert isinstance(links[0], helper1) - assert links[0].cids1[0] is self.data1.id['x'] - assert links[0].cids1[1] is self.data2.id['c'] - - assert isinstance(links[1], helper2) - assert links[1].cids1[0] is self.data1.id['x'] - assert links[1].cids2[0] is self.data2.id['c'] - - def test_same_data(self): - - # Test that we can't set the same data twice - - dialog = LinkEditor(self.data_collection) - dialog.show() - link_widget = dialog.link_widget - - link_widget.state.data1 = self.data1 - link_widget.state.data2 = self.data2 - - assert link_widget.state.data1 == self.data1 - assert link_widget.state.data2 == self.data2 - - link_widget.state.data1 = self.data2 - - assert link_widget.state.data1 == self.data2 - assert link_widget.state.data2 == self.data1 - - link_widget.state.data2 = self.data2 - - assert link_widget.state.data1 == self.data1 - assert link_widget.state.data2 == self.data2 - - dialog.accept() - - def test_preexisting_links_twodata(self): - - # Regression test for an issue that occurred specifically if there were - # exactly two datasets and pre-existing links (since this means that - # the window opens with a current_link selected by default) - - data1 = Data(x=[1, 2, 3], y=[2, 3, 4], z=[6, 5, 4], label='data1') - data2 = Data(a=[2, 3, 4], b=[4, 5, 4], c=[3, 4, 1], label='data2') - - data_collection = DataCollection([data1, data2]) - - link1 = ComponentLink([data1.id['x']], data2.id['c']) - data_collection.add_link(link1) - - dialog = LinkEditor(data_collection) - dialog.show() - - dialog.accept() - - -class TestLinkEditorForJoins: - - def setup_method(self, method): - - self.data1 = Data(x=['101', '102', '105'], y=[2, 3, 4], z=[6, 5, 4], label='data1') - self.data2 = Data(a=['102', '104', '105'], b=[4, 5, 4], c=[3, 4, 1], label='data2') - - self.data_collection = DataCollection([self.data1, self.data2]) - - def test_make_and_delete_link(self): - # Make sure the dialog opens and closes and check default settings. - dialog = LinkEditor(self.data_collection) - dialog.show() - link_widget = dialog.link_widget - link_widget.state.data1 = self.data1 - link_widget.state.data2 = self.data2 - add_JoinLink = get_action(link_widget, 'Join on ID') - - add_JoinLink.trigger() - # Ensure that all events get processed - # key_joins only happen on dialog.accept() - process_events() - dialog.accept() - - assert len(self.data_collection.links) == 0 - assert len(self.data_collection._link_manager._external_links) == 1 - - assert self.data1._key_joins != {} - assert self.data2._key_joins != {} - - dialog.show() - link_widget = dialog.link_widget - - # With two datasets this will select the current link - assert link_widget.listsel_current_link.count() == 1 - assert link_widget.link_details.text().startswith('Join two datasets') - link_widget.state.current_link.data1 = self.data1 - link_widget.state.current_link.data2 = self.data2 - - link_widget.state.current_link.link_type = 'join' # Not sure why we need to set this in the test - - assert link_widget.state.current_link.link in self.data_collection._link_manager._external_links - assert link_widget.button_remove_link.isEnabled() - - link_widget.button_remove_link.click() - process_events() - - dialog.accept() - assert len(self.data_collection.links) == 0 - assert len(self.data_collection._link_manager._external_links) == 0 - assert self.data1._key_joins == {} - assert self.data2._key_joins == {} diff --git a/glue/dialogs/subset_facet/qt/__init__.py b/glue/dialogs/subset_facet/qt/__init__.py index 747cc2ba5..92878f2e1 100644 --- a/glue/dialogs/subset_facet/qt/__init__.py +++ b/glue/dialogs/subset_facet/qt/__init__.py @@ -1 +1,4 @@ -from .subset_facet import * # noqa +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.dialogs.subset_facet.qt is deprecated, use glue_qt.dialogs.subset_facet instead', GlueDeprecationWarning) +from glue_qt.dialogs.subset_facet import * # noqa diff --git a/glue/dialogs/subset_facet/qt/subset_facet.py b/glue/dialogs/subset_facet/qt/subset_facet.py index 4be1b51d8..b5dd57673 100644 --- a/glue/dialogs/subset_facet/qt/subset_facet.py +++ b/glue/dialogs/subset_facet/qt/subset_facet.py @@ -1,101 +1,4 @@ -import os -import numpy as np -from matplotlib import cm - -from qtpy import QtWidgets -from glue.core.util import facet_subsets -from glue.core.state_objects import State -from echo import CallbackProperty, SelectionCallbackProperty -from glue.utils.qt import load_ui -from glue.core.data_combo_helper import DataCollectionComboHelper, ComponentIDComboHelper -from echo.qt import autoconnect_callbacks_to_qt -from glue.core.state_objects import StateAttributeLimitsHelper - -__all__ = ['SubsetFacetDialog'] - - -class SubsetFacetState(State): - - log = CallbackProperty(False) - v_min = CallbackProperty(0.) - v_max = CallbackProperty(1.) - steps = CallbackProperty(5) - data = SelectionCallbackProperty() - att = SelectionCallbackProperty() - cmap = CallbackProperty() - - def __init__(self, data_collection): - - super(SubsetFacetState, self).__init__() - - self.data_helper = DataCollectionComboHelper(self, 'data', data_collection) - self.att_helper = ComponentIDComboHelper(self, 'att') - self.lim_helper = StateAttributeLimitsHelper(self, attribute='att', - lower='v_min', upper='v_max', - log='log') - - self.add_callback('data', self._on_data_change) - self._on_data_change() - - def _on_data_change(self, *args, **kwargs): - self.att_helper.set_multiple_data([] if self.data is None else [self.data]) - - -class SubsetFacetDialog(QtWidgets.QDialog): - """ - Create a new dialog for subset faceting - - Parameters - ---------- - collect : :class:`~glue.core.data_collection.DataCollection` - The data collection to use - default : :class:`~glue.core.data.Data`, optional - The default dataset in the collection (optional) - """ - - def __init__(self, collect, default=None, parent=None): - - super(SubsetFacetDialog, self).__init__(parent=parent) - - self.state = SubsetFacetState(collect) - - self.ui = load_ui('subset_facet.ui', self, - directory=os.path.dirname(__file__)) - self._connections = autoconnect_callbacks_to_qt(self.state, self.ui) - - self._collect = collect - - if default is not None: - self.state.data = default - - self.state.cmap = cm.RdYlBu - - self.ui.button_ok.clicked.connect(self.accept) - self.ui.button_cancel.clicked.connect(self.reject) - - def _apply(self): - - try: - lo, hi = self.state.v_min, self.state.v_max - except ValueError: - return # limits not set. Abort - - if not np.isfinite(lo) or not np.isfinite(hi): - return - - facet_subsets(self._collect, self.state.att, lo=lo, hi=hi, - steps=self.state.steps, log=self.state.log, - cmap=self.state.cmap) - - @classmethod - def facet(cls, collect, default=None, parent=None): - """ - Class method to create facted subsets. - - The arguments are the same as __init__. - """ - self = cls(collect, parent=parent, default=default) - value = self.exec_() - - if value == QtWidgets.QDialog.Accepted: - self._apply() +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.dialogs.subset_facet.qt.subset_facet is deprecated, use glue_qt.dialogs.subset_facet.subset_facet instead', GlueDeprecationWarning) +from glue_qt.dialogs.subset_facet.subset_facet import * # noqa diff --git a/glue/dialogs/subset_facet/qt/subset_facet.ui b/glue/dialogs/subset_facet/qt/subset_facet.ui deleted file mode 100644 index 50cde1411..000000000 --- a/glue/dialogs/subset_facet/qt/subset_facet.ui +++ /dev/null @@ -1,184 +0,0 @@ - - - SubsetFacet - - - - 0 - 0 - 359 - 411 - - - - Subset Facet - - - - 4 - - - 4 - - - 4 - - - 4 - - - 4 - - - - - Datasets - - - Qt::AlignCenter - - - - - - - QComboBox::AdjustToMinimumContentsLengthWithIcon - - - - - - - Qt::LeftToRight - - - Attributes - - - Qt::AlignCenter - - - - - - - The attributes associated with this data set - - - - - - - - - Number of Subsets - - - - - - - 100 - - - 5 - - - - - - - - - - - Min - - - - - - - - - - Max - - - - - - - - - - Log spacing - - - - - - - - - - - Color Scale - - - - - - - QComboBox::AdjustToMinimumContentsLengthWithIcon - - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Cancel - - - - - - - OK - - - true - - - - - - - - - - QColormapCombo - QComboBox -
glue.utils.qt.colors
-
-
- - -
diff --git a/glue/dialogs/subset_facet/qt/tests/__init__.py b/glue/dialogs/subset_facet/qt/tests/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/glue/dialogs/subset_facet/qt/tests/test_subset_facet.py b/glue/dialogs/subset_facet/qt/tests/test_subset_facet.py deleted file mode 100644 index 18fd7b06f..000000000 --- a/glue/dialogs/subset_facet/qt/tests/test_subset_facet.py +++ /dev/null @@ -1,40 +0,0 @@ -from unittest.mock import patch -from matplotlib import cm - -from glue.core import Data, DataCollection - -from ..subset_facet import SubsetFacetDialog - - -patched_facet = patch('glue.dialogs.subset_facet.qt.subset_facet.facet_subsets') - - -class TestSubsetFacet(object): - - def setup_method(self, method): - d = Data(x=[1, 2, 3]) - dc = DataCollection([d]) - self.collect = dc - self.s = dc.new_subset_group() - - def test_limits(self): - s = SubsetFacetDialog(self.collect) - s.state.data = self.collect[0] - s.state.att = self.collect[0].id['x'] - assert s.state.v_min == 1 - assert s.state.v_max == 3 - - def test_get_set_cmap(self): - s = SubsetFacetDialog(self.collect) - assert s.state.cmap is cm.RdYlBu - - def test_apply(self): - with patched_facet as p: - s = SubsetFacetDialog(self.collect) - s.state.data = self.collect[0] - s.state.att = self.collect[0].id['x'] - s._apply() - p.assert_called_once_with(self.collect, s.state.att, - lo=1, hi=3, - steps=5, log=False, - cmap=cm.RdYlBu) diff --git a/glue/external/echo/qt/__init__.py b/glue/external/echo/qt/__init__.py deleted file mode 100644 index 8f3703cac..000000000 --- a/glue/external/echo/qt/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from echo.qt import * diff --git a/glue/external/echo/qt/autoconnect.py b/glue/external/echo/qt/autoconnect.py deleted file mode 100644 index ac119467a..000000000 --- a/glue/external/echo/qt/autoconnect.py +++ /dev/null @@ -1,2 +0,0 @@ -from echo.qt.autoconnect import * -from echo.qt.autoconnect import HANDLERS diff --git a/glue/external/echo/qt/connect.py b/glue/external/echo/qt/connect.py deleted file mode 100644 index 454aea303..000000000 --- a/glue/external/echo/qt/connect.py +++ /dev/null @@ -1,2 +0,0 @@ -from echo.qt.connect import * -from echo.qt.connect import _find_combo_data, UserDataWrapper diff --git a/glue/icons/qt/__init__.py b/glue/icons/qt/__init__.py index d1a95bd64..95a08bec4 100644 --- a/glue/icons/qt/__init__.py +++ b/glue/icons/qt/__init__.py @@ -1 +1,4 @@ -from .helpers import * # noqa +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.icons.qt is deprecated, use glue_qt.icons instead', GlueDeprecationWarning) +from glue_qt.icons import * # noqa diff --git a/glue/icons/qt/helpers.py b/glue/icons/qt/helpers.py index d43e13c5b..46996b1d5 100644 --- a/glue/icons/qt/helpers.py +++ b/glue/icons/qt/helpers.py @@ -1,79 +1,4 @@ -from qtpy import QtGui - -from matplotlib.colors import Colormap - -from glue.utils.qt import mpl_to_qt_color, tint_pixmap, cmap2pixmap -from glue.icons import icon_path - -__all__ = ['symbol_icon', 'layer_icon', 'layer_artist_icon', 'get_icon', 'POINT_ICONS'] - -POINT_ICONS = {'o': 'glue_circle_point', - 's': 'glue_box_point', - '^': 'glue_triangle_up', - '*': 'glue_star', - '+': 'glue_cross'} - - -def symbol_icon(symbol, color=None): - bm = QtGui.QBitmap(icon_path(POINT_ICONS.get(symbol, 'glue_circle'))) - - if color is not None: - return QtGui.QIcon(tint_pixmap(bm, color)) - - return QtGui.QIcon(bm) - - -def layer_icon(layer): - """Create a QtGui.QIcon for a Data or Subset instance - - :type layer: :class:`~glue.core.data.Data`, - :class:`~glue.core.subset.Subset`, - or object with a .style attribute - - :rtype: QtGui.QIcon - """ - icon = POINT_ICONS.get(layer.style.marker, 'circle_point') - bm = QtGui.QBitmap(icon_path(icon)) - color = mpl_to_qt_color(layer.style.color) - pm = tint_pixmap(bm, color) - return QtGui.QIcon(pm) - - -def layer_artist_icon(artist): - """Create a QtGui.QIcon for a LayerArtist instance""" - - # TODO: need a test for this - - from glue.viewers.scatter.layer_artist import ScatterLayerArtist - - color = artist.get_layer_color() - - if isinstance(color, Colormap): - pm = cmap2pixmap(color) - else: - if isinstance(artist, ScatterLayerArtist): - bm = QtGui.QBitmap(icon_path(POINT_ICONS.get(artist.layer.style.marker, - 'glue_circle_point'))) - else: - bm = QtGui.QBitmap(icon_path('glue_box_point')) - color = mpl_to_qt_color(color) - pm = tint_pixmap(bm, color) - - return QtGui.QIcon(pm) - - -def get_icon(icon_name): - """ - Build a QtGui.QIcon from an image name - - Parameters - ---------- - icon_name : str - Name of image file. Assumed to be a png file in glue/qt/icons - Do not include the extension - - Returns - ------- - A QtGui.QIcon object - """ - return QtGui.QIcon(icon_path(icon_name)) +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.icons.qt.helpers is deprecated, use glue_qt.icons.helpers instead', GlueDeprecationWarning) +from glue_qt.icons.helpers import * # noqa diff --git a/glue/io/qt/__init__.py b/glue/io/qt/__init__.py index e69de29bb..a40e02085 100644 --- a/glue/io/qt/__init__.py +++ b/glue/io/qt/__init__.py @@ -0,0 +1,4 @@ +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.io.qt is deprecated, use glue_qt.io instead', GlueDeprecationWarning) +from glue_qt.io import * # noqa diff --git a/glue/io/qt/directory_importer/__init__.py b/glue/io/qt/directory_importer/__init__.py index 338417b0b..ce7679906 100644 --- a/glue/io/qt/directory_importer/__init__.py +++ b/glue/io/qt/directory_importer/__init__.py @@ -1,2 +1,4 @@ -def setup(): - from . import directory_importer # noqa +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.io.qt.directory_importer is deprecated, use glue_qt.io.directory_importer instead', GlueDeprecationWarning) +from glue_qt.io.directory_importer import * # noqa diff --git a/glue/io/qt/directory_importer/directory_importer.py b/glue/io/qt/directory_importer/directory_importer.py index 2c8582989..440576b4f 100644 --- a/glue/io/qt/directory_importer/directory_importer.py +++ b/glue/io/qt/directory_importer/directory_importer.py @@ -1,7 +1,4 @@ -from glue.config import importer -from glue.dialogs.data_wizard.qt import data_wizard - - -@importer("Import from directory") -def directory_importer(): - return data_wizard(mode='directories') +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.io.qt.directory_importer.directory_importer is deprecated, use glue_qt.io.directory_importer.directory_importer instead', GlueDeprecationWarning) +from glue_qt.io.directory_importer.directory_importer import * # noqa diff --git a/glue/io/qt/subset_mask.py b/glue/io/qt/subset_mask.py index 24e3bc239..98327dfda 100644 --- a/glue/io/qt/subset_mask.py +++ b/glue/io/qt/subset_mask.py @@ -1,65 +1,4 @@ -from qtpy import compat -from glue import config -from glue.utils.qt import messagebox_on_error -from glue.io.subset_mask import SubsetMaskImporter, SubsetMaskExporter - -__all__ = ['QtSubsetMaskImporter', 'QtSubsetMaskExporter'] - - -def _make_filters_dict(registry): - - filters = {} - for e in registry: - if e.extension == '': - fltr = "{0} (*)".format(e.label) - else: - fltr = "{0} ({1})".format(e.label, ' '.join('*.' + ext for ext in e.extension)) - filters[fltr] = e.function - - return filters - - -class QtSubsetMaskImporter(SubsetMaskImporter): - - def get_filename_and_reader(self): - - subset_mask_importers = _make_filters_dict(config.subset_mask_importer) - - filters = ';;'.join(sorted(subset_mask_importers)) - - filename, fltr = compat.getopenfilename(caption="Choose a subset mask file", - filters=filters) - - filename = str(filename) - - if filename: - return filename, subset_mask_importers[fltr] - else: - return None, None - - @messagebox_on_error('An error occurred when importing the subset mask file:', sep=' ') - def run(self, data_or_subset, data_collection): - super(QtSubsetMaskImporter, self).run(data_or_subset, data_collection) - - -class QtSubsetMaskExporter(SubsetMaskExporter): - - def get_filename_and_writer(self): - - subset_mask_exporters = _make_filters_dict(config.subset_mask_exporter) - - filters = ';;'.join(sorted(subset_mask_exporters)) - - filename, fltr = compat.getsavefilename(caption="Choose a subset mask filename", - filters=filters) - - filename = str(filename) - - if filename: - return filename, subset_mask_exporters[fltr] - else: - return None, None - - @messagebox_on_error('An error occurred when exporting the subset mask:', sep=' ') - def run(self, data_or_subset): - super(QtSubsetMaskExporter, self).run(data_or_subset) +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.io.qt.subset_mask is deprecated, use glue_qt.io.subset_mask instead', GlueDeprecationWarning) +from glue_qt.io.subset_mask import * # noqa diff --git a/glue/io/qt/tests/__init__.py b/glue/io/qt/tests/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/glue/io/qt/tests/test_subset_mask.py b/glue/io/qt/tests/test_subset_mask.py deleted file mode 100644 index 0e4b469b7..000000000 --- a/glue/io/qt/tests/test_subset_mask.py +++ /dev/null @@ -1,81 +0,0 @@ -import os -from unittest.mock import patch - -from numpy.testing import assert_equal -from astropy.io import fits - -from glue.core import DataCollection, Data -from glue.io.qt.subset_mask import QtSubsetMaskImporter, QtSubsetMaskExporter - - -def test_importer(tmpdir): - - filename = tmpdir.join('test.fits').strpath - - hdu = fits.PrimaryHDU(data=[0, 1, 1]) - hdu.writeto(filename) - - data = Data(x=[1, 2, 3]) - data_collection = DataCollection([data]) - - with patch('qtpy.compat.getopenfilename') as o: - o.return_value = filename, 'FITS (*.fits *.fit *.fits.gz *.fit.gz)' - importer = QtSubsetMaskImporter() - importer.run(data, data_collection) - - assert_equal(data.subsets[0].to_mask(), [0, 1, 1]) - - -def test_importer_cancel(tmpdir): - - filename = tmpdir.join('test.fits').strpath - - hdu = fits.PrimaryHDU(data=[0, 1, 1]) - hdu.writeto(filename) - - data = Data(x=[1, 2, 3]) - data_collection = DataCollection([data]) - - with patch('qtpy.compat.getopenfilename') as o: - o.return_value = '', '' # simulates cancelling - importer = QtSubsetMaskImporter() - importer.run(data, data_collection) - - assert len(data_collection.subset_groups) == 0 - assert len(data.subsets) == 0 - - -def test_exporter(tmpdir): - - filename = tmpdir.join('test.fits').strpath - - data = Data(x=[1, 2, 3]) - data_collection = DataCollection([data]) - data_collection.new_subset_group(subset_state=data.id['x'] >= 2, label='subset a') - - with patch('qtpy.compat.getsavefilename') as o: - o.return_value = filename, 'FITS (*.fits *.fit *.fits.gz *.fit.gz)' - exporter = QtSubsetMaskExporter() - exporter.run(data) - - with fits.open(filename) as hdulist: - assert len(hdulist) == 2 - assert hdulist[0].data is None - assert hdulist[1].name == 'SUBSET A' - assert_equal(hdulist[1].data, [0, 1, 1]) - - -def test_exporter_cancel(tmpdir): - - filename = tmpdir.join('test.fits').strpath - - data = Data(x=[1, 2, 3]) - data_collection = DataCollection([data]) - data_collection.new_subset_group(subset_state=data.id['x'] >= 2, label='subset a') - - with patch('qtpy.compat.getsavefilename') as o: - o.return_value = '', '' # simulates cancelling - exporter = QtSubsetMaskExporter() - exporter.run(data) - - assert not os.path.exists(filename) diff --git a/glue/main.py b/glue/main.py index 8fcc896c2..dc566e1cf 100755 --- a/glue/main.py +++ b/glue/main.py @@ -1,258 +1,10 @@ #!/usr/bin/env python -import sys -import optparse from importlib import import_module -from glue import __version__ from glue.logger import logger -def parse(argv): - """ Parse argument list, check validity - - :param argv: Arguments passed to program - - *Returns* - A tuple of options, position arguments - """ - usage = """usage: %prog [options] [FILE FILE...] - - # start a new session - %prog - - # start a new session and load a file - %prog image.fits - - #start a new session with multiple files - %prog image.fits catalog.csv - - #restore a saved session - %prog saved_session.glu - or - %prog -g saved_session.glu - - #run a script - %prog -x script.py - - #run the test suite - %prog -t - """ - parser = optparse.OptionParser(usage=usage, - version=str(__version__)) - - parser.add_option('-x', '--execute', action='store_true', dest='script', - help="Execute FILE as a python script", default=False) - parser.add_option('-g', action='store_true', dest='restore', - help="Restore glue session from FILE", default=False) - parser.add_option('-t', '--test', action='store_true', dest='test', - help="Run test suite", default=False) - parser.add_option('-c', '--config', type='string', dest='config', - metavar='CONFIG', - help='use CONFIG as configuration file') - parser.add_option('-v', '--verbose', action='store_true', - help="Increase the vebosity level", default=False) - parser.add_option('--no-maximized', action='store_true', dest='nomax', - help="Do not start Glue maximized", default=False) - parser.add_option('--startup', dest='startup', type='string', - help="Startup actions to carry out", default='') - parser.add_option('--auto-merge', dest='auto_merge', action='store_true', - help="Automatically merge any data passed on the command-line", default='') - parser.add_option('--faulthandler', dest='faulthandler', action='store_true', - help="Run glue with the built-in faulthandler to debug segmentation faults", default=False) - - err_msg = verify(parser, argv) - if err_msg: - sys.stderr.write('\n%s\n' % err_msg) - parser.print_help() - sys.exit(1) - - return parser.parse_args(argv) - - -def verify(parser, argv): - """ Check for input errors - - :param parser: OptionParser instance - :param argv: Argument list - :type argv: List of strings - - *Returns* - An error message, or None - """ - opts, args = parser.parse_args(argv) - err_msg = None - - if opts.script and opts.restore: - err_msg = "Cannot specify -g with -x" - elif opts.script and opts.config: - err_msg = "Cannot specify -c with -x" - elif opts.script and len(args) != 1: - err_msg = "Must provide a script\n" - elif opts.restore and len(args) != 1: - err_msg = "Must provide a .glu file\n" - - return err_msg - - -def load_data_files(datafiles): - """Load data files and return a list of datasets""" - - from glue.core.data_factories import load_data - - datasets = [] - for df in datafiles: - datasets.append(load_data(df)) - - return datasets - - -def run_tests(): - from glue import test - test() - - -def start_glue(gluefile=None, config=None, datafiles=None, maximized=True, - startup_actions=None, auto_merge=False): - """Run a glue session and exit - - Parameters - ---------- - gluefile : str - An optional ``.glu`` file to restore. - config : str - An optional configuration file to use. - datafiles : str - An optional list of data files to load. - maximized : bool - Maximize screen on startup. Otherwise, use default size. - auto_merge : bool, optional - Whether to automatically merge data passed in `datafiles` (default is `False`) - """ - - import glue - - # Some Qt modules are picky in terms of being imported before the - # application is set up, so we import them here. We do it here rather - # than in get_qapp since in the past, including this code in get_qapp - # caused severe issues (e.g. segmentation faults) in plugin packages - # during testing. - try: - from qtpy import QtWebEngineWidgets # noqa - except ImportError: # Not all PyQt installations have this module - pass - - from glue.utils.qt.decorators import die_on_error - - from glue.utils.qt import get_qapp - app = get_qapp() - - splash = get_splash() - splash.show() - - # Start off by loading plugins. We need to do this before restoring - # the session or loading the configuration since these may use existing - # plugins. - load_plugins(splash=splash, require_qt_plugins=True) - - from glue.app.qt import GlueApplication - - datafiles = datafiles or [] - - hub = None - - splash.close() - - if gluefile is not None: - with die_on_error("Error restoring Glue session"): - app = GlueApplication.restore_session(gluefile, show=False) - return app.start(maximized=maximized) - - if config is not None: - glue.env = glue.config.load_configuration(search_path=[config]) - - data_collection = glue.core.DataCollection() - hub = data_collection.hub - - splash.set_progress(100) - - session = glue.core.Session(data_collection=data_collection, hub=hub) - ga = GlueApplication(session=session) - - if datafiles: - with die_on_error("Error reading data file"): - datasets = load_data_files(datafiles) - ga.add_datasets(datasets, auto_merge=auto_merge) - - if startup_actions is not None: - for name in startup_actions: - ga.run_startup_action(name) - - return ga.start(maximized=maximized) - - -def execute_script(script): - """ Run a python script and exit. - - Provides a way for people with pre-installed binaries to use - the glue library - """ - from glue.utils.qt.decorators import die_on_error - with die_on_error("Error running script"): - with open(script) as fin: - exec(fin.read()) - sys.exit(0) - - -def get_splash(): - """Instantiate a splash screen""" - from glue.app.qt.splash_screen import QtSplashScreen - splash = QtSplashScreen() - return splash - - -def main(argv=sys.argv): - - opt, args = parse(argv[1:]) - - if opt.verbose: - logger.setLevel("INFO") - - if opt.faulthandler: - import faulthandler - faulthandler.enable() - - logger.info("Input arguments: %s", sys.argv) - - # Global keywords for Glue startup. - kwargs = {'config': opt.config, - 'maximized': not opt.nomax, - 'auto_merge': opt.auto_merge} - - if opt.startup: - kwargs['startup_actions'] = opt.startup.split(',') - - if opt.test: - return run_tests() - elif opt.restore: - start_glue(gluefile=args[0], **kwargs) - elif opt.script: - execute_script(args[0]) - else: - has_file = len(args) == 1 - has_files = len(args) > 1 - has_py = has_file and args[0].endswith('.py') - has_glu = has_file and args[0].endswith('.glu') - if has_py: - execute_script(args[0]) - elif has_glu: - start_glue(gluefile=args[0], **kwargs) - elif has_file or has_files: - start_glue(datafiles=args, **kwargs) - else: - start_glue(**kwargs) - - _loaded_plugins = set() _installed_plugins = set() @@ -350,7 +102,3 @@ def load_plugins(splash=None, require_qt_plugins=False): # that were previously read. from glue._settings_helpers import load_settings load_settings() - - -if __name__ == "__main__": - sys.exit(main(sys.argv)) # pragma: no cover diff --git a/glue/plugins/dendro_viewer/qt/__init__.py b/glue/plugins/dendro_viewer/qt/__init__.py index eacbdc65d..ba953ec70 100644 --- a/glue/plugins/dendro_viewer/qt/__init__.py +++ b/glue/plugins/dendro_viewer/qt/__init__.py @@ -1,6 +1,4 @@ -from .data_viewer import DendrogramViewer # noqa - - -def setup(): - from glue.config import qt_client - qt_client.add(DendrogramViewer) +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.plugins.dendro_viewer.qt is deprecated, use glue_qt.plugins.dendro_viewer instead', GlueDeprecationWarning) +from glue_qt.plugins.dendro_viewer import * # noqa diff --git a/glue/plugins/dendro_viewer/qt/data_viewer.py b/glue/plugins/dendro_viewer/qt/data_viewer.py index 246db770d..52478e644 100644 --- a/glue/plugins/dendro_viewer/qt/data_viewer.py +++ b/glue/plugins/dendro_viewer/qt/data_viewer.py @@ -1,142 +1,4 @@ -import numpy as np - -from glue.core.roi import PointROI -from glue.core.subset import CategorySubsetState -from glue.core.exceptions import IncompatibleDataException -from glue.utils import defer_draw -from glue.utils.qt import messagebox_on_error - -from glue.plugins.dendro_viewer.dendro_helpers import _substructures -from glue.viewers.matplotlib.qt.data_viewer import MatplotlibDataViewer -from glue.plugins.dendro_viewer.layer_artist import DendrogramLayerArtist -from glue.plugins.dendro_viewer.qt.options_widget import DendrogramOptionsWidget -from glue.plugins.dendro_viewer.state import DendrogramViewerState -from glue.plugins.dendro_viewer.qt.layer_style_editor import DendrogramLayerStyleEditor -from glue.plugins.dendro_viewer.compat import update_dendrogram_viewer_state - -__all__ = ['DendrogramViewer'] - - -class DendrogramViewer(MatplotlibDataViewer): - - LABEL = 'Dendrogram' - - _layer_style_widget_cls = DendrogramLayerStyleEditor - _state_cls = DendrogramViewerState - _options_cls = DendrogramOptionsWidget - _data_artist_cls = DendrogramLayerArtist - _subset_artist_cls = DendrogramLayerArtist - - tools = ['select:pick'] - - def __init__(self, *args, **kwargs): - super(DendrogramViewer, self).__init__(*args, **kwargs) - self.axes.set_xticks([]) - self.axes.spines['top'].set_visible(False) - self.axes.spines['bottom'].set_visible(False) - self.state.add_callback('_layout', self._update_limits) - self._update_limits() - - def _update_limits(self, layout=None): - - if self.state._layout is None: - return - - x, y = self.state._layout.xy - x, y = x[::3], y[::3] - xlim = np.array([x.min(), x.max()]) - xpad = .05 * xlim.ptp() - xlim[0] -= xpad - xlim[1] += xpad - - ylim = np.array([y.min(), y.max()]) - if self.state.y_log: - ylim = np.maximum(ylim, 1e-5) - pad = 1.05 * ylim[1] / ylim[0] - ylim[0] /= pad - ylim[1] *= pad - else: - pad = .05 * ylim.ptp() - ylim[0] -= pad - ylim[1] += pad - - self.axes.set_xlim(*xlim) - self.axes.set_ylim(*ylim) - - def initialize_toolbar(self): - - super(DendrogramViewer, self).initialize_toolbar() - - def on_move(mode): - if mode._drag: - self.apply_roi(mode.roi()) - - self.toolbar.tools['select:pick']._move_callback = on_move - - def close(self, *args, **kwargs): - self.toolbar.tools['select:pick']._move_callback = None - super(DendrogramViewer, self).close(*args, **kwargs) - - @messagebox_on_error('Failed to add data') - def add_data(self, data): - if data.ndim != 1: - raise IncompatibleDataException("Only 1-D data can be added to " - "the dendrogram viewer (tried to add a {}-D " - "dataset)".format(data.ndim)) - return super(DendrogramViewer, self).add_data(data) - - # TODO: move some of the ROI stuff to state class? - - @defer_draw - def apply_roi(self, roi, override_mode=None): - - # Force redraw to get rid of ROI. We do this because applying the - # subset state below might end up not having an effect on the viewer, - # for example there may not be any layers, or the active subset may not - # be one of the layers. So we just explicitly redraw here to make sure - # a redraw will happen after this method is called. - self.redraw() - - # TODO Does subset get applied to all data or just visible data? - - if self.state._layout is None: - return - - if not roi.defined(): - return - - if len(self.layers) == 0: - return - - if isinstance(roi, PointROI): - - x, y = roi.x, roi.y - - xs, ys = self.state._layout.xy - parent_ys = ys[1::3] - xs, ys = xs[::3], ys[::3] - - delt = np.abs(x - xs) - delt[y > ys] = np.nan - delt[y < parent_ys] = np.nan - - if np.isfinite(delt).any(): - select = np.nanargmin(delt) - if self.state.select_substruct: - parent = self.state.reference_data[self.state.parent_att] - select = _substructures(parent, select) - select = np.asarray(select, dtype=int) - else: - select = np.array([], dtype=int) - - subset_state = CategorySubsetState(self.state.reference_data.pixel_component_ids[0], select) - - self.apply_subset_state(subset_state) - - else: - - raise TypeError("Only PointROI selections are supported") - - @staticmethod - def update_viewer_state(rec, context): - return update_dendrogram_viewer_state(rec, context) +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.plugins.dendro_viewer.qt.data_viewer is deprecated, use glue_qt.plugins.dendro_viewer.data_viewer instead', GlueDeprecationWarning) +from glue_qt.plugins.dendro_viewer.data_viewer import * # noqa diff --git a/glue/plugins/dendro_viewer/qt/layer_style_editor.py b/glue/plugins/dendro_viewer/qt/layer_style_editor.py index 55df739b7..c72401393 100644 --- a/glue/plugins/dendro_viewer/qt/layer_style_editor.py +++ b/glue/plugins/dendro_viewer/qt/layer_style_editor.py @@ -1,20 +1,4 @@ -import os - -from qtpy import QtWidgets - -from echo.qt import autoconnect_callbacks_to_qt -from glue.utils.qt import load_ui - - -class DendrogramLayerStyleEditor(QtWidgets.QWidget): - - def __init__(self, layer, parent=None): - - super(DendrogramLayerStyleEditor, self).__init__(parent=parent) - - self.ui = load_ui('layer_style_editor.ui', self, - directory=os.path.dirname(__file__)) - - connect_kwargs = {'alpha': dict(value_range=(0, 1))} - - self._connections = autoconnect_callbacks_to_qt(layer.state, self.ui, connect_kwargs) +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.plugins.dendro_viewer.qt.layer_style_editor is deprecated, use glue_qt.plugins.dendro_viewer.layer_style_editor instead', GlueDeprecationWarning) +from glue_qt.plugins.dendro_viewer.layer_style_editor import * # noqa diff --git a/glue/plugins/dendro_viewer/qt/layer_style_editor.ui b/glue/plugins/dendro_viewer/qt/layer_style_editor.ui deleted file mode 100644 index 87ccf9081..000000000 --- a/glue/plugins/dendro_viewer/qt/layer_style_editor.ui +++ /dev/null @@ -1,146 +0,0 @@ - - - Form - - - - 0 - 0 - 127 - 107 - - - - Form - - - - 5 - - - 5 - - - 5 - - - 5 - - - 10 - - - 5 - - - - - 100 - - - Qt::Horizontal - - - - - - - - 75 - true - - - - opacity - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - 0 - 0 - - - - - - - - - - - - 75 - true - - - - color - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - - 75 - true - - - - width - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - Qt::Horizontal - - - - 40 - 1 - - - - - - - - - QColorBox - QLabel -
glue.utils.qt.colors
-
-
- - -
diff --git a/glue/plugins/dendro_viewer/qt/options_widget.py b/glue/plugins/dendro_viewer/qt/options_widget.py index 965e56855..1d76ee37a 100644 --- a/glue/plugins/dendro_viewer/qt/options_widget.py +++ b/glue/plugins/dendro_viewer/qt/options_widget.py @@ -1,25 +1,4 @@ -import os - -from qtpy import QtWidgets - -from echo.qt import autoconnect_callbacks_to_qt -from glue.utils.qt import load_ui - -__all__ = ['DendrogramOptionsWidget'] - - -class DendrogramOptionsWidget(QtWidgets.QWidget): - - def __init__(self, viewer_state, session, parent=None): - - super(DendrogramOptionsWidget, self).__init__(parent=parent) - - self.ui = load_ui('options_widget.ui', self, - directory=os.path.dirname(__file__)) - - self._connections = autoconnect_callbacks_to_qt(viewer_state, self.ui) - - self.viewer_state = viewer_state - - def reset_limits(self): - self.viewer_state.reset_limits() +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.plugins.dendro_viewer.qt.options_widget is deprecated, use glue_qt.plugins.dendro_viewer.options_widget instead', GlueDeprecationWarning) +from glue_qt.plugins.dendro_viewer.options_widget import * # noqa diff --git a/glue/plugins/dendro_viewer/qt/options_widget.ui b/glue/plugins/dendro_viewer/qt/options_widget.ui deleted file mode 100644 index c5ba8d949..000000000 --- a/glue/plugins/dendro_viewer/qt/options_widget.ui +++ /dev/null @@ -1,133 +0,0 @@ - - - DendroWidgetPanel - - - - 0 - 0 - 251 - 140 - - - - Form - - - - - - QComboBox::AdjustToMinimumContentsLengthWithIcon - - - - - - - - 75 - true - - - - parent - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - y log - - - true - - - - - - - Qt::Horizontal - - - - 40 - 1 - - - - - - - - - 75 - true - - - - sort by - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - 75 - true - - - - height - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - QComboBox::AdjustToMinimumContentsLengthWithIcon - - - - - - - QComboBox::AdjustToMinimumContentsLengthWithIcon - - - - - - - auto select substructures - - - - - - - - diff --git a/glue/plugins/dendro_viewer/qt/tests/__init__.py b/glue/plugins/dendro_viewer/qt/tests/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/glue/plugins/dendro_viewer/qt/tests/data/__init__.py b/glue/plugins/dendro_viewer/qt/tests/data/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/glue/plugins/dendro_viewer/qt/tests/data/dendro_v0.glu b/glue/plugins/dendro_viewer/qt/tests/data/dendro_v0.glu deleted file mode 100644 index 77395b89f..000000000 --- a/glue/plugins/dendro_viewer/qt/tests/data/dendro_v0.glu +++ /dev/null @@ -1,257 +0,0 @@ -{ - "CategorySubsetState": { - "_type": "glue.core.subset.CategorySubsetState", - "att": "Pixel Axis 0 [x]", - "vals": { - "_type": "numpy.ndarray", - "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGk4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDEsKSwgfSAgICAgICAgICAgIAoCAAAAAAAAAA==" - } - }, - "Component": { - "_type": "glue.core.component.Component", - "data": { - "_type": "numpy.ndarray", - "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGY4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDQsKSwgfSAgICAgICAgICAgIArNzMzMzMz0P5qZmZmZmQFAmpmZmZmZCUCamZmZmZkRQA==" - }, - "units": "" - }, - "Component_0": { - "_type": "glue.core.component.Component", - "data": { - "_type": "numpy.ndarray", - "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGk4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDQsKSwgfSAgICAgICAgICAgIAr//////////wAAAAAAAAAAAQAAAAAAAAABAAAAAAAAAA==" - }, - "units": "" - }, - "CoordinateComponent": { - "_type": "glue.core.component.CoordinateComponent", - "axis": 0, - "world": false - }, - "CoordinateComponentLink": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "World 0" - ], - "index": 0, - "pix2world": false, - "to": [ - "Pixel Axis 0 [x]" - ] - }, - "CoordinateComponentLink_0": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "Pixel Axis 0 [x]" - ], - "index": 0, - "pix2world": true, - "to": [ - "World 0" - ] - }, - "CoordinateComponent_0": { - "_type": "glue.core.component.CoordinateComponent", - "axis": 0, - "world": true - }, - "Coordinates": { - "_type": "glue.core.coordinates.Coordinates" - }, - "DataCollection": { - "_protocol": 3, - "_type": "glue.core.data_collection.DataCollection", - "cids": [ - "height", - "Pixel Axis 0 [x]", - "World 0", - "parent" - ], - "components": [ - "Component", - "CoordinateComponent", - "CoordinateComponent_0", - "Component_0" - ], - "data": [ - "data" - ], - "groups": [ - "Subset 1_0" - ], - "links": [ - "CoordinateComponentLink", - "CoordinateComponentLink_0" - ], - "subset_group_count": 1 - }, - "DendroWidget": { - "_type": "glue.plugins.dendro_viewer.qt.viewer_widget.DendroWidget", - "layers": [ - { - "_type": "glue.plugins.dendro_viewer.layer_artist.DendroLayerArtist", - "layer": "data", - "visible": true, - "zorder": 1 - }, - { - "_type": "glue.plugins.dendro_viewer.layer_artist.DendroLayerArtist", - "layer": "Subset 1", - "visible": true, - "zorder": 2 - } - ], - "pos": [ - 0, - 0 - ], - "properties": { - "height": "height", - "order": "height", - "parent": "parent", - "ylog": false - }, - "session": "Session", - "size": [ - 640, - 480 - ] - }, - "Pixel Axis 0 [x]": { - "_type": "glue.core.component_id.PixelComponentID", - "axis": 0, - "hidden": true, - "label": "Pixel Axis 0 [x]" - }, - "Session": { - "_type": "glue.core.session.Session" - }, - "Subset 1": { - "_type": "glue.core.subset_group.GroupedSubset", - "group": "Subset 1_0", - "style": { - "_type": "glue.core.visual.VisualAttributes", - "alpha": 0.5, - "color": "#e31a1c", - "linestyle": "solid", - "linewidth": 1, - "marker": "o", - "markersize": 7 - } - }, - "Subset 1_0": { - "_type": "glue.core.subset_group.SubsetGroup", - "label": "Subset 1", - "state": "CategorySubsetState", - "style": { - "_type": "glue.core.visual.VisualAttributes", - "alpha": 0.5, - "color": "#e31a1c", - "linestyle": "solid", - "linewidth": 1, - "marker": "o", - "markersize": 7 - }, - "subsets": [ - "Subset 1" - ] - }, - "World 0": { - "_type": "glue.core.component_id.ComponentID", - "hidden": true, - "label": "World 0" - }, - "__main__": { - "_type": "glue.app.qt.application.GlueApplication", - "data": "DataCollection", - "plugins": [ - "glue_exp.tools.floodfill_selection", - "glue_vispy_viewers.volume", - "glue_exp.tools.contour_selection", - "glue.plugins.coordinate_helpers", - "glue_samp", - "glue.core.data_exporters", - "glue.plugins.export_d3po", - "glue_vispy_viewers.scatter", - "glue_aladin", - "glue.viewers.table", - "glue_exp.importers.webcam", - "glue.plugins.tools.spectrum_tool", - "glue.plugins.tools.pv_slicer", - "glue.plugins.exporters.plotly", - "glue.viewers.image", - "glue.io.formats.fits", - "glue.viewers.scatter", - "glue_exp.tools.zoom_buttons", - "glue_exp.importers.vizier", - "glue_wwt", - "glue.viewers.histogram", - "glue.plugins.dendro_viewer" - ], - "session": "Session", - "tab_names": [ - "Tab 1" - ], - "viewers": [ - [ - "DendroWidget" - ] - ] - }, - "data": { - "_key_joins": [], - "_protocol": 5, - "_type": "glue.core.data.Data", - "components": [ - [ - "height", - "Component" - ], - [ - "Pixel Axis 0 [x]", - "CoordinateComponent" - ], - [ - "World 0", - "CoordinateComponent_0" - ], - [ - "parent", - "Component_0" - ] - ], - "coords": "Coordinates", - "label": "data", - "primary_owner": [ - "height", - "Pixel Axis 0 [x]", - "World 0", - "parent" - ], - "style": { - "_type": "glue.core.visual.VisualAttributes", - "alpha": 0.78, - "color": "#404040", - "linestyle": "solid", - "linewidth": 1, - "marker": "o", - "markersize": 3 - }, - "subsets": [ - "Subset 1" - ], - "uuid": "50d692d5-e30f-4218-aa60-393ced074298" - }, - "height": { - "_type": "glue.core.component_id.ComponentID", - "hidden": false, - "label": "height" - }, - "parent": { - "_type": "glue.core.component_id.ComponentID", - "hidden": false, - "label": "parent" - } -} \ No newline at end of file diff --git a/glue/plugins/dendro_viewer/qt/tests/data/dendro_v1.glu b/glue/plugins/dendro_viewer/qt/tests/data/dendro_v1.glu deleted file mode 100644 index ec4bf9d9b..000000000 --- a/glue/plugins/dendro_viewer/qt/tests/data/dendro_v1.glu +++ /dev/null @@ -1,320 +0,0 @@ -{ - "CallbackList": { - "_type": "glue.external.echo.list.CallbackList", - "values": [ - "DendrogramLayerState", - "DendrogramLayerState_0" - ] - }, - "CategorySubsetState": { - "_type": "glue.core.subset.CategorySubsetState", - "att": "Pixel Axis 0 [x]", - "vals": { - "_type": "numpy.ndarray", - "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGk4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDEsKSwgfSAgICAgICAgICAgIAoCAAAAAAAAAA==" - } - }, - "Component": { - "_type": "glue.core.component.Component", - "data": { - "_type": "numpy.ndarray", - "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGY4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDQsKSwgfSAgICAgICAgICAgIArNzMzMzMz0P5qZmZmZmQFAmpmZmZmZCUCamZmZmZkRQA==" - }, - "units": "" - }, - "Component_0": { - "_type": "glue.core.component.Component", - "data": { - "_type": "numpy.ndarray", - "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGk4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDQsKSwgfSAgICAgICAgICAgIAr//////////wAAAAAAAAAAAQAAAAAAAAABAAAAAAAAAA==" - }, - "units": "" - }, - "CoordinateComponent": { - "_type": "glue.core.component.CoordinateComponent", - "axis": 0, - "world": false - }, - "CoordinateComponentLink": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "Pixel Axis 0 [x]" - ], - "index": 0, - "pix2world": true, - "to": [ - "World 0" - ] - }, - "CoordinateComponentLink_0": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "World 0" - ], - "index": 0, - "pix2world": false, - "to": [ - "Pixel Axis 0 [x]" - ] - }, - "CoordinateComponentLink_1": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "Pixel Axis 0 [x]" - ], - "index": 0, - "pix2world": true, - "to": [ - "World 0" - ] - }, - "CoordinateComponentLink_2": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "World 0" - ], - "index": 0, - "pix2world": false, - "to": [ - "Pixel Axis 0 [x]" - ] - }, - "CoordinateComponent_0": { - "_type": "glue.core.component.CoordinateComponent", - "axis": 0, - "world": true - }, - "Coordinates": { - "_type": "glue.core.coordinates.Coordinates" - }, - "DataCollection": { - "_protocol": 3, - "_type": "glue.core.data_collection.DataCollection", - "cids": [ - "height", - "Pixel Axis 0 [x]", - "World 0", - "parent" - ], - "components": [ - "Component", - "CoordinateComponent", - "CoordinateComponent_0", - "Component_0" - ], - "data": [ - "data" - ], - "groups": [ - "Subset 1" - ], - "links": [ - "CoordinateComponentLink", - "CoordinateComponentLink_0", - "CoordinateComponentLink_1", - "CoordinateComponentLink_2" - ], - "subset_group_count": 1 - }, - "DendrogramLayerState": { - "_type": "glue.plugins.dendro_viewer.state.DendrogramLayerState", - "values": { - "alpha": 0.78, - "color": "st__#404040", - "layer": "data", - "linewidth": 1, - "visible": true, - "zorder": 2 - } - }, - "DendrogramLayerState_0": { - "_type": "glue.plugins.dendro_viewer.state.DendrogramLayerState", - "values": { - "alpha": 0.5, - "color": "st__#e31a1c", - "layer": "Subset 1_0", - "linewidth": 1, - "visible": true, - "zorder": 3 - } - }, - "DendrogramViewer": { - "_protocol": 1, - "_type": "glue.plugins.dendro_viewer.qt.data_viewer.DendrogramViewer", - "layers": [ - { - "_type": "glue.plugins.dendro_viewer.layer_artist.DendrogramLayerArtist", - "state": "DendrogramLayerState" - }, - { - "_type": "glue.plugins.dendro_viewer.layer_artist.DendrogramLayerArtist", - "state": "DendrogramLayerState_0" - } - ], - "pos": [ - 0, - 0 - ], - "session": "Session", - "size": [ - 640, - 480 - ], - "state": { - "values": { - "aspect": "st__auto", - "height_att": "height", - "layers": "CallbackList", - "order_att": "height", - "parent_att": "parent", - "reference_data": "data", - "select_substruct": true, - "x_log": false, - "x_max": 1.05, - "x_min": -0.05, - "y_log": false, - "y_max": 4.555000000000001, - "y_min": 1.145 - } - } - }, - "Pixel Axis 0 [x]": { - "_type": "glue.core.component_id.PixelComponentID", - "axis": 0, - "hidden": true, - "label": "Pixel Axis 0 [x]" - }, - "Session": { - "_type": "glue.core.session.Session" - }, - "Subset 1": { - "_type": "glue.core.subset_group.SubsetGroup", - "label": "Subset 1", - "state": "CategorySubsetState", - "style": { - "_type": "glue.core.visual.VisualAttributes", - "alpha": 0.5, - "color": "#e31a1c", - "linestyle": "solid", - "linewidth": 1, - "marker": "o", - "markersize": 7 - }, - "subsets": [ - "Subset 1_0" - ] - }, - "Subset 1_0": { - "_type": "glue.core.subset_group.GroupedSubset", - "group": "Subset 1", - "style": { - "_type": "glue.core.visual.VisualAttributes", - "alpha": 0.5, - "color": "#e31a1c", - "linestyle": "solid", - "linewidth": 1, - "marker": "o", - "markersize": 7 - } - }, - "World 0": { - "_type": "glue.core.component_id.ComponentID", - "hidden": true, - "label": "World 0" - }, - "__main__": { - "_type": "glue.app.qt.application.GlueApplication", - "data": "DataCollection", - "plugins": [ - "glue.viewers.scatter", - "glue.io.formats.fits", - "glue_exp.importers.webcam", - "glue.viewers.table", - "glue.plugins.dendro_viewer", - "glue_wwt", - "glue.plugins.export_d3po", - "glue.plugins.tools.pv_slicer", - "glue.core.data_exporters", - "glue_aladin", - "glue_vispy_viewers.volume", - "glue_exp.tools.floodfill_selection", - "glue_exp.tools.contour_selection", - "glue.plugins.tools.spectrum_tool", - "glue_samp", - "glue_exp.importers.vizier", - "glue.plugins.coordinate_helpers", - "glue.plugins.exporters.plotly", - "glue.viewers.histogram", - "glue.viewers.image", - "glue_exp.tools.zoom_buttons", - "glue_vispy_viewers.scatter" - ], - "session": "Session", - "tab_names": [ - "Tab 1" - ], - "viewers": [ - [ - "DendrogramViewer" - ] - ] - }, - "data": { - "_key_joins": [], - "_protocol": 5, - "_type": "glue.core.data.Data", - "components": [ - [ - "height", - "Component" - ], - [ - "Pixel Axis 0 [x]", - "CoordinateComponent" - ], - [ - "World 0", - "CoordinateComponent_0" - ], - [ - "parent", - "Component_0" - ] - ], - "coords": "Coordinates", - "label": "data", - "primary_owner": [ - "height", - "Pixel Axis 0 [x]", - "World 0", - "parent" - ], - "style": { - "_type": "glue.core.visual.VisualAttributes", - "alpha": 0.78, - "color": "#404040", - "linestyle": "solid", - "linewidth": 1, - "marker": "o", - "markersize": 3 - }, - "subsets": [ - "Subset 1_0" - ], - "uuid": "50d692d5-e30f-4218-aa60-393ced074298" - }, - "height": { - "_type": "glue.core.component_id.ComponentID", - "hidden": false, - "label": "height" - }, - "parent": { - "_type": "glue.core.component_id.ComponentID", - "hidden": false, - "label": "parent" - } -} \ No newline at end of file diff --git a/glue/plugins/dendro_viewer/qt/tests/test_data_viewer.py b/glue/plugins/dendro_viewer/qt/tests/test_data_viewer.py deleted file mode 100644 index 20f19c8c3..000000000 --- a/glue/plugins/dendro_viewer/qt/tests/test_data_viewer.py +++ /dev/null @@ -1,180 +0,0 @@ -# pylint: disable=I0011,W0613,W0201,W0212,E1101,E1103 - -import os -from contextlib import nullcontext - -import pytest -import numpy as np - -from numpy.testing import assert_equal - -from glue.core import Data -from glue.core.roi import PointROI -from glue.viewers.matplotlib.qt.tests.test_data_viewer import BaseTestMatplotlibDataViewer -from glue.app.qt import GlueApplication -from glue.core.state import GlueUnSerializer - -from ..data_viewer import DendrogramViewer - -DATA = os.path.join(os.path.dirname(__file__), 'data') - - -class TestDendrogramCommon(BaseTestMatplotlibDataViewer): - - viewer_cls = DendrogramViewer - - def init_data(self): - return Data(label='d1', parent=[-1, 0, 1, 1], height=[1.3, 2.2, 3.2, 4.4]) - - # TODO: Find a way to simplify having to overload this just because - # we need to use a different ROI - def test_apply_roi_undo(self): - pass - - self.data_collection.append(self.data) - self.viewer.add_data(self.data) - - roi = PointROI(0.2, 2.8) - self.viewer.apply_roi(roi) - - assert len(self.data.subsets) == 1 - - mask1 = self.data.subsets[0].subset_state.to_mask(self.data) - - roi = PointROI(0.7, 2.8) - self.viewer.apply_roi(roi) - - assert len(self.data.subsets) == 1 - - mask2 = self.data.subsets[0].subset_state.to_mask(self.data) - - assert np.any(mask1 != mask2) - - self.application.undo() - - assert len(self.data.subsets) == 1 - - mask3 = self.data.subsets[0].subset_state.to_mask(self.data) - - assert np.all(mask3 == mask1) - - self.application.redo() - - assert len(self.data.subsets) == 1 - - mask4 = self.data.subsets[0].subset_state.to_mask(self.data) - - assert np.all(mask4 == mask2) - - @pytest.mark.skip - def test_add_invalid_data(): - pass - - -class TestDendrogramViewer(): - - def setup_method(self, method): - - self.data = Data(label='d1', parent=[-1, 0, 1, 1], height=[1.3, 2.2, 3.2, 4.4]) - - self.app = GlueApplication() - self.session = self.app.session - self.hub = self.session.hub - - self.data_collection = self.session.data_collection - self.data_collection.append(self.data) - - self.viewer = self.app.new_data_viewer(DendrogramViewer) - - self.data_collection.register_to_hub(self.hub) - self.viewer.register_to_hub(self.hub) - - def teardown_method(self, method): - self.viewer.close() - self.viewer = None - self.app.close() - self.app = None - - def test_point_select(self): - - self.viewer.add_data(self.data) - - # By default selecting a structure selects all substructures - roi = PointROI(0.5, 1.5) - self.viewer.apply_roi(roi) - assert len(self.data.subsets) == 1 - mask1 = self.data.subsets[0].subset_state.to_mask(self.data) - assert_equal(mask1, [0, 1, 1, 1]) - - # But this option can be turned off - self.viewer.state.select_substruct = False - self.viewer.apply_roi(roi) - assert len(self.data.subsets) == 1 - mask1 = self.data.subsets[0].subset_state.to_mask(self.data) - assert_equal(mask1, [0, 1, 0, 0]) - self.viewer.state.select_substruct = True - - # Try selecting a leaf - roi = PointROI(0.2, 2.8) - self.viewer.apply_roi(roi) - assert len(self.data.subsets) == 1 - mask1 = self.data.subsets[0].subset_state.to_mask(self.data) - assert_equal(mask1, [0, 0, 1, 0]) - - # Try selecting another leaf - roi = PointROI(0.7, 2.8) - self.viewer.apply_roi(roi) - assert len(self.data.subsets) == 1 - mask1 = self.data.subsets[0].subset_state.to_mask(self.data) - assert_equal(mask1, [0, 0, 0, 1]) - - def test_attribute_change_triggers_relayout(self): - - self.data.add_component([4, 5, 6, 7], 'flux') - self.viewer.add_data(self.data) - - l = self.viewer.state._layout - self.viewer.state.height_att = self.data.id['flux'] - assert self.viewer.state._layout is not l - - -class TestSessions(object): - - @pytest.mark.parametrize('protocol', [0, 1]) - def test_session_back_compat(self, protocol): - - filename = os.path.join(DATA, f'dendro_v{protocol}.glu') - - with open(filename, 'r') as f: - session = f.read() - - state = GlueUnSerializer.loads(session) - - # This is raised (only) on initial import, so depends on prior state unfortunately - with (pytest.warns(UserWarning, match='glue.external.echo is deprecated, import from echo') - if protocol == 1 else nullcontext()): - ga = state.object('__main__') - - dc = ga.session.data_collection - - assert len(dc) == 1 - - assert dc[0].label == 'data' - - viewer1 = ga.viewers[0][0] - - assert len(viewer1.state.layers) == 2 - - assert viewer1.state.parent_att is dc[0].id['parent'] - assert viewer1.state.height_att is dc[0].id['height'] - assert viewer1.state.order_att is dc[0].id['height'] - - layer_state = viewer1.state.layers[0] - assert layer_state.visible - assert layer_state.layer is dc[0] - - layer_state = viewer1.state.layers[1] - assert layer_state.visible - assert layer_state.layer is dc[0].subsets[0] - - ga.close() diff --git a/glue/plugins/export_d3po.py b/glue/plugins/export_d3po.py deleted file mode 100644 index 4d7558913..000000000 --- a/glue/plugins/export_d3po.py +++ /dev/null @@ -1,307 +0,0 @@ -import os -import json - -from glue.core import Subset - - -DISPATCH = {} - - -def save_page(page, page_number, label, subset): - """ Convert a tab of a glue session into a D3PO page - - :param page: Tuple of data viewers to save - :param label: Tab label - """ - result = {} - - # layout settings - result['grid'] = {'nRows': 1, 'nColumns': len(page)} - result['name'] = str(label) - result['caption'] = 'Generated by Glue' - - # style settings - d = page[0]._data[0] - unselected = dict(opacity=d.style.alpha, - size=d.style.markersize / 2, - color=d.style.color) - result['markerStyle'] = dict(unselected=unselected) - - if subset is not None: - s = subset.style - selected = dict(opacity=s.alpha, size=s.markersize / 2, color=s.color) - result['markerStyle']['selected'] = selected - result['selection'] = {'type': 'booleanColumn', - 'columnName': 'selection_%i' % page_number} - result['histogramStyle'] = result['markerStyle'] - - # save each plot - result['plots'] = list(map(save_plot, page, range(len(page)))) - - return result - - -def save_plot_base(plot, index): - result = {} - result['gridPosition'] = [0, index] - return result - - -def save_plot(plot, index): - typ = type(plot) - return DISPATCH[typ](plot, index) - - -def save_scatter(plot, index): - """ Convert a single glue scatter plot to a D3PO plot - - :param plot: Glue scatter plot - :class:`~glue.viewers.scatter.qt.ScatterViewer` - :param index: 1D index of plot on the page - :type index: int - - :rtype: json-serializable dict - """ - result = save_plot_base(plot, index) - result['type'] = 'scatter' - result['xAxis'] = dict(columnName=plot.state.x_att.label, - range=[float(plot.state.x_min), float(plot.state.x_max)]) - result['yAxis'] = dict(columnName=plot.state.y_att.label, - range=[float(plot.state.y_min), float(plot.state.y_max)]) - # XXX log scales - return result - - -def save_histogram(plot, index): - """ Convert a single histogram to a D3PO plot - - :param plot: Glue histogram - :type plot: :class:`~glue.viewers.histogram.qt.HistogramViewer` - - :param index: 1D index of plot on the page - :type index: int - - :rtype: json-serializable dict - """ - result = save_plot_base(plot, index) - result['type'] = 'histogram' - result['xAxis'] = dict(columnName=plot.state.x_att.label, - bins=int(plot.state.hist_n_bin), - range=[float(plot.state.hist_x_min), float(plot.state.hist_x_max)]) - # XXX normed, cumultive, log - return result - - -def stage_subsets(application): - """ - Return a tuple of the subset to use for each stage/tab, - or None if the tab has no subset - - If more than one subset is used per stage/tab, returns None - """ - result = [] - for page in application.viewers: - subset = None - for viewer in page: - for layer_artist in viewer.layers: - if not layer_artist.visible: - continue - s = layer_artist.layer - if not isinstance(s, Subset): - continue - if subset is not None and s is not subset: - return None - if subset is None: - subset = s - result.append(subset) - return tuple(result) - - -def can_save_d3po(application): - """ - Check whether an application can be exported to D3PO. - - Raises an exception if not - """ - dc = application.session.data_collection - - if len(dc) != 1: - raise ValueError("D3PO Export only supports a single dataset") - - for tab in application.viewers: - for viewer in tab: - if not isinstance(viewer, tuple(DISPATCH.keys())): - raise ValueError("D3PO Export only supports scatter " - "and histogram plots") - if sum(len(tab) for tab in application.viewers) == 0: - raise ValueError("D3PO Export requires at least one scatterplot " - "or histogram") - - if stage_subsets(application) is None: - raise ValueError("D3PO Export restricted to 0 or 1 subsets visible " - "in each tab") - - -def make_data_file(data, subsets, path): - """ - Create the data.csv file, given Data and tuple of subsets - """ - from astropy.table import Table, Column - - data_path = os.path.join(path, 'data.csv') - - t = Table([data[c] for c in data.components], - names=[c.label for c in data.components]) - - for i, subset in enumerate(subsets): - if subset is None: - continue - c = Column(data=subset.to_mask().astype('i'), name='selection_%i' % i) - t.add_column(c) - - t.write(data_path, format='ascii', delimiter=',') - - -def save_d3po(application, path, launch=True): - """Save a Glue session to a D3PO bundle. - - Currently, this has the following restrictions: - - The Glue session must have only one dataset open, and 0 or 1 subsets - - Only scatter plots or histograms are present - - At least one plot is present - - :param application: Glue appication to save - :param path: Path to directory to save in. Will be created if needed - """ - if os.path.exists(path) and not os.path.isdir(path): - os.unlink(path) - - if not os.path.exists(path): - os.mkdir(path) - - data = application.session.data_collection[0] - subsets = stage_subsets(application) - viewers = application.viewers - - # data.csv - make_data_file(data, subsets, path) - - # states.json - result = {} - result['filename'] = 'data.csv' # XXX don't think this is needed? - result['title'] = "Glue export of %s" % data.label - result['states'] = list(map(save_page, application.viewers, - range(len(viewers)), - application.tab_names, - subsets)) - - state_path = os.path.join(path, 'states.json') - with open(state_path, 'w') as outfile: - json.dump(result, outfile, indent=2, sort_keys=True) - - # index.html - html_path = os.path.join(path, 'index.html') - with open(html_path, 'w') as outfile: - outfile.write(HTML) - - # show the result - if launch: - launch_d3po(path) - - -def launch_d3po(path): - """Start a server to view an exported D3PO bundle, and open a browser. - - :param path: The TLD of the bundle - """ - from socketserver import TCPServer - from http.server import SimpleHTTPRequestHandler - from random import randrange - from socket import error - import webbrowser - from threading import Thread - - os.chdir(path) - - while True: - try: - PORT = randrange(8000, 9000) - server = TCPServer(("", PORT), SimpleHTTPRequestHandler, False) - server.allow_reuse_address = True - server.server_bind() - break - except error: # port already taken - pass - - print('Serving D3PO on port 0.0.0.0:%i' % PORT) - server.server_activate() - - thread = Thread(target=server.serve_forever) - thread.setDaemon(True) # do not prevent shutdown - thread.start() - webbrowser.open('http://0.0.0.0:%i' % PORT) - - -def setup(): - from glue.config import exporters - exporters.add('D3PO', save_d3po, can_save_d3po, outmode='directory') - - -HTML = """ - - - - - - - - - - - - - - - - - - - - - - -
-
- -
-
- - - - - - -""" - -try: - from glue.viewers.scatter.qt import ScatterViewer - from glue.viewers.histogram.qt import HistogramViewer -except ImportError: - pass -else: - DISPATCH[ScatterViewer] = save_scatter - DISPATCH[HistogramViewer] = save_histogram diff --git a/glue/plugins/tests/test_d3po.py b/glue/plugins/tests/test_d3po.py deleted file mode 100644 index 6777e021a..000000000 --- a/glue/plugins/tests/test_d3po.py +++ /dev/null @@ -1,51 +0,0 @@ -import os -from shutil import rmtree -from tempfile import mkdtemp - -import numpy as np - -from glue.core import Data, DataCollection -from glue.tests.helpers import requires_astropy -from glue.tests.helpers import requires_qt - -from ..export_d3po import make_data_file, save_d3po - - -@requires_astropy -def test_make_data_file(): - - from astropy.table import Table - - # astropy.Table interface has changed across versions. Check - # that we build a valid table - d = Data(x=[1, 2, 3], y=[2, 3, 4], label='data') - s = d.new_subset(label='test') - s.subset_state = d.id['x'] > 1 - - dir = mkdtemp() - try: - make_data_file(d, (s,), dir) - t = Table.read(os.path.join(dir, 'data.csv'), format='ascii') - np.testing.assert_array_equal(t['x'], [1, 2, 3]) - np.testing.assert_array_equal(t['y'], [2, 3, 4]) - np.testing.assert_array_equal(t['selection_0'], [0, 1, 1]) - finally: - rmtree(dir, ignore_errors=True) - - -@requires_qt -def test_save_d3po(tmpdir): - - from glue.app.qt.application import GlueApplication - from glue.viewers.scatter.qt import ScatterViewer - from glue.viewers.histogram.qt import HistogramViewer - - output = tmpdir.join('output.html').strpath - - d = Data(x=[1, 2, 3], y=[2, 3, 4], label='data') - dc = DataCollection([d]) - app = GlueApplication(dc) - app.new_data_viewer(ScatterViewer, data=d) - app.new_data_viewer(HistogramViewer, data=d) - save_d3po(app, output, launch=False) - app.close() diff --git a/glue/plugins/tools/__init__.py b/glue/plugins/tools/__init__.py index 13dd6c3e4..0a9fa2f21 100644 --- a/glue/plugins/tools/__init__.py +++ b/glue/plugins/tools/__init__.py @@ -5,18 +5,18 @@ def setup(): from glue.plugins.tools import python_export # noqa - from glue.viewers.histogram.qt.data_viewer import HistogramViewer + from glue_qt.viewers.histogram.data_viewer import HistogramViewer HistogramViewer.subtools = deepcopy(HistogramViewer.subtools) HistogramViewer.subtools['save'].append('save:python') - from glue.viewers.image.qt.data_viewer import ImageViewer + from glue_qt.viewers.image.data_viewer import ImageViewer ImageViewer.subtools = deepcopy(ImageViewer.subtools) ImageViewer.subtools['save'].append('save:python') - from glue.viewers.scatter.qt.data_viewer import ScatterViewer + from glue_qt.viewers.scatter.data_viewer import ScatterViewer ScatterViewer.subtools = deepcopy(ScatterViewer.subtools) ScatterViewer.subtools['save'].append('save:python') - from glue.viewers.profile.qt.data_viewer import ProfileViewer + from glue_qt.viewers.profile.data_viewer import ProfileViewer ProfileViewer.subtools = deepcopy(ProfileViewer.subtools) ProfileViewer.subtools['save'].append('save:python') diff --git a/glue/plugins/tools/pv_slicer/qt/__init__.py b/glue/plugins/tools/pv_slicer/qt/__init__.py index 3ebb8e8d5..031c55332 100644 --- a/glue/plugins/tools/pv_slicer/qt/__init__.py +++ b/glue/plugins/tools/pv_slicer/qt/__init__.py @@ -1,7 +1,4 @@ -from .pv_slicer import * # noqa - - -def setup(): - from glue.viewers.image.qt import ImageViewer - from glue.plugins.tools.pv_slicer.qt import PVSlicerMode # noqa - ImageViewer.tools.append('slice') +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.plugins.tools.pv_slicer.qt is deprecated, use glue_qt.plugins.tools.pv_slicer instead', GlueDeprecationWarning) +from glue_qt.plugins.tools.pv_slicer import * # noqa diff --git a/glue/plugins/tools/pv_slicer/qt/pv_slicer.py b/glue/plugins/tools/pv_slicer/qt/pv_slicer.py index 76df4b647..12c531e6c 100644 --- a/glue/plugins/tools/pv_slicer/qt/pv_slicer.py +++ b/glue/plugins/tools/pv_slicer/qt/pv_slicer.py @@ -1,274 +1,4 @@ -import numpy as np - -from glue.viewers.matplotlib.toolbar_mode import PathMode -from glue.viewers.image.qt import StandaloneImageViewer -from glue.config import viewer_tool -from glue.utils import defer_draw -from glue.core.coordinate_helpers import axis_label - - -@viewer_tool -class PVSlicerMode(PathMode): - - icon = 'glue_slice' - tool_id = 'slice' - action_text = 'Slice Extraction' - tool_tip = ('Extract a slice from an arbitrary path\n' - ' ENTER accepts the path\n' - ' ESCAPE clears the path') - status_tip = 'Draw a path then press ENTER to extract slice, or press ESC to cancel' - shortcut = 'P' - - def __init__(self, viewer, **kwargs): - super(PVSlicerMode, self).__init__(viewer, **kwargs) - self._roi_callback = self._extract_callback - self._slice_widget = None - self.viewer.state.add_callback('reference_data', self._on_reference_data_change) - - def _on_reference_data_change(self, reference_data): - if reference_data is not None: - self.enabled = reference_data.ndim == 3 - - def _clear_path(self): - self.viewer.hide_crosshairs() - self.clear() - - def _extract_callback(self, mode): - """ - Extract a PV-like slice, given a path traced on the widget - """ - vx, vy = mode.roi().to_polygon() - self._build_from_vertices(vx, vy) - - def _build_from_vertices(self, vx, vy): - pv_slice, x, y, wcs = _slice_from_path(vx, vy, self.viewer.state.reference_data, - self.viewer.state.layers[0].attribute, - self.viewer.state.wcsaxes_slice[::-1]) - if self._slice_widget is None: - self._slice_widget = PVSliceWidget(image=pv_slice, wcs=wcs, - image_viewer=self.viewer, - x=x, y=y, interpolation='nearest') - self.viewer._session.application.add_widget(self._slice_widget, - label='Custom Slice') - self._slice_widget.window_closed.connect(self._clear_path) - else: - self._slice_widget.set_image(image=pv_slice, wcs=wcs, - x=x, y=y, interpolation='nearest') - - result = self._slice_widget - result.axes.set_xlabel("Position along path") - if wcs is None: - result.axes.set_ylabel("Cube slice index") - else: - result.axes.set_ylabel(_slice_label(self.viewer.state.reference_data, - self.viewer.state.wcsaxes_slice[::-1])) - - result.show() - - def close(self): - if self._slice_widget: - self._slice_widget.close() - return super(PVSlicerMode, self).close() - - -class PVSliceWidget(StandaloneImageViewer): - - """ A standalone image widget with extra interactivity for PV slices """ - - def __init__(self, image=None, wcs=None, image_viewer=None, - x=None, y=None, **kwargs): - """ - :param image: 2D Numpy array representing the PV Slice - :param wcs: WCS for the PV slice - :param image_viewer: Parent ImageViewer this was extracted from - :param kwargs: Extra keywords are passed to imshow - """ - self._crosshairs = None - self._parent = image_viewer - super(PVSliceWidget, self).__init__(image=image, wcs=wcs, **kwargs) - conn = self.axes.figure.canvas.mpl_connect - self._down_id = conn('button_press_event', self._on_click) - self._move_id = conn('motion_notify_event', self._on_move) - self.axes.format_coord = self._format_coord - self._x = x - self._y = y - self._parent.state.add_callback('x_att', self.reset) - self._parent.state.add_callback('y_att', self.reset) - - def _format_coord(self, x, y): - """ - Return a formatted location label for the taskbar - - :param x: x pixel location in slice array - :param y: y pixel location in slice array - """ - - # xy -> xyz in image view - pix = self._pos_in_parent(xdata=x, ydata=y) - - # xyz -> data pixel coords - # accounts for fact that image might be shown transposed/rotated - s = list(self._slc) - idx = _slice_index(self._parent.state.reference_data, self._slc) - s[s.index('x')] = pix[0] - s[s.index('y')] = pix[1] - s[idx] = pix[2] - - # labels = self._parent.coordinate_labels(s) - # return ' '.join(labels) - return '' - - def set_image(self, image=None, wcs=None, x=None, y=None, **kwargs): - super(PVSliceWidget, self).set_image(image=image, wcs=wcs, **kwargs) - self._axes.set_aspect('auto') - self._axes.set_xlim(-0.5, image.shape[1] - 0.5) - self._axes.set_ylim(-0.5, image.shape[0] - 0.5) - self._slc = self._parent.state.wcsaxes_slice[::-1] - self._x = x - self._y = y - - @defer_draw - def _sync_slice(self, event): - s = list(self._slc) - # XXX breaks if display_data changes - _, _, z = self._pos_in_parent(event) - s[_slice_index(self._parent.state.reference_data, s)] = int(z) - self._parent.state.slices = tuple(s) - - @defer_draw - def _draw_crosshairs(self, event): - x, y, _ = self._pos_in_parent(event) - self._parent.show_crosshairs(x, y) - - @defer_draw - def _on_move(self, event): - if not event.button: - return - - if not event.inaxes or event.canvas.toolbar.mode != '': - return - - self._sync_slice(event) - self._draw_crosshairs(event) - - def _pos_in_parent(self, event=None, xdata=None, ydata=None): - - if event is not None: - xdata = event.xdata - ydata = event.ydata - - # Find position slice where cursor is - ind = int(round(np.clip(xdata, 0, self._im_array.shape[1] - 1))) - - # Find pixel coordinate in input image for this slice - x = self._x[ind] - y = self._y[ind] - - # The 3-rd coordinate in the input WCS is simply the second - # coordinate in the PV slice. - z = ydata - - return x, y, z - - def _on_click(self, event): - if not event.inaxes or event.canvas.toolbar.mode != '': - return - self._sync_slice(event) - self._draw_crosshairs(event) - - def reset(self, *args): - self.close() - - -def _slice_from_path(x, y, data, attribute, slc): - """ - Extract a PV-like slice from a cube - - :param x: An array of x values to extract (pixel units) - :param y: An array of y values to extract (pixel units) - :param data: :class:`~glue.core.data.Data` - :param attribute: :claass:`~glue.core.data.Component` - :param slc: orientation of the image widget that `pts` are defined on - - :returns: (slice, x, y) - slice is a 2D Numpy array, corresponding to a "PV ribbon" - cutout from the cube - x and y are the resampled points along which the - ribbon is extracted - - :note: For >3D cubes, the "V-axis" of the PV slice is the longest - cube axis ignoring the x/y axes of `slc` - """ - from pvextractor import Path, extract_pv_slice - p = Path(list(zip(x, y))) - - cube = data[attribute] - dims = list(range(data.ndim)) - s = list(slc) - ind = _slice_index(data, slc) - - from astropy.wcs import WCS - - if isinstance(data.coords, WCS): - cube_wcs = data.coords - else: - cube_wcs = None - - # transpose cube to (z, y, x, ) - def _swap(x, s, i, j): - x[i], x[j] = x[j], x[i] - s[i], s[j] = s[j], s[i] - - _swap(dims, s, ind, 0) - _swap(dims, s, s.index('y'), 1) - _swap(dims, s, s.index('x'), 2) - - cube = cube.transpose(dims) - - if cube_wcs is not None: - cube_wcs = cube_wcs.sub([data.ndim - nx for nx in dims[::-1]]) - - # slice down from >3D to 3D if needed - s = tuple([slice(None)] * 3 + [slc[d] for d in dims[3:]]) - cube = cube[s] - - # sample cube - spacing = 1 # pixel - x, y = [np.round(_x).astype(int) for _x in p.sample_points(spacing)] - - try: - result = extract_pv_slice(cube, path=p, wcs=cube_wcs, order=0) - wcs = WCS(result.header) - except Exception: # sometimes pvextractor complains due to wcs. Try to recover - result = extract_pv_slice(cube, path=p, wcs=None, order=0) - wcs = None - - data = result.data - - return data, x, y, wcs - - -def _slice_index(data, slc): - """ - The axis over which to extract PV slices - """ - for i in range(len(slc)): - if np.isreal(slc[i]): - return i - raise ValueError("Could not find slice index with slc={0}".format(slc)) - - -def _slice_label(data, slc): - """ - Returns a formatted axis label corresponding to the slice dimension - in a PV slice - - :param data: Data that slice is extracted from - :param slc: orientation in the image widget from which the PV slice - was defined - """ - idx = _slice_index(data, slc) - if getattr(data, 'coords') is None: - return data.pixel_component_ids[idx].label - else: - return axis_label(data.coords, idx) +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.plugins.tools.pv_slicer.qt.pv_slicer is deprecated, use glue_qt.plugins.tools.pv_slicer.pv_slicer instead', GlueDeprecationWarning) +from glue_qt.plugins.tools.pv_slicer.pv_slicer import * # noqa diff --git a/glue/plugins/tools/pv_slicer/qt/tests/__init__.py b/glue/plugins/tools/pv_slicer/qt/tests/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/glue/plugins/tools/pv_slicer/qt/tests/test_pv_slicer.py b/glue/plugins/tools/pv_slicer/qt/tests/test_pv_slicer.py deleted file mode 100644 index 5fe1b5c6a..000000000 --- a/glue/plugins/tools/pv_slicer/qt/tests/test_pv_slicer.py +++ /dev/null @@ -1,154 +0,0 @@ -import numpy as np -from unittest.mock import MagicMock -from numpy.testing import assert_allclose - -from glue.core import Data -from glue.core.coordinates import IdentityCoordinates -from glue.viewers.image.qt import StandaloneImageViewer, ImageViewer -from glue.tests.helpers import requires_astropy, requires_scipy -from glue.app.qt import GlueApplication -from glue.utils.qt import process_events - -from ..pv_slicer import _slice_from_path, _slice_label, _slice_index, PVSliceWidget - - -@requires_astropy -@requires_scipy -class TestSliceExtraction(object): - - def setup_method(self, method): - self.x = np.random.random((2, 3, 4)) - self.d = Data(x=self.x) - - def test_constant_y(self): - - slc = (0, 'y', 'x') - x = [-0.5, 3.5] - y = [0, 0] - s = _slice_from_path(x, y, self.d, 'x', slc)[0] - assert_allclose(s, self.x[:, 0, :]) - - def test_constant_x(self): - - slc = (0, 'y', 'x') - y = [-0.5, 2.5] - x = [0, 0] - s = _slice_from_path(x, y, self.d, 'x', slc)[0] - assert_allclose(s, self.x[:, :, 0]) - - def test_transpose(self): - slc = (0, 'x', 'y') - y = [-0.5, 3.5] - x = [0, 0] - s = _slice_from_path(x, y, self.d, 'x', slc)[0] - assert_allclose(s, self.x[:, 0, :]) - - -def test_slice_label(): - d = Data(x=np.zeros((2, 3, 4)), coords=IdentityCoordinates(n_dim=3)) - assert _slice_label(d, (0, 'y', 'x')) == 'World 0' - assert _slice_label(d, ('y', 0, 'x')) == 'World 1' - assert _slice_label(d, ('y', 'x', 0)) == 'World 2' - - -def test_slice_label_nocoords(): - d = Data(x=np.zeros((2, 3, 4))) - assert _slice_label(d, (0, 'y', 'x')) == 'Pixel Axis 0 [z]' - assert _slice_label(d, ('y', 0, 'x')) == 'Pixel Axis 1 [y]' - assert _slice_label(d, ('y', 'x', 0)) == 'Pixel Axis 2 [x]' - - -def test_slice_index(): - d = Data(x=np.zeros((2, 3, 4))) - assert _slice_index(d, (0, 'y', 'x')) == 0 - assert _slice_index(d, ('y', 0, 'x')) == 1 - - -class TestStandaloneImageViewer(object): - - def setup_method(self, method): - im = np.random.random((3, 3)) - self.w = StandaloneImageViewer(im) - - def teardown_method(self, method): - self.w.close() - - def test_set_cmap(self): - cm_mode = self.w.toolbar.tools['image:colormap'] - act = cm_mode.menu_actions()[1] - act.trigger() - assert self.w._composite.layers['image']['cmap'] is act.cmap - - def test_double_set_image(self): - assert len(self.w._axes.images) == 1 - self.w.set_image(np.zeros((3, 3))) - assert len(self.w._axes.images) == 1 - - -class MockImageViewer(object): - - def __init__(self, slice, data): - self.slice = slice - self.data = data - self.wcs = None - self.state = MagicMock() - - -class TestPVSliceWidget(object): - - def setup_method(self, method): - - self.d = Data(x=np.zeros((2, 3, 4))) - self.slc = (0, 'y', 'x') - self.image = MockImageViewer(self.slc, self.d) - self.w = PVSliceWidget(image=np.zeros((3, 4)), wcs=None, image_viewer=self.image) - - def teardown_method(self, method): - self.w.close() - - def test_basic(self): - pass - - -class TestPVSliceTool(object): - - def setup_method(self, method): - self.cube = Data(label='cube', x=np.arange(1000).reshape((5, 10, 20))) - self.application = GlueApplication() - self.application.data_collection.append(self.cube) - self.viewer = self.application.new_data_viewer(ImageViewer) - self.viewer.add_data(self.cube) - - def teardown_method(self, method): - self.viewer.close() - self.viewer = None - self.application.close() - self.application = None - - @requires_astropy - @requires_scipy - def test_basic(self): - - self.viewer.toolbar.active_tool = 'slice' - - self.viewer.axes.figure.canvas.draw() - process_events() - - x, y = self.viewer.axes.transData.transform([[0.9, 4]])[0] - self.viewer.axes.figure.canvas.button_press_event(x, y, 1) - x, y = self.viewer.axes.transData.transform([[7.2, 6.6]])[0] - self.viewer.axes.figure.canvas.button_press_event(x, y, 1) - - process_events() - - assert len(self.application.tab().subWindowList()) == 1 - - self.viewer.axes.figure.canvas.key_press_event('enter') - - process_events() - - assert len(self.application.tab().subWindowList()) == 2 - - pv_widget = self.application.tab().subWindowList()[1].widget() - assert pv_widget._x.shape == (6,) - assert pv_widget._y.shape == (6,) diff --git a/glue/qglue.py b/glue/qglue.py index 1133e48f7..66dc0e175 100644 --- a/glue/qglue.py +++ b/glue/qglue.py @@ -1,216 +1,18 @@ -""" -Utility function to load a variety of python objects into glue -""" +import warnings +from glue.utils.error import GlueDeprecationWarning -# Note: this is imported with Glue. We want -# to minimize imports so that utilities like glue-deps -# can run on systems with missing dependencies +warnings.warn('Importing from glue.qglue is deprecated, use glue_qt.qglue instead', GlueDeprecationWarning) -import sys -from contextlib import contextmanager +from glue_qt.qglue import * # noqa -import numpy as np -from glue.config import qglue_parser +def parse_data(*args, **kwargs): + warnings.warn('glue.qglue.parse_data is deprecated, use glue.core.parsers.parse_data instead', GlueDeprecationWarning) + from glue.core.parsers import parse_data + return parse_data(*args, **kwargs) -try: - from glue.core import BaseData, Data -except ImportError: - # let qglue import, even though this won't work - # qglue will throw an ImportError - BaseData = Data = None - -__all__ = ['qglue'] - - -@contextmanager -def restore_io(): - stdin = sys.stdin - stdout = sys.stdout - stderr = sys.stderr - _in = sys.__stdin__ - _out = sys.__stdout__ - _err = sys.__stderr__ - try: - yield - finally: - sys.stdin = stdin - sys.stdout = stdout - sys.stderr = stderr - sys.__stdin__ = _in - sys.__stdout__ = _out - sys.__stderr__ = _err - - -@qglue_parser(dict) -def _parse_data_dict(data, label): - result = Data(label=label) - for label, component in data.items(): - result.add_component(component, label) - return [result] - - -@qglue_parser(np.recarray) -def _parse_data_recarray(data, label): - kwargs = dict((n, data[n]) for n in data.dtype.names) - return [Data(label=label, **kwargs)] - - -@qglue_parser(BaseData) -def _parse_data_glue_data(data, label): - if isinstance(data, Data): - data.label = label - return [data] - - -@qglue_parser(np.ndarray) -def _parse_data_numpy(data, label): - return [Data(**{label: data, 'label': label})] - - -@qglue_parser(list) -def _parse_data_list(data, label): - return [Data(**{label: data, 'label': label})] - - -@qglue_parser(str) -def _parse_data_path(path, label): - from glue.core.data_factories import load_data, as_list - - data = load_data(path) - for d in as_list(data): - d.label = label - return as_list(data) - - -def parse_data(data, label): - - # First try new data translation layer - - from glue.config import data_translator - - try: - handler, preferred = data_translator.get_handler_for(data) - except TypeError: - pass - else: - data = handler.to_data(data) - data.label = label - data._preferred_translation = preferred - return [data] - - # Then try legacy 'qglue_parser' infrastructure - - for item in qglue_parser: - - data_class = item.data_class - parser = item.parser - if isinstance(data, data_class): - try: - return parser(data, label) - except Exception as e: - raise ValueError("Invalid format for data '%s'\n\n%s" % - (label, e)) - - raise TypeError("Invalid data description: %s" % data) - - -def parse_links(dc, links): - from glue.core.link_helpers import MultiLink - from glue.core import ComponentLink - - data = dict((d.label, d) for d in dc) - result = [] - - def find_cid(s): - dlabel, clabel = s.split('.') - d = data[dlabel] - c = d.find_component_id(clabel) - if c is None: - raise ValueError("Invalid link (no component named %s)" % s) - return c - - for link in links: - f, t = link[0:2] # from and to component names - u = u2 = None - if len(link) >= 3: # forward translation function - u = link[2] - if len(link) == 4: # reverse translation function - u2 = link[3] - - # component names -> component IDs - if isinstance(f, str): - f = [find_cid(f)] - else: - f = [find_cid(item) for item in f] - - if isinstance(t, str): - t = find_cid(t) - result.append(ComponentLink(f, t, u)) - else: - t = [find_cid(item) for item in t] - result += MultiLink(f, t, u, u2) - - return result - - -def qglue(**kwargs): - """ - Quickly send python variables to Glue for visualization. - - The generic calling sequence is:: - - qglue(label1=data1, label2=data2, ..., [links=links]) - - The kewyords label1, label2, ... can be named anything besides ``links`` - - data1, data2, ... can be in many formats: - * A pandas data frame - * A path to a file - * A numpy array, or python list - * A numpy rec array - * A dictionary of numpy arrays with the same shape - * An astropy Table - - ``Links`` is an optional list of link descriptions, each of which has - the format: ([left_ids], [right_ids], forward, backward) - - Each ``left_id``/``right_id`` is a string naming a component in a dataset - (i.e., ``data1.x``). ``forward`` and ``backward`` are functions which - map quantities on the left to quantities on the right, and vice - versa. `backward` is optional - - Examples:: - - balls = {'kg': [1, 2, 3], 'radius_cm': [10, 15, 30]} - cones = {'lbs': [5, 3, 3, 1]} - def lb2kg(lb): - return lb / 2.2 - def kg2lb(kg): - return kg * 2.2 - - links = [(['balls.kg'], ['cones.lbs'], lb2kg, kg2lb)] - qglue(balls=balls, cones=cones, links=links) - - :returns: A :class:`~glue.app.qt.application.GlueApplication` object - """ - from glue.core import DataCollection - from glue.app.qt import GlueApplication - from glue.dialogs.autolinker.qt import run_autolinker - - links = kwargs.pop('links', None) - - dc = DataCollection() - for label, data in kwargs.items(): - dc.extend(parse_data(data, label)) - - if links is not None: - dc.add_link(parse_links(dc, links)) - - with restore_io(): - ga = GlueApplication(dc) - run_autolinker(dc) - ga.start() - - return ga +def parse_links(*args, **kwargs): + warnings.warn('glue.qglue.parse_links is deprecated, use glue.core.parsers.parse_links instead', GlueDeprecationWarning) + from glue.core.parsers import parse_links + return parse_links(*args, **kwargs) diff --git a/glue/tests/data/__init__.py b/glue/tests/data/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/glue/tests/data/double_tables.fits b/glue/tests/data/double_tables.fits deleted file mode 100644 index 1c382e70d..000000000 Binary files a/glue/tests/data/double_tables.fits and /dev/null differ diff --git a/glue/tests/data/glue_v0.10_table.glu b/glue/tests/data/glue_v0.10_table.glu deleted file mode 100644 index 25baa1c82..000000000 --- a/glue/tests/data/glue_v0.10_table.glu +++ /dev/null @@ -1,365 +0,0 @@ -{ - "Component": { - "_type": "glue.core.component.Component", - "data": { - "_type": "numpy.ndarray", - "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGk4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDMsKSwgfSAgICAgICAgICAgIAoBAAAAAAAAAAIAAAAAAAAAAwAAAAAAAAA=" - }, - "units": "" - }, - "Component_0": { - "_type": "glue.core.component.Component", - "data": { - "_type": "numpy.ndarray", - "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGk4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDMsKSwgfSAgICAgICAgICAgIAoEAAAAAAAAAAUAAAAAAAAABgAAAAAAAAA=" - }, - "units": "" - }, - "CoordinateComponent": { - "_type": "glue.core.component.CoordinateComponent", - "axis": 0, - "world": false - }, - "CoordinateComponentLink": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates_0", - "frm": [ - "Pixel Axis 0 [x]_0" - ], - "index": 0, - "pix2world": true, - "to": [ - "World 0_0" - ] - }, - "CoordinateComponentLink_0": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates_0", - "frm": [ - "World 0_0" - ], - "index": 0, - "pix2world": false, - "to": [ - "Pixel Axis 0 [x]_0" - ] - }, - "CoordinateComponentLink_1": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "Pixel Axis 0 [x]" - ], - "index": 0, - "pix2world": true, - "to": [ - "World 0" - ] - }, - "CoordinateComponentLink_2": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "World 0" - ], - "index": 0, - "pix2world": false, - "to": [ - "Pixel Axis 0 [x]" - ] - }, - "CoordinateComponentLink_3": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "Pixel Axis 0 [x]_1" - ], - "index": 0, - "pix2world": true, - "to": [ - "World 0_1" - ] - }, - "CoordinateComponentLink_4": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "World 0_1" - ], - "index": 0, - "pix2world": false, - "to": [ - "Pixel Axis 0 [x]_1" - ] - }, - "CoordinateComponent_0": { - "_type": "glue.core.component.CoordinateComponent", - "axis": 0, - "world": true - }, - "Coordinates": { - "_type": "glue.core.coordinates.Coordinates" - }, - "Coordinates_0": { - "_type": "glue.core.coordinates.Coordinates" - }, - "DataCollection": { - "_protocol": 3, - "_type": "glue.core.data_collection.DataCollection", - "cids": [ - "x", - "Pixel Axis 0 [x]", - "World 0", - "y" - ], - "components": [ - "Component", - "CoordinateComponent", - "CoordinateComponent_0", - "Component_0" - ], - "data": [ - "data" - ], - "groups": [ - "Subset 1_0", - "Subset 2_0" - ], - "links": [ - "CoordinateComponentLink", - "CoordinateComponentLink_0", - "CoordinateComponentLink_1", - "CoordinateComponentLink_2", - "CoordinateComponentLink_3", - "CoordinateComponentLink_4" - ], - "subset_group_count": 2 - }, - "ElementSubsetState": { - "_type": "glue.core.subset.ElementSubsetState", - "data_uuid": "ca8ae9d2-170d-482a-bb93-4f8e1b4b1ddd", - "indices": [ - 0, - 1 - ] - }, - "ElementSubsetState_0": { - "_type": "glue.core.subset.ElementSubsetState", - "data_uuid": "ca8ae9d2-170d-482a-bb93-4f8e1b4b1ddd", - "indices": [ - 1 - ] - }, - "Pixel Axis 0 [x]": { - "_type": "glue.core.component_id.PixelComponentID", - "axis": 0, - "hidden": true, - "label": "Pixel Axis 0 [x]" - }, - "Pixel Axis 0 [x]_0": { - "_type": "glue.core.component_id.PixelComponentID", - "axis": 0, - "hidden": true, - "label": "Pixel Axis 0 [x]" - }, - "Pixel Axis 0 [x]_1": { - "_type": "glue.core.component_id.PixelComponentID", - "axis": 0, - "hidden": true, - "label": "Pixel Axis 0 [x]" - }, - "Session": { - "_type": "glue.core.session.Session" - }, - "Subset 1": { - "_type": "glue.core.subset_group.GroupedSubset", - "group": "Subset 1_0", - "style": { - "_type": "glue.core.visual.VisualAttributes", - "alpha": 0.5, - "color": "#e31a1c", - "linestyle": "solid", - "linewidth": 1, - "marker": "o", - "markersize": 7 - } - }, - "Subset 1_0": { - "_type": "glue.core.subset_group.SubsetGroup", - "label": "Subset 1", - "state": "ElementSubsetState", - "style": { - "_type": "glue.core.visual.VisualAttributes", - "alpha": 0.5, - "color": "#e31a1c", - "linestyle": "solid", - "linewidth": 1, - "marker": "o", - "markersize": 7 - }, - "subsets": [ - "Subset 1" - ] - }, - "Subset 2": { - "_type": "glue.core.subset_group.GroupedSubset", - "group": "Subset 2_0", - "style": { - "_type": "glue.core.visual.VisualAttributes", - "alpha": 0.5, - "color": "#33a02c", - "linestyle": "solid", - "linewidth": 1, - "marker": "o", - "markersize": 7 - } - }, - "Subset 2_0": { - "_type": "glue.core.subset_group.SubsetGroup", - "label": "Subset 2", - "state": "ElementSubsetState_0", - "style": { - "_type": "glue.core.visual.VisualAttributes", - "alpha": 0.5, - "color": "#33a02c", - "linestyle": "solid", - "linewidth": 1, - "marker": "o", - "markersize": 7 - }, - "subsets": [ - "Subset 2" - ] - }, - "TableWidget": { - "_type": "glue.viewers.table.qt.viewer_widget.TableWidget", - "layers": [ - { - "_type": "glue.viewers.table.qt.viewer_widget.TableLayerArtist", - "layer": "data", - "visible": true, - "zorder": 1 - }, - { - "_type": "glue.viewers.table.qt.viewer_widget.TableLayerArtist", - "layer": "Subset 1", - "visible": true, - "zorder": 2 - }, - { - "_type": "glue.viewers.table.qt.viewer_widget.TableLayerArtist", - "layer": "Subset 2", - "visible": true, - "zorder": 3 - } - ], - "pos": [ - 0, - 0 - ], - "properties": {}, - "session": "Session", - "size": [ - 640, - 480 - ] - }, - "World 0": { - "_type": "glue.core.component_id.ComponentID", - "hidden": true, - "label": "World 0" - }, - "World 0_0": { - "_type": "glue.core.component_id.ComponentID", - "hidden": true, - "label": "World 0" - }, - "World 0_1": { - "_type": "glue.core.component_id.ComponentID", - "hidden": true, - "label": "World 0" - }, - "__main__": { - "_type": "glue.app.qt.application.GlueApplication", - "data": "DataCollection", - "plugins": [ - "glue.plugins.tools.pv_slicer", - "glue_vispy_viewers.scatter", - "glue_vispy_viewers.volume", - "glue.core.data_exporters", - "glue.plugins.tools.spectrum_tool", - "glue.viewers.table", - "glue.plugins.coordinate_helpers", - "glue.plugins.exporters.plotly", - "glue.viewers.image", - "glue.viewers.histogram", - "glue.viewers.scatter", - "glue_ginga", - "glue.plugins.export_d3po" - ], - "session": "Session", - "tab_names": [ - "Tab 1" - ], - "viewers": [ - [ - "TableWidget" - ] - ] - }, - "data": { - "_key_joins": [], - "_protocol": 5, - "_type": "glue.core.data.Data", - "components": [ - [ - "x", - "Component" - ], - [ - "Pixel Axis 0 [x]", - "CoordinateComponent" - ], - [ - "World 0", - "CoordinateComponent_0" - ], - [ - "y", - "Component_0" - ] - ], - "coords": "Coordinates", - "label": "data", - "primary_owner": [ - "x", - "Pixel Axis 0 [x]", - "World 0", - "y" - ], - "style": { - "_type": "glue.core.visual.VisualAttributes", - "alpha": 0.8, - "color": "#595959", - "linestyle": "solid", - "linewidth": 1, - "marker": "o", - "markersize": 3 - }, - "subsets": [ - "Subset 1", - "Subset 2" - ], - "uuid": "ca8ae9d2-170d-482a-bb93-4f8e1b4b1ddd" - }, - "x": { - "_type": "glue.core.component_id.ComponentID", - "hidden": false, - "label": "x" - }, - "y": { - "_type": "glue.core.component_id.ComponentID", - "hidden": false, - "label": "y" - } -} \ No newline at end of file diff --git a/glue/tests/data/glue_v0.7_pixel_roi_selection.glu b/glue/tests/data/glue_v0.7_pixel_roi_selection.glu deleted file mode 100644 index 6abbc4730..000000000 --- a/glue/tests/data/glue_v0.7_pixel_roi_selection.glu +++ /dev/null @@ -1,363 +0,0 @@ -{ - "CoordinateComponent_2": { - "world": true, - "_type": "glue.core.component.CoordinateComponent", - "axis": 1 - }, - "CoordinateComponent_1": { - "world": true, - "_type": "glue.core.component.CoordinateComponent", - "axis": 0 - }, - "CoordinateComponent_0": { - "world": false, - "_type": "glue.core.component.CoordinateComponent", - "axis": 1 - }, - "Coordinates": { - "_type": "glue.core.coordinates.Coordinates" - }, - "__main__": { - "_type": "glue.app.qt.application.GlueApplication", - "session": "Session", - "data": "DataCollection", - "viewers": [ - [ - "ImageWidget" - ] - ], - "plugins": [ - "glue.plugins.tools.pv_slicer", - "glue.viewers.histogram", - "glue.plugins.export_plotly", - "glue.plugins.export_d3po", - "glue.viewers.image", - "glue.plugins.tools.spectrum_tool", - "glue.viewers.scatter", - "glue.plugins.coordinate_helpers" - ] - }, - "1_0": { - "style": { - "_type": "glue.core.visual.VisualAttributes", - "color": "#e31a1c", - "markersize": 7, - "marker": "o", - "alpha": 0.5, - "linewidth": 1, - "linestyle": "solid" - }, - "group": "1", - "_type": "glue.core.subset_group.GroupedSubset" - }, - "World 1": { - "hidden": true, - "_type": "glue.core.component_id.ComponentID", - "label": "World 1" - }, - "image_0": { - "hidden": false, - "_type": "glue.core.component_id.ComponentID", - "label": "image" - }, - "CoordinateComponent": { - "world": false, - "_type": "glue.core.component.CoordinateComponent", - "axis": 0 - }, - "Component": { - "units": "None", - "_type": "glue.core.component.Component", - "data": { - "_type": "numpy.ndarray", - "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGY4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDMsIDIpLCB9ICAgICAgICAgIAoAAAAAAADwPwAAAAAAAPA/AAAAAAAA8D8AAAAAAADwPwAAAAAAAPA/AAAAAAAA8D8=" - } - }, - "1": { - "_type": "glue.core.subset_group.SubsetGroup", - "style": { - "_type": "glue.core.visual.VisualAttributes", - "color": "#e31a1c", - "markersize": 7, - "marker": "o", - "alpha": 0.5, - "linewidth": 1, - "linestyle": "solid" - }, - "state": "RoiSubsetState", - "subsets": [ - "1_0" - ], - "label": "1" - }, - "ImageWidget": { - "layers": [ - { - "visible": true, - "layer": "image", - "zorder": 1, - "_type": "glue.viewers.image.layer_artist.ImageLayerArtist", - "norm": "DS9Normalize" - }, - { - "visible": true, - "layer": "1_0", - "zorder": 2, - "_type": "glue.viewers.image.layer_artist.SubsetImageLayerArtist" - } - ], - "_type": "glue.viewers.image.qt.viewer_widget.ImageWidget", - "pos": [ - 0, - 0 - ], - "session": "Session", - "properties": { - "rgb_viz": [ - true, - true, - true - ], - "ratt": null, - "rgb_mode": false, - "gatt": null, - "attribute": "image_0", - "batt": null, - "slice": [ - "y", - "x" - ], - "data": "image" - }, - "size": [ - 600, - 400 - ] - }, - "CoordinateComponentLink_2": { - "index": 1, - "_type": "glue.core.component_link.CoordinateComponentLink", - "frm": [ - "Pixel y", - "Pixel x" - ], - "to": [ - "World 1" - ], - "pix2world": true, - "coords": "Coordinates" - }, - "CoordinateComponentLink": { - "index": 0, - "_type": "glue.core.component_link.CoordinateComponentLink", - "frm": [ - "World 0", - "World 1" - ], - "to": [ - "Pixel y" - ], - "pix2world": false, - "coords": "Coordinates" - }, - "image": { - "style": { - "_type": "glue.core.visual.VisualAttributes", - "color": "#7f7f7f", - "markersize": 3, - "marker": "o", - "alpha": 0.5, - "linewidth": 1, - "linestyle": "solid" - }, - "_protocol": 3, - "subsets": [ - "1_0" - ], - "_type": "glue.core.data.Data", - "label": "image", - "coords": "Coordinates", - "components": [ - [ - "image_0", - "Component" - ], - [ - "Pixel y", - "CoordinateComponent" - ], - [ - "Pixel x", - "CoordinateComponent_0" - ], - [ - "World 0", - "CoordinateComponent_1" - ], - [ - "World 1", - "CoordinateComponent_2" - ] - ], - "_key_joins": [] - }, - "World 0": { - "hidden": true, - "_type": "glue.core.component_id.ComponentID", - "label": "World 0" - }, - "RoiSubsetState": { - "roi": "PolygonalROI", - "_type": "glue.core.subset.RoiSubsetState", - "xatt": "Pixel x", - "yatt": "Pixel y" - }, - "Pixel y": { - "hidden": true, - "_type": "glue.core.component_id.ComponentID", - "label": "Pixel y" - }, - "Pixel x": { - "hidden": true, - "_type": "glue.core.component_id.ComponentID", - "label": "Pixel x" - }, - "PolygonalROI": { - "vx": [ - 0.523465703971119, - 0.523465703971119, - 0.523465703971119, - 0.523465703971119, - 0.5451263537906135, - 0.5776173285198554, - 0.6642599277978336, - 0.8158844765342956, - 0.9675090252707577, - 1.1841155234657035, - 1.335740072202166, - 1.4765342960288805, - 1.5631768953068592, - 1.628158844765343, - 1.7039711191335734, - 1.7039711191335734, - 1.7039711191335734, - 1.6931407942238268, - 1.628158844765343, - 1.5415162454873643, - 1.454873646209386, - 1.3574007220216604, - 1.2599277978339347, - 1.162454873646209, - 1.0649819494584833, - 0.9675090252707577, - 0.8808664259927794, - 0.7942238267148012, - 0.707581227436823, - 0.6425992779783392, - 0.5992779783393498 - ], - "vy": [ - 1.9765342960288808, - 1.9548736462093863, - 1.868231046931408, - 1.7382671480144405, - 1.5108303249097472, - 1.4133574007220215, - 1.2942238267148014, - 1.2075812274368232, - 1.1859205776173285, - 1.1859205776173285, - 1.1859205776173285, - 1.2075812274368232, - 1.2184115523465704, - 1.2509025270758123, - 1.3375451263537905, - 1.4891696750902528, - 1.7707581227436824, - 2.0090252707581224, - 2.279783393501805, - 2.4530685920577615, - 2.5288808664259927, - 2.583032490974729, - 2.6263537906137184, - 2.6696750902527073, - 2.723826714801444, - 2.734657039711191, - 2.734657039711191, - 2.6805054151624548, - 2.6046931407942235, - 2.5288808664259927, - 2.4422382671480145 - ], - "_type": "glue.core.roi.PolygonalROI" - }, - "CoordinateComponentLink_1": { - "index": 0, - "_type": "glue.core.component_link.CoordinateComponentLink", - "frm": [ - "Pixel y", - "Pixel x" - ], - "to": [ - "World 0" - ], - "pix2world": true, - "coords": "Coordinates" - }, - "CoordinateComponentLink_0": { - "index": 1, - "_type": "glue.core.component_link.CoordinateComponentLink", - "frm": [ - "World 0", - "World 1" - ], - "to": [ - "Pixel x" - ], - "pix2world": false, - "coords": "Coordinates" - }, - "DS9Normalize": { - "vmax": 1.0, - "_type": "glue.viewers.image.ds9norm.DS9Normalize", - "bias": 0.5, - "vmin": 1.0, - "clip_hi": 95.0, - "stretch": "arcsinh", - "clip_lo": 5.0, - "contrast": 1.0 - }, - "DataCollection": { - "_type": "glue.core.data_collection.DataCollection", - "components": [ - "Component", - "CoordinateComponent", - "CoordinateComponent_0", - "CoordinateComponent_1", - "CoordinateComponent_2" - ], - "links": [ - "CoordinateComponentLink", - "CoordinateComponentLink_0", - "CoordinateComponentLink_1", - "CoordinateComponentLink_2" - ], - "_protocol": 2, - "groups": [ - "1" - ], - "cids": [ - "image_0", - "Pixel y", - "Pixel x", - "World 0", - "World 1" - ], - "data": [ - "image" - ] - }, - "Session": { - "_type": "glue.core.session.Session" - } -} \ No newline at end of file diff --git a/glue/tests/data/load_log_0.glu b/glue/tests/data/load_log_0.glu deleted file mode 100644 index ffd25caad..000000000 --- a/glue/tests/data/load_log_0.glu +++ /dev/null @@ -1,164 +0,0 @@ -{ - "Component": { - "_type": "glue.core.component.Component", - "log": "LoadLog", - "log_item": 0 - }, - "Component_0": { - "_type": "glue.core.component.Component", - "log": "LoadLog", - "log_item": 3 - }, - "CoordinateComponent": { - "_type": "glue.core.component.CoordinateComponent", - "axis": 0, - "world": false - }, - "CoordinateComponentLink": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "Pixel Axis 0 [x]" - ], - "index": 0, - "pix2world": true, - "to": [ - "World 0" - ] - }, - "CoordinateComponentLink_0": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "World 0" - ], - "index": 0, - "pix2world": false, - "to": [ - "Pixel Axis 0 [x]" - ] - }, - "CoordinateComponent_0": { - "_type": "glue.core.component.CoordinateComponent", - "axis": 0, - "world": true - }, - "Coordinates": { - "_type": "glue.core.coordinates.Coordinates" - }, - "DataCollection": { - "_protocol": 3, - "_type": "glue.core.data_collection.DataCollection", - "cids": [ - "a", - "Pixel Axis 0 [x]", - "World 0", - "b" - ], - "components": [ - "Component", - "CoordinateComponent", - "CoordinateComponent_0", - "Component_0" - ], - "data": [ - "simple" - ], - "groups": [], - "links": [ - "CoordinateComponentLink", - "CoordinateComponentLink_0" - ], - "subset_group_count": 0 - }, - "LoadLog": { - "_type": "glue.core.data_factories.helpers.LoadLog", - "factory": { - "_type": "types.FunctionType", - "function": "glue.core.data_factories.helpers.auto_data" - }, - "kwargs": [ - [] - ], - "path": "{DATA_PATH}simple.csv" - }, - "Pixel Axis 0 [x]": { - "_type": "glue.core.component_id.PixelComponentID", - "axis": 0, - "hidden": true, - "label": "Pixel Axis 0 [x]" - }, - "Session": { - "_type": "glue.core.session.Session" - }, - "World 0": { - "_type": "glue.core.component_id.ComponentID", - "hidden": true, - "label": "World 0" - }, - "__main__": { - "_type": "glue.app.qt.application.GlueApplication", - "data": "DataCollection", - "plugins": [ - ], - "session": "Session", - "tab_names": [ - "Tab 1" - ], - "viewers": [ - [] - ] - }, - "a": { - "_type": "glue.core.component_id.ComponentID", - "hidden": false, - "label": "a" - }, - "b": { - "_type": "glue.core.component_id.ComponentID", - "hidden": false, - "label": "b" - }, - "simple": { - "_key_joins": [], - "_protocol": 5, - "_type": "glue.core.data.Data", - "components": [ - [ - "a", - "Component" - ], - [ - "Pixel Axis 0 [x]", - "CoordinateComponent" - ], - [ - "World 0", - "CoordinateComponent_0" - ], - [ - "b", - "Component_0" - ] - ], - "coords": "Coordinates", - "label": "simple", - "primary_owner": [ - "a", - "Pixel Axis 0 [x]", - "World 0", - "b" - ], - "style": { - "_type": "glue.core.visual.VisualAttributes", - "alpha": 0.8, - "color": "0.35", - "linestyle": "solid", - "linewidth": 1, - "marker": "o", - "markersize": 3 - }, - "subsets": [], - "uuid": "fd317c94-7a6a-4ac2-b024-17e44bde3cb8" - } -} diff --git a/glue/tests/data/load_log_1.glu b/glue/tests/data/load_log_1.glu deleted file mode 100644 index 90bb2f634..000000000 --- a/glue/tests/data/load_log_1.glu +++ /dev/null @@ -1,165 +0,0 @@ -{ - "Component": { - "_type": "glue.core.component.Component", - "log": "LoadLog", - "log_item": 2 - }, - "Component_0": { - "_type": "glue.core.component.Component", - "log": "LoadLog", - "log_item": 3 - }, - "CoordinateComponent": { - "_type": "glue.core.component.CoordinateComponent", - "axis": 0, - "world": false - }, - "CoordinateComponentLink": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "World 0" - ], - "index": 0, - "pix2world": false, - "to": [ - "Pixel Axis 0 [x]" - ] - }, - "CoordinateComponentLink_0": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "Pixel Axis 0 [x]" - ], - "index": 0, - "pix2world": true, - "to": [ - "World 0" - ] - }, - "CoordinateComponent_0": { - "_type": "glue.core.component.CoordinateComponent", - "axis": 0, - "world": true - }, - "Coordinates": { - "_type": "glue.core.coordinates.Coordinates" - }, - "DataCollection": { - "_protocol": 3, - "_type": "glue.core.data_collection.DataCollection", - "cids": [ - "Pixel Axis 0 [x]", - "World 0", - "a", - "b" - ], - "components": [ - "CoordinateComponent", - "CoordinateComponent_0", - "Component", - "Component_0" - ], - "data": [ - "simple" - ], - "groups": [], - "links": [ - "CoordinateComponentLink", - "CoordinateComponentLink_0" - ], - "subset_group_count": 0 - }, - "LoadLog": { - "_protocol": 1, - "_type": "glue.core.data_factories.helpers.LoadLog", - "factory": { - "_type": "types.FunctionType", - "function": "glue.core.data_factories.helpers.auto_data" - }, - "kwargs": [ - [] - ], - "path": "{DATA_PATH}simple.csv" - }, - "Pixel Axis 0 [x]": { - "_type": "glue.core.component_id.PixelComponentID", - "axis": 0, - "hidden": true, - "label": "Pixel Axis 0 [x]" - }, - "Session": { - "_type": "glue.core.session.Session" - }, - "World 0": { - "_type": "glue.core.component_id.ComponentID", - "hidden": true, - "label": "World 0" - }, - "__main__": { - "_type": "glue.app.qt.application.GlueApplication", - "data": "DataCollection", - "plugins": [ - ], - "session": "Session", - "tab_names": [ - "Tab 1" - ], - "viewers": [ - [] - ] - }, - "a": { - "_type": "glue.core.component_id.ComponentID", - "hidden": false, - "label": "a" - }, - "b": { - "_type": "glue.core.component_id.ComponentID", - "hidden": false, - "label": "b" - }, - "simple": { - "_key_joins": [], - "_protocol": 5, - "_type": "glue.core.data.Data", - "components": [ - [ - "Pixel Axis 0 [x]", - "CoordinateComponent" - ], - [ - "World 0", - "CoordinateComponent_0" - ], - [ - "a", - "Component" - ], - [ - "b", - "Component_0" - ] - ], - "coords": "Coordinates", - "label": "simple", - "primary_owner": [ - "Pixel Axis 0 [x]", - "World 0", - "a", - "b" - ], - "style": { - "_type": "glue.core.visual.VisualAttributes", - "alpha": 0.8, - "color": "0.35", - "linestyle": "solid", - "linewidth": 1, - "marker": "o", - "markersize": 3 - }, - "subsets": [], - "uuid": "741368cc-0826-4e8e-aa11-9e8ac0a093cd" - } -} diff --git a/glue/tests/data/session_coordinate_links_013.glu b/glue/tests/data/session_coordinate_links_013.glu deleted file mode 100644 index f52ee9ef8..000000000 --- a/glue/tests/data/session_coordinate_links_013.glu +++ /dev/null @@ -1,335 +0,0 @@ -{ - "Component": { - "_type": "glue.core.component.Component", - "data": { - "_type": "numpy.ndarray", - "data": "k05VTVBZAQB2AHsnZGVzY3InOiAnPGk4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDMsKSwgfSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAoBAAAAAAAAAAIAAAAAAAAAAwAAAAAAAAA=" - }, - "units": "" - }, - "ComponentLink": { - "_type": "glue.core.component_link.ComponentLink", - "frm": [ - "x_0", - "y_0" - ], - "inverse": null, - "to": [ - "x" - ], - "using": { - "_type": "glue.core.link_helpers.PartialResult", - "func": { - "_type": "types.MethodType", - "instance": "Galactic_to_FK5", - "method": "forward" - }, - "index": 0 - } - }, - "ComponentLink_0": { - "_type": "glue.core.component_link.ComponentLink", - "frm": [ - "x", - "y" - ], - "inverse": null, - "to": [ - "x_0" - ], - "using": { - "_type": "glue.core.link_helpers.PartialResult", - "func": { - "_type": "types.MethodType", - "instance": "Galactic_to_FK5", - "method": "backward" - }, - "index": 0 - } - }, - "ComponentLink_1": { - "_type": "glue.core.component_link.ComponentLink", - "frm": [ - "x_0", - "y_0" - ], - "inverse": null, - "to": [ - "y" - ], - "using": { - "_type": "glue.core.link_helpers.PartialResult", - "func": { - "_type": "types.MethodType", - "instance": "Galactic_to_FK5", - "method": "forward" - }, - "index": 1 - } - }, - "ComponentLink_2": { - "_type": "glue.core.component_link.ComponentLink", - "frm": [ - "x", - "y" - ], - "inverse": null, - "to": [ - "y_0" - ], - "using": { - "_type": "glue.core.link_helpers.PartialResult", - "func": { - "_type": "types.MethodType", - "instance": "Galactic_to_FK5", - "method": "backward" - }, - "index": 1 - } - }, - "Component_0": { - "_type": "glue.core.component.Component", - "data": { - "_type": "numpy.ndarray", - "data": "k05VTVBZAQB2AHsnZGVzY3InOiAnPGk4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDMsKSwgfSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAoCAAAAAAAAAAMAAAAAAAAABAAAAAAAAAA=" - }, - "units": "" - }, - "Component_1": { - "_type": "glue.core.component.Component", - "data": { - "_type": "numpy.ndarray", - "data": "k05VTVBZAQB2AHsnZGVzY3InOiAnPGk4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDMsKSwgfSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAoBAAAAAAAAAAIAAAAAAAAAAwAAAAAAAAA=" - }, - "units": "" - }, - "Component_2": { - "_type": "glue.core.component.Component", - "data": { - "_type": "numpy.ndarray", - "data": "k05VTVBZAQB2AHsnZGVzY3InOiAnPGk4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDMsKSwgfSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAoCAAAAAAAAAAMAAAAAAAAABAAAAAAAAAA=" - }, - "units": "" - }, - "CoordinateComponent": { - "_type": "glue.core.component.CoordinateComponent", - "axis": 0, - "world": false - }, - "CoordinateComponent_0": { - "_type": "glue.core.component.CoordinateComponent", - "axis": 0, - "world": true - }, - "CoordinateComponent_1": { - "_type": "glue.core.component.CoordinateComponent", - "axis": 0, - "world": false - }, - "CoordinateComponent_2": { - "_type": "glue.core.component.CoordinateComponent", - "axis": 0, - "world": true - }, - "Coordinates": { - "_type": "glue.core.coordinates.Coordinates" - }, - "Coordinates_0": { - "_type": "glue.core.coordinates.Coordinates" - }, - "DataCollection": { - "_protocol": 4, - "_type": "glue.core.data_collection.DataCollection", - "cids": [ - "Pixel Axis 0 [x]", - "World 0", - "x", - "y", - "Pixel Axis 0 [x]_0", - "World 0_0", - "x_0", - "y_0" - ], - "components": [ - "CoordinateComponent", - "CoordinateComponent_0", - "Component", - "Component_0", - "CoordinateComponent_1", - "CoordinateComponent_2", - "Component_1", - "Component_2" - ], - "data": [ - "data1", - "data2" - ], - "groups": [], - "links": [ - "ComponentLink", - "ComponentLink_0", - "ComponentLink_1", - "ComponentLink_2" - ], - "subset_group_count": 0 - }, - "Galactic_to_FK5": { - "_type": "glue.plugins.coordinate_helpers.link_helpers.Galactic_to_FK5", - "cids": [ - "x_0", - "y_0", - "x", - "y" - ] - }, - "Pixel Axis 0 [x]": { - "_type": "glue.core.component_id.PixelComponentID", - "axis": 0, - "label": "Pixel Axis 0 [x]" - }, - "Pixel Axis 0 [x]_0": { - "_type": "glue.core.component_id.PixelComponentID", - "axis": 0, - "label": "Pixel Axis 0 [x]" - }, - "Session": { - "_type": "glue.core.session.Session" - }, - "World 0": { - "_type": "glue.core.component_id.ComponentID", - "label": "World 0" - }, - "World 0_0": { - "_type": "glue.core.component_id.ComponentID", - "label": "World 0" - }, - "__main__": { - "_type": "glue.app.qt.application.GlueApplication", - "data": "DataCollection", - "plugins": [ - "glue.plugins.tools", - "glue.plugins.export_d3po", - "glue.core.data_exporters", - "mosviz", - "glue.io.formats.fits", - "glue.plugins.tools.pv_slicer", - "glue.plugins.coordinate_helpers", - "glue.plugins.data_factories.spectral_cube" - ], - "session": "Session", - "tab_names": [ - "Tab 1" - ], - "viewers": [ - [] - ] - }, - "data1": { - "_key_joins": [], - "_protocol": 5, - "_type": "glue.core.data.Data", - "components": [ - [ - "Pixel Axis 0 [x]", - "CoordinateComponent" - ], - [ - "World 0", - "CoordinateComponent_0" - ], - [ - "x", - "Component" - ], - [ - "y", - "Component_0" - ] - ], - "coords": "Coordinates", - "label": "data1", - "meta": { - "_type": "collections.OrderedDict", - "contents": {} - }, - "primary_owner": [ - "Pixel Axis 0 [x]", - "World 0", - "x", - "y" - ], - "style": { - "_type": "glue.core.visual.VisualAttributes", - "alpha": 0.8, - "color": "#919191", - "linestyle": "solid", - "linewidth": 1, - "marker": "o", - "markersize": 3 - }, - "subsets": [], - "uuid": "4eb0477a-dc4c-4062-8245-4249124f66b0" - }, - "data2": { - "_key_joins": [], - "_protocol": 5, - "_type": "glue.core.data.Data", - "components": [ - [ - "Pixel Axis 0 [x]_0", - "CoordinateComponent_1" - ], - [ - "World 0_0", - "CoordinateComponent_2" - ], - [ - "x_0", - "Component_1" - ], - [ - "y_0", - "Component_2" - ] - ], - "coords": "Coordinates_0", - "label": "data2", - "meta": { - "_type": "collections.OrderedDict", - "contents": {} - }, - "primary_owner": [ - "Pixel Axis 0 [x]_0", - "World 0_0", - "x_0", - "y_0" - ], - "style": { - "_type": "glue.core.visual.VisualAttributes", - "alpha": 0.8, - "color": "#919191", - "linestyle": "solid", - "linewidth": 1, - "marker": "o", - "markersize": 3 - }, - "subsets": [], - "uuid": "a0d5cba0-9770-4b38-8682-82c80854b109" - }, - "x": { - "_type": "glue.core.component_id.ComponentID", - "label": "x" - }, - "x_0": { - "_type": "glue.core.component_id.ComponentID", - "label": "x" - }, - "y": { - "_type": "glue.core.component_id.ComponentID", - "label": "y" - }, - "y_0": { - "_type": "glue.core.component_id.ComponentID", - "label": "y" - } -} \ No newline at end of file diff --git a/glue/tests/data/session_links.glu b/glue/tests/data/session_links.glu deleted file mode 100644 index 7e68cf769..000000000 --- a/glue/tests/data/session_links.glu +++ /dev/null @@ -1,456 +0,0 @@ -{ - "World 0_0": { - "hidden": true, - "_type": "glue.core.data.ComponentID", - "label": "World 0" - }, - "CoordinateComponent_2": { - "world": true, - "_type": "glue.core.data.CoordinateComponent", - "axis": 0 - }, - "CoordinateComponent_1": { - "world": false, - "_type": "glue.core.data.CoordinateComponent", - "axis": 0 - }, - "CoordinateComponent_0": { - "world": true, - "_type": "glue.core.data.CoordinateComponent", - "axis": 0 - }, - "Coordinates_0": { - "_type": "glue.core.coordinates.Coordinates" - }, - "Coordinates": { - "_type": "glue.core.coordinates.Coordinates" - }, - "DerivedComponent": { - "_type": "glue.core.data.DerivedComponent", - "link": "ComponentLink" - }, - "__main__": { - "_type": "glue.qt.glue_application.GlueApplication", - "session": "Session", - "data": "DataCollection", - "viewers": [ - [] - ] - }, - "DerivedComponent_2": { - "_type": "glue.core.data.DerivedComponent", - "link": "ComponentLink_2" - }, - "DerivedComponent_1": { - "_type": "glue.core.data.DerivedComponent", - "link": "ComponentLink_0" - }, - "DerivedComponent_0": { - "_type": "glue.core.data.DerivedComponent", - "link": "ComponentLink_1" - }, - "CoordinateComponent": { - "world": false, - "_type": "glue.core.data.CoordinateComponent", - "axis": 0 - }, - "ComponentLink": { - "_type": "glue.core.component_link.ComponentLink", - "inverse": null, - "frm": [ - "l", - "b" - ], - "to": [ - "ra" - ], - "using": { - "index": 0, - "_type": "glue.core.link_helpers.PartialResult", - "func": { - "function": "glue.external.aplpy.gal2fk5", - "_type": "types.FunctionType" - } - }, - "hidden": false - }, - "CoordinateComponentLink": { - "index": 0, - "_type": "glue.core.component_link.CoordinateComponentLink", - "frm": [ - "Pixel Axis 0_0" - ], - "to": [ - "World 0_0" - ], - "pix2world": true, - "coords": "Coordinates" - }, - "ra": { - "hidden": false, - "_type": "glue.core.data.ComponentID", - "label": "ra" - }, - "ComponentLink_2": { - "_type": "glue.core.component_link.ComponentLink", - "inverse": null, - "frm": [ - "ra", - "dec" - ], - "to": [ - "b" - ], - "using": { - "function": "glue.core.link_helpers.radec2glat", - "_type": "types.FunctionType" - }, - "hidden": false - }, - "ComponentLink_1": { - "_type": "glue.core.component_link.ComponentLink", - "inverse": null, - "frm": [ - "l", - "b" - ], - "to": [ - "dec" - ], - "using": { - "index": 1, - "_type": "glue.core.link_helpers.PartialResult", - "func": { - "function": "glue.external.aplpy.gal2fk5", - "_type": "types.FunctionType" - } - }, - "hidden": false - }, - "World 0": { - "hidden": true, - "_type": "glue.core.data.ComponentID", - "label": "World 0" - }, - "Pixel Axis 0_0": { - "hidden": true, - "_type": "glue.core.data.ComponentID", - "label": "Pixel Axis 0" - }, - "Pixel Axis 0": { - "hidden": true, - "_type": "glue.core.data.ComponentID", - "label": "Pixel Axis 0" - }, - "ComponentLink_3": { - "_type": "glue.core.component_link.ComponentLink", - "inverse": null, - "frm": [ - "l", - "b" - ], - "to": [ - "dec" - ], - "using": { - "function": "glue.core.link_helpers.lb2dec", - "_type": "types.FunctionType" - }, - "hidden": false - }, - "CoordinateComponentLink_2": { - "index": 0, - "_type": "glue.core.component_link.CoordinateComponentLink", - "frm": [ - "Pixel Axis 0" - ], - "to": [ - "World 0" - ], - "pix2world": true, - "coords": "Coordinates_0" - }, - "CoordinateComponentLink_1": { - "index": 0, - "_type": "glue.core.component_link.CoordinateComponentLink", - "frm": [ - "World 0" - ], - "to": [ - "Pixel Axis 0" - ], - "pix2world": false, - "coords": "Coordinates_0" - }, - "Component": { - "units": null, - "_type": "glue.core.data.Component", - "data": { - "_type": "numpy.ndarray", - "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGk4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDMsKSwgfSAgICAgICAgICAgIAoBAAAAAAAAAAIAAAAAAAAAAwAAAAAAAAA=" - } - }, - "ComponentLink_6": { - "_type": "glue.core.component_link.ComponentLink", - "inverse": null, - "frm": [ - "l", - "b" - ], - "to": [ - "ra" - ], - "using": { - "function": "glue.core.link_helpers.lb2ra", - "_type": "types.FunctionType" - }, - "hidden": false - }, - "ComponentLink_5": { - "_type": "glue.core.component_link.ComponentLink", - "inverse": null, - "frm": [ - "ra", - "dec" - ], - "to": [ - "b" - ], - "using": { - "index": 1, - "_type": "glue.core.link_helpers.PartialResult", - "func": { - "function": "glue.external.aplpy.fk52gal", - "_type": "types.FunctionType" - } - }, - "hidden": false - }, - "ComponentLink_4": { - "_type": "glue.core.component_link.ComponentLink", - "inverse": null, - "frm": [ - "ra", - "dec" - ], - "to": [ - "l" - ], - "using": { - "index": 0, - "_type": "glue.core.link_helpers.PartialResult", - "func": { - "function": "glue.external.aplpy.fk52gal", - "_type": "types.FunctionType" - } - }, - "hidden": false - }, - "CoordinateComponentLink_0": { - "index": 0, - "_type": "glue.core.component_link.CoordinateComponentLink", - "frm": [ - "World 0_0" - ], - "to": [ - "Pixel Axis 0_0" - ], - "pix2world": false, - "coords": "Coordinates" - }, - "b": { - "hidden": false, - "_type": "glue.core.data.ComponentID", - "label": "b" - }, - "Component_2": { - "units": null, - "_type": "glue.core.data.Component", - "data": { - "_type": "numpy.ndarray", - "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGk4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDMsKSwgfSAgICAgICAgICAgIAoBAAAAAAAAAAIAAAAAAAAAAwAAAAAAAAA=" - } - }, - "Component_1": { - "units": null, - "_type": "glue.core.data.Component", - "data": { - "_type": "numpy.ndarray", - "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGk4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDMsKSwgfSAgICAgICAgICAgIAoBAAAAAAAAAAIAAAAAAAAAAwAAAAAAAAA=" - } - }, - "Component_0": { - "units": null, - "_type": "glue.core.data.Component", - "data": { - "_type": "numpy.ndarray", - "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGk4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDMsKSwgfSAgICAgICAgICAgIAoBAAAAAAAAAAIAAAAAAAAAAwAAAAAAAAA=" - } - }, - "t2": { - "style": { - "_type": "glue.core.visual.VisualAttributes", - "color": "#373737", - "markersize": 3, - "marker": "o", - "alpha": 0.5, - "linewidth": 1, - "linestyle": "solid" - }, - "_protocol": 3, - "subsets": [], - "_type": "glue.core.data.Data", - "label": "t2", - "coords": "Coordinates_0", - "components": [ - [ - "b", - "Component" - ], - [ - "Pixel Axis 0", - "CoordinateComponent" - ], - [ - "World 0", - "CoordinateComponent_0" - ], - [ - "l", - "Component_0" - ], - [ - "ra", - "DerivedComponent" - ], - [ - "dec", - "DerivedComponent_0" - ] - ], - "_key_joins": [] - }, - "l": { - "hidden": false, - "_type": "glue.core.data.ComponentID", - "label": "l" - }, - "t1": { - "style": { - "_type": "glue.core.visual.VisualAttributes", - "color": "#373737", - "markersize": 3, - "marker": "o", - "alpha": 0.5, - "linewidth": 1, - "linestyle": "solid" - }, - "_protocol": 3, - "subsets": [], - "_type": "glue.core.data.Data", - "label": "t1", - "coords": "Coordinates", - "components": [ - [ - "dec", - "Component_1" - ], - [ - "Pixel Axis 0_0", - "CoordinateComponent_1" - ], - [ - "World 0_0", - "CoordinateComponent_2" - ], - [ - "ra", - "Component_2" - ], - [ - "l", - "DerivedComponent_1" - ], - [ - "b", - "DerivedComponent_2" - ] - ], - "_key_joins": [] - }, - "DataCollection": { - "_type": "glue.core.data_collection.DataCollection", - "components": [ - "Component", - "CoordinateComponent", - "CoordinateComponent_0", - "Component_0", - "DerivedComponent", - "DerivedComponent_0", - "Component_1", - "CoordinateComponent_1", - "CoordinateComponent_2", - "Component_2", - "DerivedComponent_1", - "DerivedComponent_2" - ], - "links": [ - "ComponentLink", - "ComponentLink_0", - "ComponentLink_1", - "CoordinateComponentLink", - "ComponentLink_2", - "CoordinateComponentLink_0", - "ComponentLink_3", - "ComponentLink_4", - "ComponentLink_5", - "CoordinateComponentLink_1", - "ComponentLink_6", - "CoordinateComponentLink_2" - ], - "_protocol": 2, - "groups": [], - "cids": [ - "b", - "Pixel Axis 0", - "World 0", - "l", - "ra", - "dec", - "dec", - "Pixel Axis 0_0", - "World 0_0", - "ra", - "l", - "b" - ], - "data": [ - "t2", - "t1" - ] - }, - "Session": { - "_type": "glue.core.session.Session" - }, - "dec": { - "hidden": false, - "_type": "glue.core.data.ComponentID", - "label": "dec" - }, - "ComponentLink_0": { - "_type": "glue.core.component_link.ComponentLink", - "inverse": null, - "frm": [ - "ra", - "dec" - ], - "to": [ - "l" - ], - "using": { - "function": "glue.core.link_helpers.radec2glon", - "_type": "types.FunctionType" - }, - "hidden": false - } -} \ No newline at end of file diff --git a/glue/tests/data/simple.csv b/glue/tests/data/simple.csv deleted file mode 100644 index fa0e8a7a0..000000000 --- a/glue/tests/data/simple.csv +++ /dev/null @@ -1,4 +0,0 @@ -a,b -1,2 -3,2 -5,3 diff --git a/glue/tests/data/simple_hdf5_grid.glu b/glue/tests/data/simple_hdf5_grid.glu deleted file mode 100644 index 9fbacf9fd..000000000 --- a/glue/tests/data/simple_hdf5_grid.glu +++ /dev/null @@ -1,498 +0,0 @@ -{ - "CoordinateComponent_9": { - "world": true, - "_type": "glue.core.data.CoordinateComponent", - "axis": 1 - }, - "CoordinateComponent_8": { - "world": true, - "_type": "glue.core.data.CoordinateComponent", - "axis": 0 - }, - "World 0_0": { - "hidden": true, - "_type": "glue.core.data.ComponentID", - "label": "World 0" - }, - "World 2": { - "hidden": true, - "_type": "glue.core.data.ComponentID", - "label": "World 2" - }, - "World 1": { - "hidden": true, - "_type": "glue.core.data.ComponentID", - "label": "World 1" - }, - "CoordinateComponent_0": { - "world": false, - "_type": "glue.core.data.CoordinateComponent", - "axis": 1 - }, - "single_grid": { - "style": { - "_type": "glue.core.visual.VisualAttributes", - "color": "#373737", - "markersize": 3, - "marker": "o", - "alpha": 0.5, - "linewidth": 1, - "linestyle": "solid" - }, - "_protocol": 3, - "subsets": [], - "_type": "glue.core.data.Data", - "label": "single_grid", - "coords": "Coordinates", - "components": [ - [ - "/array1_0", - "Component_0" - ], - [ - "Pixel z_0", - "CoordinateComponent_5" - ], - [ - "Pixel y_0", - "CoordinateComponent_6" - ], - [ - "Pixel x_0", - "CoordinateComponent_7" - ], - [ - "World 0_0", - "CoordinateComponent_8" - ], - [ - "World 1_0", - "CoordinateComponent_9" - ], - [ - "World 2_0", - "CoordinateComponent_10" - ] - ], - "_key_joins": [] - }, - "CoordinateComponent_6": { - "world": false, - "_type": "glue.core.data.CoordinateComponent", - "axis": 1 - }, - "CoordinateComponent_5": { - "world": false, - "_type": "glue.core.data.CoordinateComponent", - "axis": 0 - }, - "CoordinateComponent_4": { - "world": true, - "_type": "glue.core.data.CoordinateComponent", - "axis": 2 - }, - "World 1_0": { - "hidden": true, - "_type": "glue.core.data.ComponentID", - "label": "World 1" - }, - "CoordinateComponentLink_6": { - "index": 2, - "_type": "glue.core.component_link.CoordinateComponentLink", - "frm": [ - "World 0", - "World 1", - "World 2" - ], - "to": [ - "Pixel x" - ], - "pix2world": false, - "coords": "Coordinates_0" - }, - "CoordinateComponentLink_10": { - "index": 2, - "_type": "glue.core.component_link.CoordinateComponentLink", - "frm": [ - "World 0_0", - "World 1_0", - "World 2_0" - ], - "to": [ - "Pixel x_0" - ], - "pix2world": false, - "coords": "Coordinates" - }, - "__main__": { - "_type": "glue.qt.glue_application.GlueApplication", - "session": "Session", - "data": "DataCollection", - "viewers": [ - [] - ] - }, - "CoordinateComponentLink_2": { - "index": 1, - "_type": "glue.core.component_link.CoordinateComponentLink", - "frm": [ - "World 0", - "World 1", - "World 2" - ], - "to": [ - "Pixel y" - ], - "pix2world": false, - "coords": "Coordinates_0" - }, - "Pixel x_0": { - "hidden": true, - "_type": "glue.core.data.ComponentID", - "label": "Pixel x" - }, - "World 2_0": { - "hidden": true, - "_type": "glue.core.data.ComponentID", - "label": "World 2" - }, - "Component": { - "log_item": 0, - "_type": "glue.core.data.Component", - "log": "LoadLog" - }, - "Pixel y_0": { - "hidden": true, - "_type": "glue.core.data.ComponentID", - "label": "Pixel y" - }, - "CoordinateComponent_3": { - "world": true, - "_type": "glue.core.data.CoordinateComponent", - "axis": 1 - }, - "Pixel z_0": { - "hidden": true, - "_type": "glue.core.data.ComponentID", - "label": "Pixel z" - }, - "CoordinateComponentLink": { - "index": 0, - "_type": "glue.core.component_link.CoordinateComponentLink", - "frm": [ - "World 0", - "World 1", - "World 2" - ], - "to": [ - "Pixel z" - ], - "pix2world": false, - "coords": "Coordinates_0" - }, - "/array1_0": { - "hidden": false, - "_type": "glue.core.data.ComponentID", - "label": "/array1" - }, - "CoordinateComponent_2": { - "world": true, - "_type": "glue.core.data.CoordinateComponent", - "axis": 0 - }, - "CoordinateComponent_10": { - "world": true, - "_type": "glue.core.data.CoordinateComponent", - "axis": 2 - }, - "CoordinateComponent_1": { - "world": false, - "_type": "glue.core.data.CoordinateComponent", - "axis": 2 - }, - "CoordinateComponentLink_9": { - "index": 0, - "_type": "glue.core.component_link.CoordinateComponentLink", - "frm": [ - "Pixel z", - "Pixel y", - "Pixel x" - ], - "to": [ - "World 0" - ], - "pix2world": true, - "coords": "Coordinates_0" - }, - "World 0": { - "hidden": true, - "_type": "glue.core.data.ComponentID", - "label": "World 0" - }, - "Pixel z": { - "hidden": true, - "_type": "glue.core.data.ComponentID", - "label": "Pixel z" - }, - "Pixel y": { - "hidden": true, - "_type": "glue.core.data.ComponentID", - "label": "Pixel y" - }, - "Pixel x": { - "hidden": true, - "_type": "glue.core.data.ComponentID", - "label": "Pixel x" - }, - "CoordinateComponentLink_3": { - "index": 2, - "_type": "glue.core.component_link.CoordinateComponentLink", - "frm": [ - "Pixel z_0", - "Pixel y_0", - "Pixel x_0" - ], - "to": [ - "World 2_0" - ], - "pix2world": true, - "coords": "Coordinates" - }, - "CoordinateComponent_7": { - "world": false, - "_type": "glue.core.data.CoordinateComponent", - "axis": 2 - }, - "CoordinateComponentLink_1": { - "index": 0, - "_type": "glue.core.component_link.CoordinateComponentLink", - "frm": [ - "World 0_0", - "World 1_0", - "World 2_0" - ], - "to": [ - "Pixel z_0" - ], - "pix2world": false, - "coords": "Coordinates" - }, - "CoordinateComponentLink_0": { - "index": 1, - "_type": "glue.core.component_link.CoordinateComponentLink", - "frm": [ - "Pixel z", - "Pixel y", - "Pixel x" - ], - "to": [ - "World 1" - ], - "pix2world": true, - "coords": "Coordinates_0" - }, - "CoordinateComponentLink_7": { - "index": 1, - "_type": "glue.core.component_link.CoordinateComponentLink", - "frm": [ - "Pixel z_0", - "Pixel y_0", - "Pixel x_0" - ], - "to": [ - "World 1_0" - ], - "pix2world": true, - "coords": "Coordinates" - }, - "LoadLog": { - "path": "{DATA_PATH}single_grid.hdf5", - "_type": "glue.core.data_factories.LoadLog", - "factory": { - "function": "glue.core.data_factories.auto_data", - "_type": "types.FunctionType" - }, - "kwargs": [ - [] - ] - }, - "CoordinateComponentLink_5": { - "index": 0, - "_type": "glue.core.component_link.CoordinateComponentLink", - "frm": [ - "Pixel z_0", - "Pixel y_0", - "Pixel x_0" - ], - "to": [ - "World 0_0" - ], - "pix2world": true, - "coords": "Coordinates" - }, - "Coordinates_0": { - "_type": "glue.core.coordinates.Coordinates" - }, - "Coordinates": { - "_type": "glue.core.coordinates.Coordinates" - }, - "CoordinateComponent": { - "world": false, - "_type": "glue.core.data.CoordinateComponent", - "axis": 0 - }, - "CoordinateComponentLink_4": { - "index": 2, - "_type": "glue.core.component_link.CoordinateComponentLink", - "frm": [ - "Pixel z", - "Pixel y", - "Pixel x" - ], - "to": [ - "World 2" - ], - "pix2world": true, - "coords": "Coordinates_0" - }, - "CoordinateComponentLink_8": { - "index": 1, - "_type": "glue.core.component_link.CoordinateComponentLink", - "frm": [ - "World 0_0", - "World 1_0", - "World 2_0" - ], - "to": [ - "Pixel y_0" - ], - "pix2world": false, - "coords": "Coordinates" - }, - "/array1": { - "hidden": false, - "_type": "glue.core.data.ComponentID", - "label": "/array1" - }, - "Component_0": { - "log_item": 0, - "_type": "glue.core.data.Component", - "log": "LoadLog_0" - }, - "DataCollection": { - "_type": "glue.core.data_collection.DataCollection", - "components": [ - "Component", - "CoordinateComponent", - "CoordinateComponent_0", - "CoordinateComponent_1", - "CoordinateComponent_2", - "CoordinateComponent_3", - "CoordinateComponent_4", - "Component_0", - "CoordinateComponent_5", - "CoordinateComponent_6", - "CoordinateComponent_7", - "CoordinateComponent_8", - "CoordinateComponent_9", - "CoordinateComponent_10" - ], - "links": [ - "CoordinateComponentLink", - "CoordinateComponentLink_0", - "CoordinateComponentLink_1", - "CoordinateComponentLink_2", - "CoordinateComponentLink_3", - "CoordinateComponentLink_4", - "CoordinateComponentLink_5", - "CoordinateComponentLink_6", - "CoordinateComponentLink_7", - "CoordinateComponentLink_8", - "CoordinateComponentLink_9", - "CoordinateComponentLink_10" - ], - "_protocol": 2, - "groups": [], - "cids": [ - "/array1", - "Pixel z", - "Pixel y", - "Pixel x", - "World 0", - "World 1", - "World 2", - "/array1_0", - "Pixel z_0", - "Pixel y_0", - "Pixel x_0", - "World 0_0", - "World 1_0", - "World 2_0" - ], - "data": [ - "single_grid_auto", - "single_grid" - ] - }, - "Session": { - "_type": "glue.core.session.Session" - }, - "LoadLog_0": { - "path": "{DATA_PATH}single_grid.hdf5", - "_type": "glue.core.data_factories.LoadLog", - "factory": { - "function": "glue.core.data_factories.gridded_data", - "_type": "types.FunctionType" - }, - "kwargs": [ - [] - ] - }, - "single_grid_auto": { - "style": { - "_type": "glue.core.visual.VisualAttributes", - "color": "#373737", - "markersize": 3, - "marker": "o", - "alpha": 0.5019607843137255, - "linewidth": 1, - "linestyle": "solid" - }, - "_protocol": 3, - "subsets": [], - "_type": "glue.core.data.Data", - "label": "single_grid_auto", - "coords": "Coordinates_0", - "components": [ - [ - "/array1", - "Component" - ], - [ - "Pixel z", - "CoordinateComponent" - ], - [ - "Pixel y", - "CoordinateComponent_0" - ], - [ - "Pixel x", - "CoordinateComponent_1" - ], - [ - "World 0", - "CoordinateComponent_2" - ], - [ - "World 1", - "CoordinateComponent_3" - ], - [ - "World 2", - "CoordinateComponent_4" - ] - ], - "_key_joins": [] - } -} \ No newline at end of file diff --git a/glue/tests/data/simple_table_015.glu b/glue/tests/data/simple_table_015.glu deleted file mode 100644 index 0f731efd4..000000000 --- a/glue/tests/data/simple_table_015.glu +++ /dev/null @@ -1,152 +0,0 @@ -{ - "Component": { - "_type": "glue.core.component.Component", - "log": "LoadLog", - "log_item": 2 - }, - "Component_0": { - "_type": "glue.core.component.Component", - "log": "LoadLog", - "log_item": 3 - }, - "CoordinateComponent": { - "_type": "glue.core.component.CoordinateComponent", - "axis": 0, - "world": false - }, - "CoordinateComponent_0": { - "_type": "glue.core.component.CoordinateComponent", - "axis": 0, - "world": true - }, - "Coordinates": { - "_type": "glue.core.coordinates.Coordinates" - }, - "DataCollection": { - "_protocol": 4, - "_type": "glue.core.data_collection.DataCollection", - "cids": [ - "Pixel Axis 0 [x]", - "World 0", - "a", - "b" - ], - "components": [ - "CoordinateComponent", - "CoordinateComponent_0", - "Component", - "Component_0" - ], - "data": [ - "single_table[HDU1]" - ], - "groups": [], - "links": [], - "subset_group_count": 0 - }, - "LoadLog": { - "_protocol": 1, - "_type": "glue.core.data_factories.helpers.LoadLog", - "factory": { - "_type": "types.FunctionType", - "function": "glue.core.data_factories.fits.fits_reader" - }, - "kwargs": [ - [] - ], - "path": "{DATA_PATH}/single_table.fits" - }, - "Pixel Axis 0 [x]": { - "_type": "glue.core.component_id.PixelComponentID", - "axis": 0, - "label": "Pixel Axis 0 [x]" - }, - "Session": { - "_type": "glue.core.session.Session" - }, - "World 0": { - "_type": "glue.core.component_id.ComponentID", - "label": "World 0" - }, - "__main__": { - "_type": "glue.app.qt.application.GlueApplication", - "data": "DataCollection", - "plugins": [ - "glue.viewers.profile.qt", - "glue.core.data_exporters", - "glue.io.formats.fits", - "glue.plugins.data_factories.spectral_cube", - "glue.plugins.tools.pv_slicer", - "glue.viewers.histogram.qt", - "glue.plugins.coordinate_helpers", - "glue.viewers.image.qt", - "glue.plugins.export_d3po", - "glue.plugins.tools", - "glue.viewers.scatter.qt", - "glue.plugins.wcs_autolinking", - "glue.plugins.dendro_viewer.qt", - "glue.viewers.table.qt" - ], - "session": "Session", - "tab_names": [ - "Tab 1" - ], - "viewers": [ - [] - ] - }, - "a": { - "_type": "glue.core.component_id.ComponentID", - "label": "a" - }, - "b": { - "_type": "glue.core.component_id.ComponentID", - "label": "b" - }, - "single_table[HDU1]": { - "_key_joins": [], - "_protocol": 5, - "_type": "glue.core.data.Data", - "components": [ - [ - "Pixel Axis 0 [x]", - "CoordinateComponent" - ], - [ - "World 0", - "CoordinateComponent_0" - ], - [ - "a", - "Component" - ], - [ - "b", - "Component_0" - ] - ], - "coords": "Coordinates", - "label": "single_table[HDU1]", - "meta": { - "_type": "collections.OrderedDict", - "contents": {} - }, - "primary_owner": [ - "Pixel Axis 0 [x]", - "World 0", - "a", - "b" - ], - "style": { - "_type": "glue.core.visual.VisualAttributes", - "alpha": 0.8, - "color": "#595959", - "linestyle": "solid", - "linewidth": 1, - "marker": "o", - "markersize": 3 - }, - "subsets": [], - "uuid": "d804c55e-ebee-4850-a0f1-3b854bda62b4" - } -} diff --git a/glue/tests/data/simple_table_resaved.glu b/glue/tests/data/simple_table_resaved.glu deleted file mode 100644 index b39b2344d..000000000 --- a/glue/tests/data/simple_table_resaved.glu +++ /dev/null @@ -1,152 +0,0 @@ -{ - "Component": { - "_type": "glue.core.component.Component", - "log": "LoadLog", - "log_item": 2 - }, - "Component_0": { - "_type": "glue.core.component.Component", - "log": "LoadLog", - "log_item": 3 - }, - "CoordinateComponent": { - "_type": "glue.core.component.CoordinateComponent", - "axis": 0, - "world": false - }, - "CoordinateComponent_0": { - "_type": "glue.core.component.CoordinateComponent", - "axis": 0, - "world": true - }, - "Coordinates": { - "_type": "glue.core.coordinates.Coordinates" - }, - "DataCollection": { - "_protocol": 4, - "_type": "glue.core.data_collection.DataCollection", - "cids": [ - "Pixel Axis 0 [x]", - "World 0", - "a", - "b" - ], - "components": [ - "CoordinateComponent", - "CoordinateComponent_0", - "Component", - "Component_0" - ], - "data": [ - "single_table[HDU1]" - ], - "groups": [], - "links": [], - "subset_group_count": 0 - }, - "LoadLog": { - "_protocol": 2, - "_type": "glue.core.data_factories.helpers.LoadLog", - "factory": { - "_type": "types.FunctionType", - "function": "glue.core.data_factories.fits.fits_reader" - }, - "kwargs": [ - [] - ], - "path": "{DATA_PATH}/single_table.fits" - }, - "Pixel Axis 0 [x]": { - "_type": "glue.core.component_id.PixelComponentID", - "axis": 0, - "label": "Pixel Axis 0 [x]" - }, - "Session": { - "_type": "glue.core.session.Session" - }, - "World 0": { - "_type": "glue.core.component_id.ComponentID", - "label": "World 0" - }, - "__main__": { - "_type": "glue.app.qt.application.GlueApplication", - "data": "DataCollection", - "plugins": [ - "glue.viewers.scatter.qt", - "glue.plugins.data_factories.spectral_cube", - "glue.core.data_exporters", - "glue.viewers.image.qt", - "glue.plugins.coordinate_helpers", - "glue.plugins.tools", - "glue.viewers.table.qt", - "glue.plugins.dendro_viewer.qt", - "glue.viewers.histogram.qt", - "glue.plugins.wcs_autolinking", - "glue.io.formats.fits", - "glue.plugins.tools.pv_slicer", - "glue.plugins.export_d3po", - "glue.viewers.profile.qt" - ], - "session": "Session", - "tab_names": [ - "Tab 1" - ], - "viewers": [ - [] - ] - }, - "a": { - "_type": "glue.core.component_id.ComponentID", - "label": "a" - }, - "b": { - "_type": "glue.core.component_id.ComponentID", - "label": "b" - }, - "single_table[HDU1]": { - "_key_joins": [], - "_protocol": 5, - "_type": "glue.core.data.Data", - "components": [ - [ - "Pixel Axis 0 [x]", - "CoordinateComponent" - ], - [ - "World 0", - "CoordinateComponent_0" - ], - [ - "a", - "Component" - ], - [ - "b", - "Component_0" - ] - ], - "coords": "Coordinates", - "label": "single_table[HDU1]", - "meta": { - "_type": "collections.OrderedDict", - "contents": {} - }, - "primary_owner": [ - "Pixel Axis 0 [x]", - "World 0", - "a", - "b" - ], - "style": { - "_type": "glue.core.visual.VisualAttributes", - "alpha": 0.8, - "color": "#595959", - "linestyle": "solid", - "linewidth": 1, - "marker": "o", - "markersize": 3 - }, - "subsets": [], - "uuid": "d804c55e-ebee-4850-a0f1-3b854bda62b4" - } -} diff --git a/glue/tests/data/simple_tables.glu b/glue/tests/data/simple_tables.glu deleted file mode 100644 index 7775bcc64..000000000 --- a/glue/tests/data/simple_tables.glu +++ /dev/null @@ -1,522 +0,0 @@ -{ - "LoadLog": { - "path": "{DATA_PATH}single_table.fits", - "_type": "glue.core.data_factories.LoadLog", - "factory": { - "function": "glue.core.data_factories.auto_data", - "_type": "types.FunctionType" - }, - "kwargs": [ - [] - ] - }, - "a_2": { - "hidden": false, - "_type": "glue.core.data.ComponentID", - "label": "a" - }, - "a_1": { - "hidden": false, - "_type": "glue.core.data.ComponentID", - "label": "a" - }, - "a_0": { - "hidden": false, - "_type": "glue.core.data.ComponentID", - "label": "a" - }, - "World 0_0": { - "hidden": true, - "_type": "glue.core.data.ComponentID", - "label": "World 0" - }, - "World 0_1": { - "hidden": true, - "_type": "glue.core.data.ComponentID", - "label": "World 0" - }, - "World 0_2": { - "hidden": true, - "_type": "glue.core.data.ComponentID", - "label": "World 0" - }, - "World 0": { - "hidden": true, - "_type": "glue.core.data.ComponentID", - "label": "World 0" - }, - "Coordinates_1": { - "_type": "glue.core.coordinates.Coordinates" - }, - "CoordinateComponent_6": { - "world": true, - "_type": "glue.core.data.CoordinateComponent", - "axis": 0 - }, - "CoordinateComponent_5": { - "world": false, - "_type": "glue.core.data.CoordinateComponent", - "axis": 0 - }, - "CoordinateComponent_4": { - "world": true, - "_type": "glue.core.data.CoordinateComponent", - "axis": 0 - }, - "__main__": { - "_type": "glue.qt.glue_application.GlueApplication", - "session": "Session", - "data": "DataCollection", - "viewers": [ - [] - ] - }, - "b_0": { - "hidden": false, - "_type": "glue.core.data.ComponentID", - "label": "b" - }, - "b_1": { - "hidden": false, - "_type": "glue.core.data.ComponentID", - "label": "b" - }, - "b_2": { - "hidden": false, - "_type": "glue.core.data.ComponentID", - "label": "b" - }, - "double_tables": { - "style": { - "_type": "glue.core.visual.VisualAttributes", - "color": "#373737", - "markersize": 3, - "marker": "o", - "alpha": 0.5, - "linewidth": 1, - "linestyle": "solid" - }, - "_protocol": 3, - "subsets": [], - "_type": "glue.core.data.Data", - "label": "double_tables", - "coords": "Coordinates", - "components": [ - [ - "a_2", - "Component_5" - ], - [ - "Pixel Axis 0_2", - "CoordinateComponent_5" - ], - [ - "World 0_2", - "CoordinateComponent_6" - ], - [ - "b_2", - "Component_6" - ] - ], - "_key_joins": [] - }, - "Component": { - "log_item": 0, - "_type": "glue.core.data.Component", - "log": "LoadLog" - }, - "single_table": { - "style": { - "_type": "glue.core.visual.VisualAttributes", - "color": "#373737", - "markersize": 3, - "marker": "o", - "alpha": 0.5, - "linewidth": 1, - "linestyle": "solid" - }, - "_protocol": 3, - "subsets": [], - "_type": "glue.core.data.Data", - "label": "single_table", - "coords": "Coordinates_0", - "components": [ - [ - "a_0", - "Component_1" - ], - [ - "Pixel Axis 0_0", - "CoordinateComponent_1" - ], - [ - "World 0_0", - "CoordinateComponent_2" - ], - [ - "b_0", - "Component_2" - ] - ], - "_key_joins": [] - }, - "CoordinateComponent_3": { - "world": false, - "_type": "glue.core.data.CoordinateComponent", - "axis": 0 - }, - "CoordinateComponentLink": { - "index": 0, - "_type": "glue.core.component_link.CoordinateComponentLink", - "frm": [ - "World 0_2" - ], - "to": [ - "Pixel Axis 0_2" - ], - "pix2world": false, - "coords": "Coordinates" - }, - "CoordinateComponent_2": { - "world": true, - "_type": "glue.core.data.CoordinateComponent", - "axis": 0 - }, - "CoordinateComponent_1": { - "world": false, - "_type": "glue.core.data.CoordinateComponent", - "axis": 0 - }, - "single_table_auto": { - "style": { - "_type": "glue.core.visual.VisualAttributes", - "color": "#373737", - "markersize": 3, - "marker": "o", - "alpha": 0.5019607843137255, - "linewidth": 1, - "linestyle": "solid" - }, - "_protocol": 3, - "subsets": [], - "_type": "glue.core.data.Data", - "label": "single_table_auto", - "coords": "Coordinates_1", - "components": [ - [ - "a", - "Component" - ], - [ - "Pixel Axis 0", - "CoordinateComponent" - ], - [ - "World 0", - "CoordinateComponent_0" - ], - [ - "b", - "Component_0" - ] - ], - "_key_joins": [] - }, - "CoordinateComponent_0": { - "world": true, - "_type": "glue.core.data.CoordinateComponent", - "axis": 0 - }, - "Pixel Axis 0_0": { - "hidden": true, - "_type": "glue.core.data.ComponentID", - "label": "Pixel Axis 0" - }, - "Pixel Axis 0_1": { - "hidden": true, - "_type": "glue.core.data.ComponentID", - "label": "Pixel Axis 0" - }, - "Pixel Axis 0_2": { - "hidden": true, - "_type": "glue.core.data.ComponentID", - "label": "Pixel Axis 0" - }, - "Pixel Axis 0": { - "hidden": true, - "_type": "glue.core.data.ComponentID", - "label": "Pixel Axis 0" - }, - "CoordinateComponentLink_3": { - "index": 0, - "_type": "glue.core.component_link.CoordinateComponentLink", - "frm": [ - "World 0" - ], - "to": [ - "Pixel Axis 0" - ], - "pix2world": false, - "coords": "Coordinates_1" - }, - "CoordinateComponentLink_2": { - "index": 0, - "_type": "glue.core.component_link.CoordinateComponentLink", - "frm": [ - "Pixel Axis 0" - ], - "to": [ - "World 0" - ], - "pix2world": true, - "coords": "Coordinates_1" - }, - "CoordinateComponentLink_1": { - "index": 0, - "_type": "glue.core.component_link.CoordinateComponentLink", - "frm": [ - "Pixel Axis 0_0" - ], - "to": [ - "World 0_0" - ], - "pix2world": true, - "coords": "Coordinates_0" - }, - "CoordinateComponentLink_0": { - "index": 0, - "_type": "glue.core.component_link.CoordinateComponentLink", - "frm": [ - "Pixel Axis 0_1" - ], - "to": [ - "World 0_1" - ], - "pix2world": true, - "coords": "Coordinates_2" - }, - "CoordinateComponentLink_6": { - "index": 0, - "_type": "glue.core.component_link.CoordinateComponentLink", - "frm": [ - "World 0_1" - ], - "to": [ - "Pixel Axis 0_1" - ], - "pix2world": false, - "coords": "Coordinates_2" - }, - "CoordinateComponentLink_5": { - "index": 0, - "_type": "glue.core.component_link.CoordinateComponentLink", - "frm": [ - "World 0_0" - ], - "to": [ - "Pixel Axis 0_0" - ], - "pix2world": false, - "coords": "Coordinates_0" - }, - "Coordinates_0": { - "_type": "glue.core.coordinates.Coordinates" - }, - "Coordinates_2": { - "_type": "glue.core.coordinates.Coordinates" - }, - "Coordinates": { - "_type": "glue.core.coordinates.Coordinates" - }, - "CoordinateComponent": { - "world": false, - "_type": "glue.core.data.CoordinateComponent", - "axis": 0 - }, - "CoordinateComponentLink_4": { - "index": 0, - "_type": "glue.core.component_link.CoordinateComponentLink", - "frm": [ - "Pixel Axis 0_2" - ], - "to": [ - "World 0_2" - ], - "pix2world": true, - "coords": "Coordinates" - }, - "a": { - "hidden": false, - "_type": "glue.core.data.ComponentID", - "label": "a" - }, - "b": { - "hidden": false, - "_type": "glue.core.data.ComponentID", - "label": "b" - }, - "double_tables_auto": { - "style": { - "_type": "glue.core.visual.VisualAttributes", - "color": "#373737", - "markersize": 3, - "marker": "o", - "alpha": 0.5019607843137255, - "linewidth": 1, - "linestyle": "solid" - }, - "_protocol": 3, - "subsets": [], - "_type": "glue.core.data.Data", - "label": "double_tables_auto", - "coords": "Coordinates_2", - "components": [ - [ - "a_1", - "Component_3" - ], - [ - "Pixel Axis 0_1", - "CoordinateComponent_3" - ], - [ - "World 0_1", - "CoordinateComponent_4" - ], - [ - "b_1", - "Component_4" - ] - ], - "_key_joins": [] - }, - "Component_3": { - "log_item": 0, - "_type": "glue.core.data.Component", - "log": "LoadLog_0" - }, - "Component_2": { - "log_item": 3, - "_type": "glue.core.data.Component", - "log": "LoadLog_1" - }, - "Component_1": { - "log_item": 0, - "_type": "glue.core.data.Component", - "log": "LoadLog_1" - }, - "Component_0": { - "log_item": 3, - "_type": "glue.core.data.Component", - "log": "LoadLog" - }, - "Component_6": { - "log_item": 3, - "_type": "glue.core.data.Component", - "log": "LoadLog_2" - }, - "Component_5": { - "log_item": 0, - "_type": "glue.core.data.Component", - "log": "LoadLog_2" - }, - "Component_4": { - "log_item": 3, - "_type": "glue.core.data.Component", - "log": "LoadLog_0" - }, - "DataCollection": { - "_type": "glue.core.data_collection.DataCollection", - "components": [ - "Component", - "CoordinateComponent", - "CoordinateComponent_0", - "Component_0", - "Component_1", - "CoordinateComponent_1", - "CoordinateComponent_2", - "Component_2", - "Component_3", - "CoordinateComponent_3", - "CoordinateComponent_4", - "Component_4", - "Component_5", - "CoordinateComponent_5", - "CoordinateComponent_6", - "Component_6" - ], - "links": [ - "CoordinateComponentLink", - "CoordinateComponentLink_0", - "CoordinateComponentLink_1", - "CoordinateComponentLink_2", - "CoordinateComponentLink_3", - "CoordinateComponentLink_4", - "CoordinateComponentLink_5", - "CoordinateComponentLink_6" - ], - "_protocol": 2, - "groups": [], - "cids": [ - "a", - "Pixel Axis 0", - "World 0", - "b", - "a_0", - "Pixel Axis 0_0", - "World 0_0", - "b_0", - "a_1", - "Pixel Axis 0_1", - "World 0_1", - "b_1", - "a_2", - "Pixel Axis 0_2", - "World 0_2", - "b_2" - ], - "data": [ - "single_table_auto", - "single_table", - "double_tables_auto", - "double_tables" - ] - }, - "Session": { - "_type": "glue.core.session.Session" - }, - "LoadLog_2": { - "path": "{DATA_PATH}double_tables.fits", - "_type": "glue.core.data_factories.LoadLog", - "factory": { - "function": "glue.core.data_factories.astropy_tabular_data", - "_type": "types.FunctionType" - }, - "kwargs": [ - [] - ] - }, - "LoadLog_0": { - "path": "{DATA_PATH}double_tables.fits", - "_type": "glue.core.data_factories.LoadLog", - "factory": { - "function": "glue.core.data_factories.auto_data", - "_type": "types.FunctionType" - }, - "kwargs": [ - [] - ] - }, - "LoadLog_1": { - "path": "{DATA_PATH}single_table.fits", - "_type": "glue.core.data_factories.LoadLog", - "factory": { - "function": "glue.core.data_factories.astropy_tabular_data", - "_type": "types.FunctionType" - }, - "kwargs": [ - [] - ] - } -} \ No newline at end of file diff --git a/glue/tests/data/simple_viewers.glu b/glue/tests/data/simple_viewers.glu deleted file mode 100644 index 88cea9d7b..000000000 --- a/glue/tests/data/simple_viewers.glu +++ /dev/null @@ -1,429 +0,0 @@ -{ - "ScatterWidget": { - "layers": [ - { - "_type": "glue.clients.layer_artist.ScatterLayerArtist", - "layer": "table", - "yatt": "a", - "visible": true, - "zorder": 1, - "xatt": "b" - } - ], - "_type": "glue.qt.widgets.scatter_widget.ScatterWidget", - "pos": [ - 0, - 0 - ], - "session": "Session", - "properties": { - "xflip": false, - "ymax": 3.04, - "xlog": true, - "yflip": true, - "yatt": "a", - "ylog": false, - "xmax": 4.36, - "xmin": 1.24, - "hidden": false, - "ymin": 0.96, - "xatt": "b" - }, - "size": [ - 670, - 512 - ] - }, - "World 0_0": { - "hidden": true, - "_type": "glue.core.data.ComponentID", - "label": "World 0" - }, - "CoordinateComponent_2": { - "world": false, - "_type": "glue.core.data.CoordinateComponent", - "axis": 1 - }, - "image": { - "style": { - "_type": "glue.core.visual.VisualAttributes", - "color": "#373737", - "markersize": 3, - "marker": "o", - "alpha": 0.5, - "linewidth": 1, - "linestyle": "solid" - }, - "_protocol": 3, - "subsets": [], - "_type": "glue.core.data.Data", - "label": "image", - "coords": "Coordinates", - "components": [ - [ - "image_0", - "Component_1" - ], - [ - "Pixel y", - "CoordinateComponent_1" - ], - [ - "Pixel x", - "CoordinateComponent_2" - ], - [ - "World 0_0", - "CoordinateComponent_3" - ], - [ - "World 1", - "CoordinateComponent_4" - ] - ], - "_key_joins": [] - }, - "World 0": { - "hidden": true, - "_type": "glue.core.data.ComponentID", - "label": "World 0" - }, - "Coordinates_0": { - "_type": "glue.core.coordinates.Coordinates" - }, - "Coordinates": { - "_type": "glue.core.coordinates.Coordinates" - }, - "CoordinateComponent_4": { - "world": true, - "_type": "glue.core.data.CoordinateComponent", - "axis": 1 - }, - "__main__": { - "_type": "glue.qt.glue_application.GlueApplication", - "session": "Session", - "data": "DataCollection", - "viewers": [ - [ - "ScatterWidget", - "ImageWidget", - "HistogramWidget" - ] - ] - }, - "table": { - "style": { - "_type": "glue.core.visual.VisualAttributes", - "color": "#373737", - "markersize": 3, - "marker": "o", - "alpha": 0.5, - "linewidth": 1, - "linestyle": "solid" - }, - "_protocol": 3, - "subsets": [], - "_type": "glue.core.data.Data", - "label": "table", - "coords": "Coordinates_0", - "components": [ - [ - "a", - "Component" - ], - [ - "Pixel Axis 0", - "CoordinateComponent" - ], - [ - "World 0", - "CoordinateComponent_0" - ], - [ - "b", - "Component_0" - ] - ], - "_key_joins": [] - }, - "image_0": { - "hidden": false, - "_type": "glue.core.data.ComponentID", - "label": "image" - }, - "CoordinateComponent": { - "world": false, - "_type": "glue.core.data.CoordinateComponent", - "axis": 0 - }, - "Pixel x": { - "hidden": true, - "_type": "glue.core.data.ComponentID", - "label": "Pixel x" - }, - "Component": { - "units": null, - "_type": "glue.core.data.Component", - "data": { - "_type": "numpy.ndarray", - "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGk4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDMsKSwgfSAgICAgICAgICAgIAoBAAAAAAAAAAIAAAAAAAAAAwAAAAAAAAA=" - } - }, - "CoordinateComponent_3": { - "world": true, - "_type": "glue.core.data.CoordinateComponent", - "axis": 0 - }, - "ImageWidget": { - "layers": [ - { - "visible": true, - "layer": "image", - "zorder": 1, - "_type": "glue.clients.layer_artist.ImageLayerArtist", - "norm": "DS9Normalize" - } - ], - "_type": "glue.qt.widgets.image_widget.ImageWidget", - "pos": [ - 672, - 0 - ], - "session": "Session", - "properties": { - "rgb_viz": [ - true, - true, - true - ], - "ratt": null, - "rgb_mode": false, - "gatt": null, - "attribute": "image_0", - "batt": null, - "slice": [ - "y", - "x" - ], - "data": "image" - }, - "size": [ - 562, - 513 - ] - }, - "CoordinateComponentLink": { - "index": 0, - "_type": "glue.core.component_link.CoordinateComponentLink", - "frm": [ - "World 0_0", - "World 1" - ], - "to": [ - "Pixel y" - ], - "pix2world": false, - "coords": "Coordinates" - }, - "World 1": { - "hidden": true, - "_type": "glue.core.data.ComponentID", - "label": "World 1" - }, - "CoordinateComponent_0": { - "world": true, - "_type": "glue.core.data.CoordinateComponent", - "axis": 0 - }, - "HistogramWidget": { - "layers": [ - { - "nbins": 5.0, - "layer": "table", - "lo": 1.3, - "xlog": false, - "_type": "glue.clients.layer_artist.HistogramLayerArtist", - "visible": true, - "hi": 4.2999999999999998, - "zorder": 1 - } - ], - "_type": "glue.qt.widgets.histogram_widget.HistogramWidget", - "pos": [ - 0, - 535 - ], - "session": "Session", - "properties": { - "nbins": 5.0, - "normed": false, - "autoscale": true, - "xlog": false, - "cumulative": false, - "component": "b", - "ylog": false, - "xmax": 4.3, - "xmin": 1.3 - }, - "size": [ - 1235, - 531 - ] - }, - "Pixel y": { - "hidden": true, - "_type": "glue.core.data.ComponentID", - "label": "Pixel y" - }, - "Pixel Axis 0": { - "hidden": true, - "_type": "glue.core.data.ComponentID", - "label": "Pixel Axis 0" - }, - "CoordinateComponentLink_3": { - "index": 1, - "_type": "glue.core.component_link.CoordinateComponentLink", - "frm": [ - "Pixel y", - "Pixel x" - ], - "to": [ - "World 1" - ], - "pix2world": true, - "coords": "Coordinates" - }, - "CoordinateComponentLink_2": { - "index": 0, - "_type": "glue.core.component_link.CoordinateComponentLink", - "frm": [ - "World 0" - ], - "to": [ - "Pixel Axis 0" - ], - "pix2world": false, - "coords": "Coordinates_0" - }, - "CoordinateComponentLink_1": { - "index": 0, - "_type": "glue.core.component_link.CoordinateComponentLink", - "frm": [ - "Pixel y", - "Pixel x" - ], - "to": [ - "World 0_0" - ], - "pix2world": true, - "coords": "Coordinates" - }, - "CoordinateComponentLink_0": { - "index": 0, - "_type": "glue.core.component_link.CoordinateComponentLink", - "frm": [ - "Pixel Axis 0" - ], - "to": [ - "World 0" - ], - "pix2world": true, - "coords": "Coordinates_0" - }, - "CoordinateComponentLink_4": { - "index": 1, - "_type": "glue.core.component_link.CoordinateComponentLink", - "frm": [ - "World 0_0", - "World 1" - ], - "to": [ - "Pixel x" - ], - "pix2world": false, - "coords": "Coordinates" - }, - "a": { - "hidden": false, - "_type": "glue.core.data.ComponentID", - "label": "a" - }, - "b": { - "hidden": false, - "_type": "glue.core.data.ComponentID", - "label": "b" - }, - "CoordinateComponent_1": { - "world": false, - "_type": "glue.core.data.CoordinateComponent", - "axis": 0 - }, - "Component_1": { - "units": null, - "_type": "glue.core.data.Component", - "data": { - "_type": "numpy.ndarray", - "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGY4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDMsIDMpLCB9ICAgICAgICAgIAooXatr9N7uP2BEXHn4oZo/A7ENS98v7D9I5JtTwmHEP3ajNj9M198/ZE5h+Z+Nzz9wEO/ZkuqlP94MlPisMuQ/G94dgwMP6j8=" - } - }, - "Component_0": { - "units": null, - "_type": "glue.core.data.Component", - "data": { - "_type": "numpy.ndarray", - "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGY4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDMsKSwgfSAgICAgICAgICAgIArNzMzMzMz0P83MzMzMzARAMzMzMzMzEUA=" - } - }, - "DS9Normalize": { - "vmax": 0.93116720658359486, - "_type": "glue.clients.ds9norm.DS9Normalize", - "bias": 0.5, - "vmin": 0.032727208219196571, - "clip_hi": 95.0, - "stretch": "arcsinh", - "clip_lo": 5.0, - "contrast": 1.0 - }, - "DataCollection": { - "_type": "glue.core.data_collection.DataCollection", - "components": [ - "Component", - "CoordinateComponent", - "CoordinateComponent_0", - "Component_0", - "Component_1", - "CoordinateComponent_1", - "CoordinateComponent_2", - "CoordinateComponent_3", - "CoordinateComponent_4" - ], - "links": [ - "CoordinateComponentLink", - "CoordinateComponentLink_0", - "CoordinateComponentLink_1", - "CoordinateComponentLink_2", - "CoordinateComponentLink_3", - "CoordinateComponentLink_4" - ], - "_protocol": 2, - "groups": [], - "cids": [ - "a", - "Pixel Axis 0", - "World 0", - "b", - "image_0", - "Pixel y", - "Pixel x", - "World 0_0", - "World 1" - ], - "data": [ - "table", - "image" - ] - }, - "Session": { - "_type": "glue.core.session.Session" - } -} \ No newline at end of file diff --git a/glue/tests/data/single_grid.hdf5 b/glue/tests/data/single_grid.hdf5 deleted file mode 100644 index 4914eccf7..000000000 Binary files a/glue/tests/data/single_grid.hdf5 and /dev/null differ diff --git a/glue/tests/data/single_table.fits b/glue/tests/data/single_table.fits deleted file mode 100644 index f4f08f352..000000000 Binary files a/glue/tests/data/single_table.fits and /dev/null differ diff --git a/glue/tests/helpers.py b/glue/tests/helpers.py index db26cb393..5928cc646 100644 --- a/glue/tests/helpers.py +++ b/glue/tests/helpers.py @@ -15,6 +15,9 @@ def make_marker(mark_creator, module, label=None, version=None, mark_if='lt'): if label == 'PyQt5': # PyQt5 does not use __version__ from PyQt5 import QtCore version_installed = QtCore.PYQT_VERSION_STR + elif label == 'PyQt6': # PyQt6 does not use __version__ + from PyQt6 import QtCore + version_installed = QtCore.PYQT_VERSION_STR else: mod = __import__(module) version_installed = mod.__version__ @@ -73,11 +76,13 @@ def mark_creator(installed, lbl, vrs): H5PY_INSTALLED, requires_h5py = make_skipper('h5py') PYQT5_INSTALLED, requires_pyqt5 = make_skipper('PyQt5') +PYQT6_INSTALLED, requires_pyqt6 = make_skipper('PyQt6') PYSIDE2_INSTALLED, requires_pyside2 = make_skipper('PySide2') +PYSIDE6_INSTALLED, requires_pyside6 = make_skipper('PySide6') HYPOTHESIS_INSTALLED, requires_hypothesis = make_skipper('hypothesis') -QT_INSTALLED = PYQT5_INSTALLED or PYSIDE2_INSTALLED +QT_INSTALLED = PYQT5_INSTALLED or PYQT6_INSTALLED or PYSIDE2_INSTALLED or PYSIDE6_INSTALLED SPECTRAL_CUBE_INSTALLED, requires_spectral_cube = make_skipper('spectral_cube', label='spectral-cube') diff --git a/glue/tests/test_config.py b/glue/tests/test_config.py index 3d97772a4..a77133b67 100644 --- a/glue/tests/test_config.py +++ b/glue/tests/test_config.py @@ -1,25 +1,4 @@ -from ..config import qt_client, link_function, data_factory -from glue.tests.helpers import requires_qt - - -@requires_qt -def test_default_clients(): - - from glue.viewers.image.qt import ImageViewer - from glue.viewers.scatter.qt import ScatterViewer - from glue.viewers.histogram.qt import HistogramViewer - - assert ImageViewer in qt_client - assert ScatterViewer in qt_client - assert HistogramViewer in qt_client - - -def test_add_client(): - @qt_client - class TestClient(object): - pass - - assert TestClient in qt_client +from ..config import link_function, data_factory def test_add_link_default(): diff --git a/glue/tests/test_deps.py b/glue/tests/test_deps.py deleted file mode 100644 index 10814bc02..000000000 --- a/glue/tests/test_deps.py +++ /dev/null @@ -1,67 +0,0 @@ -import sys -from subprocess import check_call - -from glue.tests.helpers import requires_qt - -from .._deps import Dependency, categories - - -class TestDependency(object): - - def test_installed(self): - d = Dependency('math', 'the math module') - assert d.installed - - def test_uninstalled(self): - d = Dependency('asdfasdf', 'Non-existent module') - assert not d.installed - - def test_installed_str(self): - d = Dependency('math', 'info') - assert str(d) == " math:\tINSTALLED (unknown version)" - - def test_noinstalled_str(self): - d = Dependency('asdfasdf', 'info') - assert str(d) == " asdfasdf:\tMISSING (info)" - - def test_failed_str(self): - d = Dependency('asdfasdf', 'info') - d.failed = True - assert str(d) == " asdfasdf:\tFAILED (info)" - - -@requires_qt -def test_optional_dependency_not_imported(): - """ - Ensure that a GlueApplication instance can be created without - importing any non-required dependency - """ - optional_deps = categories[5:] - deps = [dep.module for cateogry, deps in optional_deps for dep in deps] - - code = """ -class ImportDenier(object): - __forbidden = set(%s) - - def find_module(self, mod_name, pth=None): - if pth: - return - if mod_name in self.__forbidden: - return self - - def load_module(self, mod_name): - raise ImportError("Importing %%s" %% mod_name) - - def exec_module(self, mod_name): - raise ImportError("Importing %%s" %% mod_name) - -import sys -sys.meta_path.append(ImportDenier()) - -from glue.app.qt import GlueApplication -from glue.core import data_factories -ga = GlueApplication() -""" % deps - - cmd = [sys.executable, '-c', code] - check_call(cmd) diff --git a/glue/tests/test_main.py b/glue/tests/test_main.py deleted file mode 100644 index 85d23b25d..000000000 --- a/glue/tests/test_main.py +++ /dev/null @@ -1,111 +0,0 @@ -import os -import pytest -from unittest.mock import patch - -from glue.tests.helpers import requires_qt - -from ..core import Data -from ..main import load_data_files, main, start_glue - - -def test_load_data_files(): - with patch('glue.core.data_factories.load_data') as ld: - ld.return_value = Data() - dc = load_data_files(['test.py']) - assert len(dc) == 1 - - -def check_main(cmd, glue, config, data): - """Pass command to main program, check for expected parsing""" - with patch('glue.main.start_glue') as sg: - main(cmd.split()) - args, kwargs = sg.call_args - assert kwargs.get('datafiles', None) == data - assert kwargs.get('gluefile', None) == glue - assert kwargs.get('config', None) == config - - -def check_exec(cmd, pyfile): - """Assert that main correctly dispatches to execute_script""" - with patch('glue.main.execute_script') as es: - main(cmd.split()) - args, kwargs = es.call_args - assert args[0] == pyfile - - -def test_main_single_data(): - check_main('glueqt test.fits', None, None, ['test.fits']) - - -def test_main_multi_data(): - check_main('glueqt test.fits t2.csv', None, None, ['test.fits', 't2.csv']) - - -def test_main_config(): - check_main('glueqt -c config.py', None, 'config.py', None) - - -def test_main_glu_arg(): - check_main('glueqt -g test.glu', 'test.glu', None, None) - - -def test_main_auto_glu(): - check_main('glueqt test.glu', 'test.glu', None, None) - - -def test_main_many_args(): - check_main('glueqt -c config.py data.fits d2.csv', None, - 'config.py', ['data.fits', 'd2.csv']) - - -def test_exec(): - check_exec('glueqt -x test.py', 'test.py') - - -def test_auto_exec(): - check_exec('glueqt test.py', 'test.py') - - -@requires_qt -def test_exec_real(tmpdir): - # Actually test the script execution functionlity - filename = tmpdir.join('test.py').strpath - with open(filename, 'w') as f: - f.write('a = 1') - with patch('qtpy.QtWidgets.QMessageBox') as qmb: - with patch('sys.exit') as exit: - main('glue -x {0}'.format(os.path.abspath(filename)).split()) - assert exit.called_once_with(0) - - -@pytest.mark.parametrize(('cmd'), ['glueqt -g test.glu test.fits', - 'glueqt -g test.py test.fits', - 'glueqt -x test.py -g test.glu', - 'glueqt -x test.py -c test.py', - 'glueqt -x', - 'glueqt -g', - 'glueqt -c']) -def test_invalid(cmd): - with pytest.raises(SystemExit): - main(cmd.split()) - - -@requires_qt -@pytest.mark.parametrize(('glue', 'config', 'data'), - [('test.glu', None, None), - (None, 'test.py', None), - (None, None, ['test.fits']), - (None, None, ['a.fits', 'b.fits']), - (None, 'test.py', ['a.fits'])]) -def test_start(glue, config, data): - with patch('glue.config.load_configuration') as lc: - with patch('glue.main.load_data_files') as ldf: - with patch('glue.app.qt.GlueApplication') as ga: - - ldf.return_value = Data() - - start_glue(glue, config, data) - if config: - lc.assert_called_once_with(search_path=[config]) - if data: - ldf.assert_called_once_with(data) diff --git a/glue/tests/test_qglue.py b/glue/tests/test_qglue.py deleted file mode 100644 index cb0e26810..000000000 --- a/glue/tests/test_qglue.py +++ /dev/null @@ -1,237 +0,0 @@ -import pytest -import numpy as np -import pandas as pd -from unittest.mock import patch - -from .. import qglue -from ..core import BaseCartesianData, Data -from ..core.exceptions import IncompatibleAttribute -from ..core.registry import Registry -from .helpers import requires_astropy, requires_qt - - -@requires_qt -@requires_astropy -class TestQGlue(object): - - def setup_method(self, method): - - from astropy.table import Table - from astropy.io.fits import HDUList, ImageHDU - - Registry().clear() - - x = [1, 2, 3] - y = [2, 3, 4] - - u = [10, 20, 30, 40] - v = [20, 40, 60, 80] - - self.xy = {'x': x, 'y': y} - self.dict_data = {'u': u, 'v': v} - self.recarray_data = np.rec.array([(0, 1), (2, 3)], - dtype=[(str('a'), int), (str('b'), int)]) - self.astropy_table = Table({'x': x, 'y': y}) - self.bad_data = {'x': x, 'u': u} - self.hdulist = HDUList([ImageHDU(x, name='PRIMARY')]) - - self.x = np.array(x) - self.y = np.array(y) - self.u = np.array(u) - self.v = np.array(v) - - def check_setup(self, dc, expected): - # assert that the assembled data collection returned - # form qglue matches expected structure - - # test for expected data, components - for data in dc: - components = set(c.label for c in data.components) - e = expected.pop(data.label) - for component in e: - assert component in components - assert len(expected) == 0 - - def test_qglue_starts_application(self): - pandas_data = pd.DataFrame(self.xy) - with patch('glue.app.qt.GlueApplication') as ga: - qglue(data1=pandas_data) - ga.assert_called_once() - - def test_single_pandas(self): - with patch('glue.app.qt.GlueApplication') as ga: - qglue(data1=self.xy) - dc = ga.call_args[0][0] - self.check_setup(dc, {'data1': ['x', 'y']}) - - def test_single_pandas_nonstring_column(self): - with patch('glue.app.qt.GlueApplication') as ga: - qglue(data1=pd.DataFrame({1: [1, 2, 3]})) - dc = ga.call_args[0][0] - self.check_setup(dc, {'data1': ['1']}) - - def test_single_numpy(self): - with patch('glue.app.qt.GlueApplication') as ga: - qglue(data1=np.array([1, 2, 3])) - dc = ga.call_args[0][0] - self.check_setup(dc, {'data1': ['data1']}) - - def test_single_list(self): - with patch('glue.app.qt.GlueApplication') as ga: - qglue(data1=[1, 2, 3]) - dc = ga.call_args[0][0] - self.check_setup(dc, {'data1': ['data1']}) - - def test_single_dict(self): - with patch('glue.app.qt.GlueApplication') as ga: - qglue(data2=self.dict_data) - dc = ga.call_args[0][0] - self.check_setup(dc, {'data2': ['u', 'v']}) - - def test_recarray(self): - with patch('glue.app.qt.GlueApplication') as ga: - qglue(data3=self.recarray_data) - dc = ga.call_args[0][0] - self.check_setup(dc, {'data3': ['a', 'b']}) - - def test_astropy_table(self): - with patch('glue.app.qt.GlueApplication') as ga: - qglue(data4=self.astropy_table) - dc = ga.call_args[0][0] - self.check_setup(dc, {'data4': ['x', 'y']}) - - def test_multi_data(self): - with patch('glue.app.qt.GlueApplication') as ga: - qglue(data1=self.dict_data, data2=self.xy) - dc = ga.call_args[0][0] - self.check_setup(dc, {'data1': ['u', 'v'], - 'data2': ['x', 'y']}) - - def test_hdulist(self): - with patch('glue.app.qt.GlueApplication') as ga: - qglue(data1=self.hdulist).data_collection - dc = ga.call_args[0][0] - self.check_setup(dc, {'data1': ['PRIMARY']}) - - def test_glue_data(self): - d = Data(x=[1, 2, 3]) - with patch('glue.app.qt.GlueApplication') as ga: - qglue(x=d) - dc = ga.call_args[0][0] - assert d.label == 'x' - - def test_base_data(self): - - class CustomData(BaseCartesianData): - - def get_kind(self): - pass - - def compute_histogram(self): - pass - - def compute_statistic(self): - pass - - def get_mask(self): - pass - - @property - def shape(self): - return () - - @property - def main_components(self): - return [] - - d = CustomData() - with patch('glue.app.qt.GlueApplication') as ga: - qglue(x=d) - dc = ga.call_args[0][0] - - assert dc[0] is d - - def test_simple_link(self): - using = lambda x: x * 2 - links = [['data1.x', 'data2.u', using]] - with patch('glue.app.qt.GlueApplication') as ga: - qglue(data1=self.xy, data2=self.dict_data, links=links) - dc = ga.call_args[0][0] - - links = [[['x'], 'u', using]] - self.check_setup(dc, {'data1': ['x', 'y'], - 'data2': ['u', 'v']}) - - d = dc[0] if dc[0].label == 'data1' else dc[1] - np.testing.assert_array_equal(d['x'], self.x) - np.testing.assert_array_equal(d['u'], self.x * 2) - d = dc[0] if dc[0].label == 'data2' else dc[1] - with pytest.raises(IncompatibleAttribute) as exc: - d['x'] - - def test_multi_link(self): - - def forwards(x, y): - return x * 2, y * 3 - - def backwards(x, y): - return x / 2, y / 3 - - links = [[['Data1.x', 'Data1.y'], - ['Data2.u', 'Data2.v'], forwards, backwards]] - with patch('glue.app.qt.GlueApplication') as ga: - qglue(Data1=self.xy, Data2=self.dict_data, links=links) - dc = ga.call_args[0][0] - - self.check_setup(dc, {'Data1': ['x', 'y'], - 'Data2': ['u', 'v']}) - - for d in dc: - if d.label == 'Data1': - np.testing.assert_array_equal(d['x'], self.x) - np.testing.assert_array_equal(d['y'], self.y) - np.testing.assert_array_equal(d['u'], self.x * 2) - np.testing.assert_array_equal(d['v'], self.y * 3) - else: - np.testing.assert_array_equal(d['x'], self.u / 2) - np.testing.assert_array_equal(d['y'], self.v / 3) - np.testing.assert_array_equal(d['u'], self.u) - np.testing.assert_array_equal(d['v'], self.v) - - def test_implicit_identity_link(self): - links = [('Data1.x', 'Data2.v'), - ('Data1.y', 'Data2.u')] - with patch('glue.app.qt.GlueApplication') as ga: - qglue(Data1=self.xy, Data2=self.dict_data, links=links) - dc = ga.call_args[0][0] - # currently, identity links rename the second link to first, - # so u/v disappear - for d in dc: - if d.label == 'Data1': - np.testing.assert_array_equal(d['x'], self.x) - np.testing.assert_array_equal(d['y'], self.y) - else: - np.testing.assert_array_equal(d['y'], self.u) - np.testing.assert_array_equal(d['x'], self.v) - - def test_bad_link(self): - forwards = lambda *args: args - links = [(['Data1.a'], ['Data2.b'], forwards)] - with pytest.raises(ValueError) as exc: - qglue(Data1=self.xy, Data2=self.dict_data, links=links) - assert exc.value.args[0] == "Invalid link (no component named Data1.a)" - - def test_bad_data_shape(self): - with pytest.raises(ValueError) as exc: - qglue(d=self.bad_data) - assert exc.value.args[0].startswith("Invalid format for data 'd'") - - def test_bad_data_format(self): - with pytest.raises(TypeError) as exc: - qglue(d=5) - assert exc.value.args[0].startswith("Invalid data description") - - def test_malformed_data_dict(self): - with pytest.raises(ValueError) as exc: - qglue(d={'x': 'bad'}) - assert exc.value.args[0].startswith("Invalid format for data 'd'") diff --git a/glue/tests/test_session_back_compat.py b/glue/tests/test_session_back_compat.py deleted file mode 100644 index 741577d0b..000000000 --- a/glue/tests/test_session_back_compat.py +++ /dev/null @@ -1,333 +0,0 @@ -# Make sure that session files can be read in a backward-compatible manner - -import os -import pytest -import numpy as np - -from glue.tests.helpers import requires_astropy, requires_h5py, requires_qt, PYSIDE2_INSTALLED # noqa -from glue.core.component import CoordinateComponent, Component -from glue.core.state import GlueUnSerializer -from glue.core.component_id import PixelComponentID - -DATA = os.path.join(os.path.dirname(__file__), 'data') - - -@requires_qt -@requires_astropy -def test_load_simple_tables_04(): - - # This loads a session file made with Glue v0.4. In this session, we have - # loaded four tables. The first two are from the same file, but one loaded - # via the auto loader and the other via the Astropy FITS table loader. The - # second two were loaded similarly to the first two, but the file contains - # two HDUs this time. However, in Glue v0.4, only the first HDU was read so - # we shouldn't have access to columns c and d in ``double_tables.fits``. - - with open(os.path.join(DATA, 'simple_tables.glu'), 'r') as f: - template = f.read() - - content = template.replace('{DATA_PATH}', (DATA + os.sep).replace('\\', '\\\\')) - state = GlueUnSerializer.loads(content) - - ga = state.object('__main__') - - dc = ga.session.data_collection - - # All tables should actually be the same because the FITS reader back at - # 0.4 only read in the first HDU so the new reader is back-compatible - # since it preserves HDU order. - - assert len(dc) == 4 - - assert dc[0].label == 'single_table_auto' - assert dc[1].label == 'single_table' - assert dc[2].label == 'double_tables_auto' - assert dc[3].label == 'double_tables' - - np.testing.assert_equal(dc[0]['a'], [1, 2, 3]) - np.testing.assert_equal(dc[0]['b'], [4, 5, 6]) - np.testing.assert_equal(dc[0]['a'], dc[1]['a']) - np.testing.assert_equal(dc[0]['b'], dc[1]['b']) - np.testing.assert_equal(dc[0]['a'], dc[2]['a']) - np.testing.assert_equal(dc[0]['b'], dc[2]['b']) - np.testing.assert_equal(dc[0]['a'], dc[3]['a']) - np.testing.assert_equal(dc[0]['b'], dc[3]['b']) - - ga.close() - - -@requires_qt -@requires_h5py -def test_load_hdf5_grids_04(): - - # This loads a session file made with Glue v0.4. In this session, we have - # loaded two gridded datasets from an HDF5 datafile: the first one loaded - # via the auto loader and the other via the FITS/HDF5 loader. - - with open(os.path.join(DATA, 'simple_hdf5_grid.glu'), 'r') as f: - template = f.read() - - content = template.replace('{DATA_PATH}', (DATA + os.sep).replace('\\', '\\\\')) - state = GlueUnSerializer.loads(content) - - ga = state.object('__main__') - - dc = ga.session.data_collection - - assert len(dc) == 2 - - assert dc[0].label == 'single_grid_auto' - assert dc[1].label == 'single_grid' - - np.testing.assert_equal(dc[0]['/array1'], 1) - np.testing.assert_equal(dc[0]['/array1'].shape, (2, 3, 4)) - - ga.close() - - -@requires_qt -@requires_astropy -def test_load_link_helpers_04(): - - # This loads a session file made with Glue v0.4. In this session, we have - # two tables, and we use all the celestial link functions that were present - # in Glue v0.4. We now check that the paths are patched when loading the - # session (since the functions have been moved to a deprecated location) - - with open(os.path.join(DATA, 'session_links.glu'), 'r') as f: - content = f.read() - - state = GlueUnSerializer.loads(content) - - ga = state.object('__main__') - ga.close() - - -@requires_qt -@requires_astropy -@pytest.mark.skipif('PYSIDE2_INSTALLED') -def test_load_viewers_04(): - - # FIXME - for some reason this test with PySide2 causes a leftover reference - # to GlueApplication and appears to be due to x_log being True in the - # scatter plot. I suspect maybe there is some kind of circular reference - - # This loads a session file made with Glue v0.4. In this session, we have - # three viewers: one scatter viewer, one image viewer, and one histogram - # viewer. - - with open(os.path.join(DATA, 'simple_viewers.glu'), 'r') as f: - content = f.read() - - state = GlueUnSerializer.loads(content) - - ga = state.object('__main__') - - assert len(ga.viewers[0]) == 3 - labels = sorted([x.LABEL for x in ga.viewers[0]]) - - assert labels == ['1D Histogram', '2D Image', '2D Scatter'] - - viewers = {} - for x in ga.viewers[0]: - viewers[x.LABEL] = x - - h = viewers['1D Histogram'] - assert h.viewer_size == (1235, 531) - assert h.position == (0, 535) - assert h.state.x_att.label == 'b' - - i = viewers['2D Image'] - assert i.viewer_size == (562, 513) - assert i.position == (672, 0) - assert i.state.layers[0].attribute.label == "image" - - s = viewers['2D Scatter'] - assert s.viewer_size == (670, 512) - assert s.position == (0, 0) - assert s.state.x_att.label == 'b' - assert s.state.y_att.label == 'a' - assert s.state.x_log - assert not s.state.y_log - - ga.close() - - -@requires_qt -def test_load_pixel_components_07(): - - # This loads a session file made with Glue v0.7. In 0.7 and before, - # PixelComponentID did not exist, so we need to make sure that when loading - # in such files, we transform the appropriate ComponentIDs to - # PixelComponentIDs. - - with open(os.path.join(DATA, 'glue_v0.7_pixel_roi_selection.glu'), 'r') as f: - content = f.read() - - state = GlueUnSerializer.loads(content) - - ga = state.object('__main__') - - assert isinstance(ga.data_collection[0].pixel_component_ids[0], PixelComponentID) - assert isinstance(ga.data_collection[0].pixel_component_ids[1], PixelComponentID) - - ga.close() - - -@requires_qt -def test_table_widget_010(): - - from glue.viewers.table.qt.tests.test_data_viewer import check_values_and_color - - # This loads a session file made with Glue v0.10 that includes a table - # viewer. This is to make sure that loading table viewers from old files - # will always be backward-compatible. - - with open(os.path.join(DATA, 'glue_v0.10_table.glu'), 'r') as f: - state = GlueUnSerializer.load(f) - - ga = state.object('__main__') - - viewer = ga.viewers[0][0] - - data = {'x': [1, 2, 3], - 'y': [4, 5, 6]} - - colors = ['#e31a1c', '#6d7326', None] - - check_values_and_color(viewer.model, data, colors) - - ga.close() - - -@requires_qt -@pytest.mark.parametrize('protocol', (0, 1)) -def test_load_log(protocol): - - # Prior to Glue v0.13, components were added to the data as: first - # non-coordinate component, then coordinate components, then remaining non- - # coordinate components. In Glue v0.13, this changed to be coordinate - # components then non-coordinate components. The LoadLog functionality - # relies on an absolute component index, so we need to be careful - if the - # session file was created prior to Glue v0.13, we need to load the - # components in the log using the old order. The load_log_1.glu file was - # made with Glue v0.12.2, while the load_log_2.glu file was made with - # Glue v0.13. - - with open(os.path.join(DATA, 'load_log_{0}.glu'.format(protocol)), 'r') as f: - template = f.read() - - content = template.replace('{DATA_PATH}', (DATA + os.sep).replace('\\', '\\\\')) - state = GlueUnSerializer.loads(content) - - ga = state.object('__main__') - - dc = ga.session.data_collection - - assert len(dc) == 1 - - data = dc[0] - - assert data.label == 'simple' - - np.testing.assert_equal(data['Pixel Axis 0 [x]'], [0, 1, 2]) - np.testing.assert_equal(data['World 0'], [0, 1, 2]) - np.testing.assert_equal(data['a'], [1, 3, 5]) - np.testing.assert_equal(data['b'], [2, 2, 3]) - - if protocol == 0: - assert data.components == [data.id['a'], data.id['Pixel Axis 0 [x]'], data.id['World 0'], data.id['b']] - else: - assert data.components == [data.id['Pixel Axis 0 [x]'], data.id['World 0'], data.id['a'], data.id['b']] - - assert type(data.get_component('Pixel Axis 0 [x]')) == CoordinateComponent - assert type(data.get_component('World 0')) == CoordinateComponent - assert type(data.get_component('a')) == Component - assert type(data.get_component('b')) == Component - - ga.close() - - -@requires_qt -@requires_astropy -def test_load_coordinate_link_helpers_013(): - - # This loads a session file made with Glue v0.13. In this session, we have - # two tables, and we use all the celestial link functions that were present - # in Glue v0.13. We now check that the paths are patched when loading the - # session (since the functions have been moved to a deprecated location) - - with open(os.path.join(DATA, 'session_coordinate_links_013.glu'), 'r') as f: - content = f.read() - - state = GlueUnSerializer.loads(content) - - ga = state.object('__main__') - - data1, data2 = ga.session.data_collection - - print(data1) - print(data2) - - # Check that the links works - data1[data2.id['x']] - data1[data2.id['y']] - data2[data1.id['x']] - data2[data1.id['y']] - - ga.close() - - -@requires_qt -@requires_astropy -def test_load_resave_coords(tmp_path): - - # This is a regression test for a bug that caused issues when creating a - # session file with a dataset without any world coordinates (e.g. a table) - # in glue 0.15, then loading and re-saving the session in glue 0.16. The - # issue is that the glue 0.16 file will use LoadLog with protocol 2, but - # since the file was originally created in glue 0.15, it will include a - # fake world coordinate column. LoadLog with protocol 2 was incorrectly - # assumed to never have these world coordinates. - - # Load in a file create with glue 0.15.* which includes a world component - # even though Data.coords is the default identity transform - with open(os.path.join(DATA, 'simple_table_015.glu'), 'r') as f: - template = f.read() - - content = template.replace('{DATA_PATH}', (DATA + os.sep).replace('\\', '\\\\')) - state = GlueUnSerializer.loads(content) - - ga = state.object('__main__') - dc = ga.session.data_collection - assert len(dc) == 1 - ga.save_session(tmp_path / 'test.glu') - ga.close() - - with open(tmp_path / 'test.glu', 'r') as f: - content = f.read() - - state = GlueUnSerializer.loads(content) - ga = state.object('__main__') - dc = ga.session.data_collection - assert len(dc) == 1 - ga.close() - - -@requires_qt -@requires_astropy -def test_load_resave_coords_intermediate(tmp_path): - - # Load in a file create with glue 0.15.* which includes a world component - # even though Data.coords is the default identity transform - with open(os.path.join(DATA, 'simple_table_resaved.glu'), 'r') as f: - template = f.read() - - content = template.replace('{DATA_PATH}', (DATA + os.sep).replace('\\', '\\\\')) - state = GlueUnSerializer.loads(content) - - ga = state.object('__main__') - dc = ga.session.data_collection - assert len(dc) == 1 - ga.save_session(tmp_path / 'test.glu') - ga.close() diff --git a/glue/utils/matplotlib.py b/glue/utils/matplotlib.py index 0e6464dfb..6a9319701 100644 --- a/glue/utils/matplotlib.py +++ b/glue/utils/matplotlib.py @@ -13,29 +13,18 @@ # We avoid importing matplotlib up here otherwise Matplotlib and therefore Qt # get imported as soon as glue.utils is imported. -from glue.external.axescache import AxesCache from glue.utils.misc import DeferredMethod MATPLOTLIB_GE_30 = Version(__version__) >= Version('3') MATPLOTLIB_GE_36 = Version(__version__) >= Version('3.6') -__all__ = ['renderless_figure', 'all_artists', 'new_artists', 'remove_artists', +__all__ = ['all_artists', 'new_artists', 'remove_artists', 'get_extent', 'view_cascade', 'fast_limits', 'defer_draw', - 'color2rgb', 'point_contour', 'cache_axes', + 'color2rgb', 'point_contour', 'datetime64_to_mpl', 'mpl_to_datetime64', 'color2hex'] -def renderless_figure(): - # Matplotlib figure that skips the render step, for test speed - from unittest.mock import MagicMock - import matplotlib.pyplot as plt - fig = plt.figure() - fig.canvas.draw = MagicMock() - plt.close('all') - return fig - - def all_artists(fig): """ Build a set of all Matplotlib artists in a Figure @@ -334,31 +323,6 @@ def freeze_margins(axes, margins=[1, 1, 1, 1]): axes.figure.canvas.mpl_connect('resize_event', axes.resizer.on_resize) -def cache_axes(axes, toolbar): - """ - Set up caching for an axes object. - - After this, cached renders will be used to quickly re-render an axes during - window resizing or interactive pan/zooming. - - This function returns an AxesCache instance. - - Parameters - ---------- - axes : :class:`matplotlib.axes.Axes` - The axes to cache - toolbar : :class:`glue.viewers.common.qt.toolbar.BasicToolbar` - The toolbar managing the axes' canvas - """ - canvas = axes.figure.canvas - cache = AxesCache(axes) - canvas.resize_begin.connect(cache.enable) - canvas.resize_end.connect(cache.disable) - toolbar.pan_begin.connect(cache.enable) - toolbar.pan_end.connect(cache.disable) - return cache - - class ColormapPatchHandler(HandlerBase): def __init__(self, cmap, nb_subpatch=10, xpad=0.0, ypad=0.0): """ diff --git a/glue/utils/qt/__init__.py b/glue/utils/qt/__init__.py index 613727a41..95cc710cd 100644 --- a/glue/utils/qt/__init__.py +++ b/glue/utils/qt/__init__.py @@ -1,11 +1,4 @@ -from .autocomplete_widget import * # noqa -from .dialogs import * # noqa -from .colors import * # noqa -from .decorators import * # noqa -from .helpers import * # noqa -from .mixins import * # noqa -from .mime import * # noqa -from .python_list_model import * # noqa -from .threading import * # noqa -from .app import * # noqa -from .delegates import * # noqa +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.utils.qt is deprecated, use glue_qt.utils instead', GlueDeprecationWarning) +from glue_qt.utils import * # noqa diff --git a/glue/utils/qt/app.py b/glue/utils/qt/app.py index c01068da9..1ade6b52a 100644 --- a/glue/utils/qt/app.py +++ b/glue/utils/qt/app.py @@ -1,97 +1,4 @@ -import time -import platform -from qtpy import QtCore, QtGui, QtWidgets - -from glue.config import settings -from glue._settings_helpers import save_settings - -__all__ = ['process_events', 'get_qapp', 'fix_tab_widget_fontsize', 'update_global_font_size'] - -qapp = None - - -def __get_font_size_offset(): - if platform.system() == 'Darwin': - # On Mac, the fonts are generally too large compared to other - # applications, so we reduce the default here. In future we should - # make this a setting in the system preferences. - size_offset = 2 - else: - # On other platforms, we reduce the font size by 1 point to save - # space too. Again, this should probably be a global setting. - size_offset = 1 - return size_offset - - -def process_events(wait=None): - app = get_qapp() - if wait is None: - app.processEvents() - else: - start = time.time() - while time.time() - start < wait: - app.processEvents() - - -def get_qapp(icon_path=None): - - global qapp - - qapp = QtWidgets.QApplication.instance() - - if qapp is None: - - # NOTE: plugins that need WebEngine may complain that QtWebEngineWidgets - # needs to be imported before QApplication is constructed, but this can - # cause segmentation faults to crop up under certain conditions, so we - # don't do it here and instead ask that the plugins do it in their - # main __init__.py (which should get executed before glue is launched). - - qapp = QtWidgets.QApplication(['']) - qapp.setQuitOnLastWindowClosed(True) - - if icon_path is not None: - qapp.setWindowIcon(QtGui.QIcon(icon_path)) - - size_offset = __get_font_size_offset() - font = qapp.font() - - if settings.FONT_SIZE is None or settings.FONT_SIZE == -1: - default_font = QtGui.QFont() - settings.FONT_SIZE = default_font.pointSize() - save_settings() - - point_size = settings.FONT_SIZE - font.setPointSize(int(point_size - size_offset)) - qapp.setFont(font) - - # Make sure we use high resolution icons for HDPI displays. - try: - qapp.setAttribute(QtCore.Qt.AA_UseHighDpiPixmaps) - except AttributeError: # PyQt6/PySide6 don't have this setting as it is default - pass - - return qapp - - -def fix_tab_widget_fontsize(tab_widget): - """ - Because of a bug in Qt, tab titles on MacOS X don't have the right font size - """ - if platform.system() == 'Darwin': - app = get_qapp() - app_font = app.font() - tab_widget.setStyleSheet('font-size: {0}px'.format(app_font.pointSize())) - - -def update_global_font_size(): - """Updates the global font size through the current UI backend - """ - if qapp is None: - get_qapp() - - font = qapp.font() - point_size = settings.FONT_SIZE - size_offset = __get_font_size_offset() - font.setPointSize(int(point_size - size_offset)) - qapp.setFont(font) +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.utils.qt.app is deprecated, use glue_qt.utils.app instead', GlueDeprecationWarning) +from glue_qt.utils.app import * # noqa diff --git a/glue/utils/qt/autocomplete_widget.py b/glue/utils/qt/autocomplete_widget.py index e67a5afe0..07c699e3a 100644 --- a/glue/utils/qt/autocomplete_widget.py +++ b/glue/utils/qt/autocomplete_widget.py @@ -1,116 +1,4 @@ -# Code adapted from: -# -# http://rowinggolfer.blogspot.de/2010/08/qtextedit-with-autocompletion-using.html -# -# and based on: -# -# http://qt-project.org/doc/qt-4.8/tools-customcompleter.html - -from qtpy import QtGui, QtWidgets -from qtpy.QtCore import Qt - - -__all__ = ["CompletionTextEdit"] - - -class CompletionTextEdit(QtWidgets.QTextEdit): - - def __init__(self, parent=None): - - super(CompletionTextEdit, self).__init__(parent) - - self.setMinimumWidth(400) - self.completer = None - self.word_list = None - - self.moveCursor(QtGui.QTextCursor.End) - - def set_word_list(self, word_list): - self.word_list = word_list - self.set_completer(QtWidgets.QCompleter(word_list)) - - def set_completer(self, completer): - - if self.completer: - self.disconnect(self.completer, 0, self, 0) - if not completer: - return - - self.completer = completer - - self.completer.setWidget(self) - self.completer.setCompletionMode(QtWidgets.QCompleter.PopupCompletion) - self.completer.setCaseSensitivity(Qt.CaseInsensitive) - self.completer.activated.connect(self.insert_completion) - - def insert_completion(self, completion): - - tc = self.textCursor() - - existing = self.text_under_cursor() - - completion = completion[len(existing):] + " " - - self.setTextCursor(tc) - - self.insertPlainText(completion) - - self.completer.setCompletionPrefix('') - - def text_under_cursor(self): - tc = self.textCursor() - text = self.toPlainText() - pos1 = tc.position() - if pos1 == 0 or text[pos1 - 1] == ' ': - return '' - else: - sub = text[:pos1] - if ' ' in sub: - pos2 = sub.rindex(' ') - return sub[pos2 + 1:] - else: - return sub - - # The following methods override methods in QTextEdit and should not be - # renamed. - - def focusInEvent(self, event): - if self.completer: - self.completer.setWidget(self) - QtWidgets.QTextEdit.focusInEvent(self, event) - - def keyPressEvent(self, event): - - if self.completer and self.completer.popup().isVisible(): - if event.key() in ( - Qt.Key_Enter, - Qt.Key_Return, - Qt.Key_Escape, - Qt.Key_Tab, - Qt.Key_Backtab): - event.ignore() - return - - # Check if TAB has been pressed - is_shortcut = event.key() == Qt.Key_Tab - - if not self.completer or not is_shortcut: - QtWidgets.QTextEdit.keyPressEvent(self, event) - return - - eow = "~!@#$%^&*()_+{}|:\"<>?,./;'[]\\-=" - - completion_prefix = self.text_under_cursor() - - if not is_shortcut and (len(event.text()) == 0 or event.text()[-1:] in eow): - self.completer.popup().hide() - return - - self.completer.setCompletionPrefix(completion_prefix) - popup = self.completer.popup() - popup.setCurrentIndex(self.completer.completionModel().index(0, 0)) - - cr = self.cursorRect() - cr.setWidth(self.completer.popup().sizeHintForColumn(0) + - self.completer.popup().verticalScrollBar().sizeHint().width()) - self.completer.complete(cr) +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.utils.qt.autocomplete_widget is deprecated, use glue_qt.utils.autocomplete_widget instead', GlueDeprecationWarning) +from glue_qt.utils.autocomplete_widget import * # noqa diff --git a/glue/utils/qt/colors.py b/glue/utils/qt/colors.py index daa62d712..acd5d2aad 100644 --- a/glue/utils/qt/colors.py +++ b/glue/utils/qt/colors.py @@ -1,220 +1,4 @@ -import numpy as np -from matplotlib.colors import ColorConverter - -from glue import config -from qtpy import QtCore, QtWidgets, QtGui -from echo import add_callback -from glue.utils import nonpartial -from glue.utils.qt.widget_properties import WidgetProperty - -from matplotlib import cm - -__all__ = ['mpl_to_qt_color', 'qt_to_mpl_color', 'cmap2pixmap', - 'tint_pixmap', 'QColorBox', 'ColorProperty', 'connect_color', - 'QColormapCombo'] - - -def mpl_to_qt_color(color, alpha=None): - """ - Convert a matplotlib color string into a Qt QColor object - - Parameters - ---------- - color : str - A color specification that matplotlib understands - alpha : float - Optional opacity. Float in range [0,1] - - Returns - ------- - qcolor : ``QColor`` - A QColor object representing the converted color - """ - if color in [None, 'none', 'None']: - return QtGui.QColor(0, 0, 0, 0) - - cc = ColorConverter() - r, g, b, a = cc.to_rgba(color) - if alpha is not None: - a = alpha - return QtGui.QColor(int(r * 255), int(g * 255), int(b * 255), int(a * 255)) - - -def qt_to_mpl_color(qcolor): - """ - Convert a QColor object into a string that matplotlib understands - - Note: This ignores opacity - - Parameters - ---------- - qcolor : ``QColor`` - The Qt color - - Returns - ------- - color : str - A hex string describing that color - """ - hexid = qcolor.name() - return str(hexid) - - -def cmap2pixmap(cmap, steps=50, size=(100, 100)): - """ - Convert a matplotlib colormap into a QPixmap - - Parameters - ---------- - cmap : :class:`matplotlib.colors.Colormap` - The colormap to use - steps : int - The number of color steps in the output. Default=50 - - Returns - ------- - pixmap : ``QPixmap`` - The QPixmap instance - """ - sm = cm.ScalarMappable(cmap=cmap) - sm.norm.vmin = 0.0 - sm.norm.vmax = 1.0 - inds = np.linspace(0, 1, steps) - rgbas = sm.to_rgba(inds) - rgbas = [QtGui.QColor(int(r * 255), int(g * 255), - int(b * 255), int(a * 255)).rgba() for r, g, b, a in rgbas] - im = QtGui.QImage(steps, 1, QtGui.QImage.Format_Indexed8) - im.setColorTable(rgbas) - for i in range(steps): - im.setPixel(i, 0, i) - im = im.scaled(*size) - pm = QtGui.QPixmap.fromImage(im) - return pm - - -def tint_pixmap(bm, color): - """ - Re-color a monochrome pixmap object using `color` - - Parameters - ---------- - bm : ``QBitmap`` - The Pixmap object - color : ``QColor`` - The Qt color - - Returns - ------- - pixmap : ``QPixmap`` - The new pixmap - """ - if bm.depth() != 1: - raise TypeError("Input pixmap must have a depth of 1: %i" % bm.depth()) - - image = bm.toImage() - image.setColor(1, color.rgba()) - image.setColor(0, QtGui.QColor(0, 0, 0, 0).rgba()) - - result = QtGui.QPixmap.fromImage(image) - return result - - -class ColorProperty(WidgetProperty): - - def getter(self, widget): - return widget.color() - - def setter(self, widget, value): - widget.setColor(value) - - -def connect_color(client, prop, widget): - - def update_widget(text): - widget.setColor(text) - - def update_prop(): - setattr(client, prop, widget.color()) - - add_callback(client, prop, update_widget) - widget.colorChanged.connect(nonpartial(update_prop)) - update_widget(getattr(client, prop)) - - -from echo.qt.autoconnect import HANDLERS -HANDLERS['color'] = connect_color - - -class QColorBox(QtWidgets.QLabel): - - mousePressed = QtCore.Signal() - colorChanged = QtCore.Signal() - - def __init__(self, *args, **kwargs): - super(QColorBox, self).__init__(*args, **kwargs) - self.mousePressed.connect(nonpartial(self.query_color)) - self.colorChanged.connect(nonpartial(self.on_color_change)) - self.setColor("#000000") - - def mousePressEvent(self, event): - self.mousePressed.emit() - event.accept() - - def query_color(self): - color = QtWidgets.QColorDialog.getColor(self._qcolor, parent=self) - if color.isValid(): - self.setColor(qt_to_mpl_color(color)) - - def setColor(self, color): - self._color = color - self.colorChanged.emit() - - def color(self): - return self._color - - def on_color_change(self): - self._qcolor = mpl_to_qt_color(self.color()) - image = QtGui.QImage(70, 22, QtGui.QImage.Format_RGB32) - try: - image.fill(self._qcolor) - except TypeError: - # PySide and old versions of PyQt require a RGBA integer - image.fill(self._qcolor.rgba()) - pixmap = QtGui.QPixmap.fromImage(image) - self.setPixmap(pixmap) - - -from echo.qt.connect import UserDataWrapper - - -class QColormapCombo(QtWidgets.QComboBox): - - def __init__(self, *args, **kwargs): - super(QColormapCombo, self).__init__(*args, **kwargs) - for label, cmap in config.colormaps: - self.addItem(label, userData=UserDataWrapper(cmap)) - self._update_icons() - - def _update_icons(self): - self.setIconSize(QtCore.QSize(self.width(), 15)) - for index in range(self.count()): - cmap = self.itemData(index).data - icon = QtGui.QIcon(cmap2pixmap(cmap, size=(self.width(), 15), steps=200)) - self.setItemIcon(index, icon) - - def resizeEvent(self, *args, **kwargs): - super(QColormapCombo, self).resizeEvent(*args, **kwargs) - self._update_icons() - - -if __name__ == "__main__": - - from glue.utils.qt import get_qapp - - app = get_qapp() - - label = QColorBox() - label.resize(100, 100) - label.show() - label.raise_() - app.exec_() +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.utils.qt.colors is deprecated, use glue_qt.utils.colors instead', GlueDeprecationWarning) +from glue_qt.utils.colors import * # noqa diff --git a/glue/utils/qt/decorators.py b/glue/utils/qt/decorators.py index f4eff8b51..36f64a5d1 100644 --- a/glue/utils/qt/decorators.py +++ b/glue/utils/qt/decorators.py @@ -1,101 +1,4 @@ -import os -import sys -import traceback -from contextlib import contextmanager -from functools import wraps - -__all__ = ['set_cursor', 'set_cursor_cm', 'messagebox_on_error', - 'die_on_error'] - - -def set_cursor(shape): - """Set the Qt cursor for the duration of a function call, and unset - - :param shape: Cursor shape to set. - """ - def wrapper(func): - @wraps(func) - def result(*args, **kwargs): - from glue.utils.qt import get_qapp # Here to avoid circ import - app = get_qapp() - app.setOverrideCursor(shape) - try: - return func(*args, **kwargs) - finally: - app.restoreOverrideCursor() - return result - - return wrapper - - -# TODO: Does this really belong in this module? -@contextmanager -def set_cursor_cm(shape): - """Context manager equivalent for :func:`set_cursor`.""" - from glue.utils.qt import get_qapp - app = get_qapp() - app.setOverrideCursor(shape) - try: - yield - finally: - app.restoreOverrideCursor() - - -# TODO: We should be able to avoid defining these as classes and defining -# __call__ below, by using contextmanager. - -class messagebox_on_error(object): - - def __init__(self, msg, sep='\n', exit=False): - self.msg = msg - self.sep = sep - self.exit = exit - - def __call__(self, f): - @wraps(f) - def decorated(*args, **kwargs): - # If we are in glue testing mode, just execute function - if os.environ.get("GLUE_TESTING") == 'True': - return f(*args, **kwargs) - with self: - return f(*args, **kwargs) - return decorated - - def __enter__(self): - pass - - def __exit__(self, exc_type, exc_val, tb): - - if exc_type is None: - return - - # Make sure application has been started - from glue.utils.qt import get_qapp # Here to avoid circular import - get_qapp() - - m = "%s\n%s" % (self.msg, exc_val) - detail = ''.join(traceback.format_exception(exc_type, exc_val, tb)) - if len(m) > 500: - detail = "Full message:\n\n%s\n\n%s" % (m, detail) - m = m[:500] + '...' - - from qtpy import QtWidgets - from qtpy.QtCore import Qt - - qmb = QtWidgets.QMessageBox(QtWidgets.QMessageBox.Critical, "Error", m) - qmb.setDetailedText(detail) - qmb.resize(400, qmb.size().height()) - qmb.setTextInteractionFlags(Qt.TextSelectableByMouse) - qmb.exec_() - - if self.exit: - sys.exit(1) - - # Just for cases where we are testing and patching sys.exit - return True - - -class die_on_error(messagebox_on_error): - - def __init__(self, msg): - super(die_on_error, self).__init__(msg, exit=True) +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.utils.qt.decorators is deprecated, use glue_qt.utils.decorators instead', GlueDeprecationWarning) +from glue_qt.utils.decorators import * # noqa diff --git a/glue/utils/qt/delegates.py b/glue/utils/qt/delegates.py index 76f1ea7fb..8906740dc 100644 --- a/glue/utils/qt/delegates.py +++ b/glue/utils/qt/delegates.py @@ -1,60 +1,4 @@ -from qtpy import QtWidgets, QtGui, QtCore - - -class HtmlItemDelegate(QtWidgets.QStyledItemDelegate): - """ - An item delegate that can be used for e.g. QTreeView, QTreeWidget, - QListView or QListWidget. This will automatically interpret any HTML that - is inside the items in these views/widgets. - - This is more efficient than using e.g. QLabel instances embedded in the - views/widgets, and is required for horizontal scroll bars to appear - correctly. - """ - - # Implementation adapted based on solutions presented on StackOverflow: - # https://stackoverflow.com/questions/1956542/how-to-make-item-view-render-rich-html-text-in-qt - - def paint(self, painter, option, index): - - options = QtWidgets.QStyleOptionViewItem(option) - self.initStyleOption(options, index) - - painter.save() - - doc = QtGui.QTextDocument() - doc.setTextWidth(options.rect.width()) - - text_option = QtGui.QTextOption() - text_option.setWrapMode(QtGui.QTextOption.NoWrap) - text_option.setAlignment(options.displayAlignment) - doc.setDefaultTextOption(text_option) - - doc.setHtml(options.text) - - options.text = "" - options.widget.style().drawControl(QtWidgets.QStyle.CE_ItemViewItem, options, painter) - - iconSize = options.icon.actualSize(options.rect.size()) - painter.translate(options.rect.left() + iconSize.width(), options.rect.top()) - clip = QtCore.QRectF(0, 0, options.rect.width() + iconSize.width(), options.rect.height()) - - doc.drawContents(painter, clip) - - painter.restore() - - def sizeHint(self, option, index): - - options = QtWidgets.QStyleOptionViewItem(option) - self.initStyleOption(options, index) - - doc = QtGui.QTextDocument() - - text_option = QtGui.QTextOption() - text_option.setWrapMode(QtGui.QTextOption.NoWrap) - doc.setDefaultTextOption(text_option) - - doc.setHtml(options.text) - doc.setTextWidth(options.rect.width()) - - return QtCore.QSize(doc.idealWidth(), doc.size().height()) +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.utils.qt.delegates is deprecated, use glue_qt.utils.delegates instead', GlueDeprecationWarning) +from glue_qt.utils.delegates import * # noqa diff --git a/glue/utils/qt/dialogs.py b/glue/utils/qt/dialogs.py index 2d6081c9f..dcd1d13b9 100644 --- a/glue/utils/qt/dialogs.py +++ b/glue/utils/qt/dialogs.py @@ -1,99 +1,4 @@ -from qtpy import QtWidgets - -__all__ = ['pick_item', 'pick_class', 'get_text', 'CenteredDialog'] - - -def pick_item(items, labels, title="Pick an item", label="Pick an item", - default=None): - """ - Prompt the user to choose an item - - Returns the selected item, or `None` - - Parameters - ---------- - items : iterable - Items to choose (can be any Python object) - labels : iterables - Labels for the items to choose - title : str, optional - The title of the dialog - label : str, optional - The prompt message - """ - - if default in items: - current = items.index(default) - else: - current = 0 - - choice, isok = QtWidgets.QInputDialog.getItem(None, title, label, - labels, current=current, - editable=False) - if isok: - index = labels.index(str(choice)) - return items[index] - - -def pick_class(classes, sort=False, **kwargs): - """ - Prompt the user to pick from a list of classes using Qt - - This is the same as `pick_item`, but the labels are automatically determined - from the classes using the LABEL attribute, and if not set, then the - __name__. - - Returns the class that was selected, or `None` - - Parameters - ---------- - classes : iterable - The classes to choose from - title : str, optional - The title of the dialog - label : str, optional - The prompt message - """ - def _label(c): - try: - return c.LABEL - except AttributeError: - return c.__name__ - - if sort: - classes = sorted(classes, key=lambda x: _label(x)) - choices = [_label(c) for c in classes] - return pick_item(classes, choices, **kwargs) - - -def get_text(title='Enter a label', default=None): - """ - Prompt the user to enter text using Qt - - Returns the text the user typed, or `None` - - Parameters - ---------- - title : str - The prompt message and widget title. - default : str - The default text to show in the prompt. - """ - result, isok = QtWidgets.QInputDialog.getText(None, title, title, text=default) - if isok: - return str(result) - - -class CenteredDialog(QtWidgets.QDialog): - """ - A dialog that is centered on the screen. - """ - - def center(self): - # Adapted from StackOverflow - # https://stackoverflow.com/questions/20243637/pyqt4-center-window-on-active-screen - frameGm = self.frameGeometry() - screen = QtWidgets.QApplication.desktop().screenNumber(QtWidgets.QApplication.desktop().cursor().pos()) - centerPoint = QtWidgets.QApplication.desktop().screenGeometry(screen).center() - frameGm.moveCenter(centerPoint) - self.move(frameGm.topLeft()) +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.utils.qt.dialogs is deprecated, use glue_qt.utils.dialogs instead', GlueDeprecationWarning) +from glue_qt.utils.dialogs import * # noqa diff --git a/glue/utils/qt/helpers.py b/glue/utils/qt/helpers.py index 0b6fc238b..07138b9d9 100644 --- a/glue/utils/qt/helpers.py +++ b/glue/utils/qt/helpers.py @@ -1,229 +1,4 @@ -import os -import sys -from contextlib import contextmanager - -from qtpy import QtCore, QtWidgets -from qtpy.QtCore import Qt -from qtpy.uic import loadUi -from glue.utils.qt import get_text - -__all__ = ['update_combobox', 'GlueTabBar', 'load_ui', 'process_dialog', - 'combo_as_string', 'qurl_to_path'] - - -def update_combobox(combo, labeldata, default_index=0, block_signals=True): - """ - Redefine the items in a QComboBox - - Parameters - ---------- - widget : QComboBox - The widget to update - labeldata : sequence of N (label, data) tuples - The combobox will contain N items with the appropriate - labels, and data set as the userData - - Returns - ------- - combo : QComboBox - The updated input - - Notes - ----- - - If the current userData in the combo box matches - any of labeldata, that selection will be retained. - Otherwise, the first item will be selected. - - Signals are disabled while the combo box is updated - - The QComboBox is modified inplace - """ - - if block_signals: - combo.blockSignals(True) - idx = combo.currentIndex() - if idx >= 0: - current = combo.itemData(idx) - else: - current = None - - combo.clear() - index = None - for i, (label, data) in enumerate(labeldata): - combo.addItem(label, userData=data) - if data is current or data == current: - index = i - - if default_index < 0: - default_index = combo.count() + default_index - - if index is None: - index = min(default_index, combo.count() - 1) - combo.setCurrentIndex(index) - - if block_signals: - combo.blockSignals(False) - - # We need to force emit this, otherwise if the index happens to be the - # same as before, even if the data is different, callbacks won't be - # called. So we block the signals until just before now then always call - # callback manually. - combo.currentIndexChanged.emit(index) - - -class GlueTabBar(QtWidgets.QTabBar): - - def __init__(self, *args, **kwargs): - super(GlueTabBar, self).__init__(*args, **kwargs) - - def choose_rename_tab(self, index=None): - """ - Prompt user to rename a tab - - Parameters - ---------- - index : int - Index of tab to edit. Defaults to current index - """ - index = index or self.currentIndex() - label = get_text("New Tab Label") - if not label: - return - self.rename_tab(index, label) - - def rename_tab(self, index, label): - """ - Updates the name used for given tab - - Parameters - ---------- - index : int - Index of tab to edit. Defaults to current index - label : str - New label to use for this tab - """ - self.setTabText(index, label) - - def mouseDoubleClickEvent(self, event): - if event.button() != Qt.LeftButton: - return - index = self.tabAt(event.pos()) - if index >= 0: - self.choose_rename_tab(index) - - -def load_ui(path, parent=None, directory=None): - """ - Load a .ui file - - Parameters - ---------- - path : str - Name of ui file to load - - parent : QObject - Object to use as the parent of this widget - - Returns - ------- - w - The new widget - """ - - if directory is not None: - full_path = os.path.join(directory, path) - else: - full_path = os.path.abspath(path) - - if not os.path.exists(full_path) and 'site-packages.zip' in full_path: - # Workaround for Mac app - full_path = os.path.join(full_path.replace('site-packages.zip', 'glue')) - - return loadUi(full_path, parent) - - -@contextmanager -def process_dialog(delay=0, accept=False, reject=False, function=None): - """ - Context manager to automatically capture the active dialog and carry out - certain actions. - - Note that only one of ``accept``, ``reject``, or ``function`` should be - specified. - - Parameters - ---------- - delay : int, optional - The delay in ms before acting on the dialog (since it may not yet exist - when the context manager is called). - accept : bool, optional - If `True`, accept the dialog after the specified delay. - reject : bool, optional - If `False`, reject the dialog after the specified delay - function : func, optional - For more complex user actions, specify a function that takes the dialog - as the first and only argument. - """ - - def _accept(dialog): - dialog.accept() - - def _reject(dialog): - dialog.reject() - - n_args = sum((accept, reject, function is not None)) - - if n_args > 1: - raise ValueError("Only one of ``accept``, ``reject``, or " - "``function`` should be specified") - elif n_args == 0: - raise ValueError("One of ``accept``, ``reject``, or " - "``function`` should be specified") - - if accept: - function = _accept - elif reject: - function = _reject - - def wrapper(): - from glue.utils.qt import get_qapp - app = get_qapp() - # Make sure that any window/dialog that needs to be shown is shown - app.processEvents() - dialog = app.activeWindow() - function(dialog) - - timer = QtCore.QTimer() - timer.setInterval(delay) - timer.setSingleShot(True) - timer.timeout.connect(wrapper) - timer.start() - - yield - - -def combo_as_string(combo): - """ - Return the text labels of a combo box as a string to make it easier to - check the content of a combo box in tests. - """ - items = [combo.itemText(i) for i in range(combo.count())] - return ":".join(items) - - -def qurl_to_path(url): - """ - Convert a local QUrl to a normal path - """ - - # Get path to file - path = url.path() - - # Workaround for a Qt bug that causes paths to start with a / - # on Windows: https://bugreports.qt.io/browse/QTBUG-46417 - if sys.platform.startswith('win'): - if path.startswith('/') and path[2] == ':': - path = path[1:] - - return path +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.utils.qt.helpers is deprecated, use glue_qt.utils.helpers instead', GlueDeprecationWarning) +from glue_qt.utils.helpers import * # noqa diff --git a/glue/utils/qt/mime.py b/glue/utils/qt/mime.py index 939df4b09..10a67dd2b 100644 --- a/glue/utils/qt/mime.py +++ b/glue/utils/qt/mime.py @@ -1,56 +1,4 @@ -from qtpy import QtCore - -__all__ = ['PyMimeData'] - - -class PyMimeData(QtCore.QMimeData): - """ - A custom MimeData object that stores live python objects - - Associate specific objects with a mime type by passing - mime type / object key/value pairs to the __init__ method - - If a single object is passed to the init method, that - object is associated with the PyMimeData.MIME_TYPE mime type - """ - MIME_TYPE = 'application/py_instance' - - def __init__(self, instance=None, **kwargs): - """ - :param instance: The python object to store - - kwargs: Optional mime type / objects pairs to store as objects - """ - super(PyMimeData, self).__init__() - - self._instances = {} - - self.setData(self.MIME_TYPE, instance) - for k, v in kwargs.items(): - self.setData(k, v) - - def formats(self): - return list(set(super(PyMimeData, self).formats() + - list(self._instances.keys()))) - - def hasFormat(self, fmt): - return fmt in self._instances or super(PyMimeData, self).hasFormat(fmt) - - def setData(self, mime, data): - try: - super(PyMimeData, self).setData(mime, QtCore.QByteArray(1, b'1')) - except TypeError: # PySide6 - super(PyMimeData, self).setData(mime, QtCore.QByteArray(b'1')) - - self._instances[mime] = data - - def data(self, mime_type): - """ Retrieve the data stored at the specified mime_type - - If mime_type is application/py_instance, a python object - is returned. Otherwise, a QtCore.QByteArray is returned """ - - if str(mime_type) in self._instances: - return self._instances[mime_type] - - return super(PyMimeData, self).data(mime_type) +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.utils.qt.mime is deprecated, use glue_qt.utils.mime instead', GlueDeprecationWarning) +from glue_qt.utils.mime import * # noqa diff --git a/glue/utils/qt/mixins.py b/glue/utils/qt/mixins.py index b780b5041..0037a89ae 100644 --- a/glue/utils/qt/mixins.py +++ b/glue/utils/qt/mixins.py @@ -1,74 +1,4 @@ -from glue.utils.qt.mime import PyMimeData - -__all__ = ['GlueItemWidget'] - - -class GlueItemWidget(object): - - """ - A mixin for QtWidgets.QListWidget/GlueTreeWidget subclasses, that provides - drag+drop functionality. - """ - # Implementation detail: QXXWidgetItems are unhashable in PySide, - # and cannot be used as dictionary keys. we hash on IDs instead - - SUPPORTED_MIME_TYPE = None - - def __init__(self, parent=None): - super(GlueItemWidget, self).__init__(parent) - self._mime_data = {} - self.setDragEnabled(True) - - def mimeTypes(self): - """ - Return the list of MIME Types supported for this object. - """ - types = [self.SUPPORTED_MIME_TYPE] - return types - - def mimeData(self, selected_items): - """ - Return a list of MIME data associated with the each selected item. - - Parameters - ---------- - selected_items : list - A list of ``QtWidgets.QListWidgetItems`` or ``QtWidgets.QTreeWidgetItems`` instances - - Returns - ------- - result : list - A list of MIME objects - """ - try: - data = [self.get_data(i) for i in selected_items] - except KeyError: - data = None - result = PyMimeData(data, **{self.SUPPORTED_MIME_TYPE: data}) - - # apparent bug in pyside garbage collects custom mime - # data, and crashes. Save result here to avoid - self._mime = result - - return result - - def get_data(self, item): - """ - Convenience method to fetch the data associated with a ``QxxWidgetItem``. - """ - # return item.data(Qt.UserRole) - return self._mime_data.get(id(item), None) - - def set_data(self, item, data): - """ - Convenience method to set data associated with a ``QxxWidgetItem``. - """ - # item.setData(Qt.UserRole, data) - self._mime_data[id(item)] = data - - def drop_data(self, item): - self._mime_data.pop(id(item)) - - @property - def data(self): - return self._mime_data +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.utils.qt.mixins is deprecated, use glue_qt.utils.mixins instead', GlueDeprecationWarning) +from glue_qt.utils.mixins import * # noqa diff --git a/glue/utils/qt/python_list_model.py b/glue/utils/qt/python_list_model.py index faeeabc06..a3ad8699b 100644 --- a/glue/utils/qt/python_list_model.py +++ b/glue/utils/qt/python_list_model.py @@ -1,151 +1,4 @@ -from qtpy import QtCore -from qtpy.QtCore import Qt - -__all__ = ['PythonListModel'] - - -class PythonListModel(QtCore.QAbstractListModel): - - """ - A Qt Model that wraps a python list, and exposes a list-like interface - - This can be connected directly to multiple QListViews, which will - stay in sync with the state of the container. - """ - - def __init__(self, items, parent=None): - """ - Create a new model - - Parameters - ---------- - items : list - The initial list to wrap - parent : QObject - The model parent - """ - super(PythonListModel, self).__init__(parent) - self.items = items - - def rowCount(self, parent=None): - """Number of rows""" - return len(self.items) - - def headerData(self, section, orientation, role): - """Column labels""" - if role != Qt.DisplayRole: - return None - return "%i" % section - - def row_label(self, row): - """ The textual label for the row""" - return str(self.items[row]) - - def data(self, index, role): - """Retrieve data at each index""" - if not index.isValid(): - return None - if role == Qt.DisplayRole or role == Qt.EditRole: - return self.row_label(index.row()) - if role == Qt.UserRole: - return self.items[index.row()] - - def setData(self, index, value, role): - """ - Update the data in-place - - Parameters - ---------- - index : QModelIndex - The location of the change - value : object - The new value - role : QEditRole - Which aspect of the model to update - """ - if not index.isValid(): - return False - - if role == Qt.UserRole: - row = index.row() - self.items[row] = value - self.dataChanged.emit(index, index) - return True - - return super(PythonListModel, self).setData(index, value, role) - - def removeRow(self, row, parent=None): - """ - Remove a row from the table - - Parameters - ---------- - row : int - Row to remove - - Returns - ------- - successful : bool - """ - if row < 0 or row >= len(self.items): - return False - - self.beginRemoveRows(QtCore.QModelIndex(), row, row) - self._remove_row(row) - self.endRemoveRows() - return True - - def pop(self, row=None): - """ - Remove and return an item (default last item) - - Parameters - ---------- - row : int (optional) - Which row to remove. Default=last - - Returns - ------- - popped : object - """ - if row is None: - row = len(self) - 1 - result = self[row] - self.removeRow(row) - return result - - def _remove_row(self, row): - # actually remove data. Subclasses can override this as needed - self.items.pop(row) - - def __getitem__(self, row): - return self.items[row] - - def __setitem__(self, row, value): - index = self.index(row) - self.setData(index, value, role=Qt.UserRole) - - def __len__(self): - return len(self.items) - - def insert(self, row, value): - self.beginInsertRows(QtCore.QModelIndex(), row, row) - self.items.insert(row, value) - self.endInsertRows() - self.rowsInserted.emit(self.index(row), row, row) - - def append(self, value): - row = len(self) - self.insert(row, value) - - def extend(self, values): - for v in values: - self.append(v) - - def set_list(self, values): - """ - Set the model to a new list - """ - self.beginResetModel() - self.items = values - self.endResetModel() +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.utils.qt.python_list_model is deprecated, use glue_qt.utils.python_list_model instead', GlueDeprecationWarning) +from glue_qt.utils.python_list_model import * # noqa diff --git a/glue/utils/qt/tests/__init__.py b/glue/utils/qt/tests/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/glue/utils/qt/tests/test_colors.py b/glue/utils/qt/tests/test_colors.py deleted file mode 100644 index e4e102118..000000000 --- a/glue/utils/qt/tests/test_colors.py +++ /dev/null @@ -1,54 +0,0 @@ -from unittest.mock import MagicMock - -from echo import CallbackProperty -from qtpy import QtGui - -from ..colors import qt_to_mpl_color, QColorBox, connect_color, QColormapCombo - - -def test_colors(): - assert qt_to_mpl_color(QtGui.QColor(255, 0, 0)) == '#ff0000' - assert qt_to_mpl_color(QtGui.QColor(255, 255, 255)) == '#ffffff' - - -# TODO: add a test for the other way around - -# TODO: add a test for cmap2pixmap - -# TODO: add a test for tint_pixmap - -def test_color_box(): - - func = MagicMock() - - label = QColorBox() - label.resize(100, 100) - label.colorChanged.connect(func) - label.setColor('#472822') - - assert func.call_count == 1 - - -def test_connect_color(): - - class FakeClass(object): - color = CallbackProperty() - - c = FakeClass() - - label = QColorBox() - - connect_color(c, 'color', label) - - label.setColor('#472822') - - assert c.color == '#472822' - - c.color = '#012345' - - assert label.color() == '#012345' - - -def test_colormap_combo(): - - combo = QColormapCombo() diff --git a/glue/utils/qt/tests/test_decorators.py b/glue/utils/qt/tests/test_decorators.py deleted file mode 100644 index 1a3a2d1fd..000000000 --- a/glue/utils/qt/tests/test_decorators.py +++ /dev/null @@ -1,98 +0,0 @@ -import os -from unittest.mock import patch - -from ..decorators import messagebox_on_error, die_on_error - - -def test_messagebox_on_error(): - - os.environ['GLUE_TESTING'] = 'False' - - def failing_function(): - raise ValueError("Dialog failure") - - def working_function(): - pass - - @messagebox_on_error('An error occurred') - def decorated_failing_function(): - failing_function() - - @messagebox_on_error('An error occurred') - def decorated_working_function(): - working_function() - - # Test decorator - - with patch('qtpy.QtWidgets.QMessageBox') as mb: - decorated_failing_function() - assert mb.call_args[0][2] == 'An error occurred\nDialog failure' - - with patch('qtpy.QtWidgets.QMessageBox') as mb: - decorated_working_function() - assert mb.call_count == 0 - - # Test context manager - - with patch('qtpy.QtWidgets.QMessageBox') as mb: - with messagebox_on_error('An error occurred'): - failing_function() - assert mb.call_args[0][2] == 'An error occurred\nDialog failure' - - with patch('qtpy.QtWidgets.QMessageBox') as mb: - with messagebox_on_error('An error occurred'): - working_function() - assert mb.call_count == 0 - - os.environ['GLUE_TESTING'] = 'True' - - -def test_die_on_error(): - - os.environ['GLUE_TESTING'] = 'False' - - def failing_function(): - raise ValueError("Dialog failure") - - def working_function(): - pass - - @die_on_error('An error occurred') - def decorated_failing_function(): - failing_function() - - @die_on_error('An error occurred') - def decorated_working_function(): - working_function() - - # Test decorator - - with patch('sys.exit') as exit: - with patch('qtpy.QtWidgets.QMessageBox') as mb: - decorated_failing_function() - assert mb.call_args[0][2] == 'An error occurred\nDialog failure' - assert exit.called_once_with(1) - - with patch('sys.exit') as exit: - with patch('qtpy.QtWidgets.QMessageBox') as mb: - decorated_working_function() - assert mb.call_count == 0 - assert exit.call_count == 0 - - # Test context manager - - with patch('sys.exit') as exit: - with patch('qtpy.QtWidgets.QMessageBox') as mb: - with die_on_error('An error occurred'): - failing_function() - assert mb.call_args[0][2] == 'An error occurred\nDialog failure' - assert exit.called_once_with(1) - - with patch('sys.exit') as exit: - with patch('qtpy.QtWidgets.QMessageBox') as mb: - with die_on_error('An error occurred'): - working_function() - assert mb.call_count == 0 - assert exit.call_count == 0 - - os.environ['GLUE_TESTING'] = 'True' diff --git a/glue/utils/qt/tests/test_dialogs.py b/glue/utils/qt/tests/test_dialogs.py deleted file mode 100644 index 439143128..000000000 --- a/glue/utils/qt/tests/test_dialogs.py +++ /dev/null @@ -1,49 +0,0 @@ -from unittest import mock - -from ..dialogs import pick_item, pick_class, get_text - - -def test_pick_item(): - - items = ['a', 'b', 'c'] - labels = ['1', '2', '3'] - - with mock.patch('qtpy.QtWidgets.QInputDialog') as d: - d.getItem.return_value = '1', True - assert pick_item(items, labels) == 'a' - d.getItem.return_value = '2', True - assert pick_item(items, labels) == 'b' - d.getItem.return_value = '3', True - assert pick_item(items, labels) == 'c' - d.getItem.return_value = '3', False - assert pick_item(items, labels) is None - - -def test_pick_class(): - - class Foo: - pass - - class Bar: - pass - - Bar.LABEL = 'Baz' - - with mock.patch('glue.utils.qt.dialogs.pick_item') as d: - pick_class([Foo, Bar], default=Foo) - d.assert_called_once_with([Foo, Bar], ['Foo', 'Baz'], default=Foo) - - with mock.patch('glue.utils.qt.dialogs.pick_item') as d: - pick_class([Foo, Bar], sort=True) - d.assert_called_once_with([Bar, Foo], ['Baz', 'Foo']) - - -def test_get_text(): - - with mock.patch('qtpy.QtWidgets.QInputDialog') as d: - - d.getText.return_value = 'abc', True - assert get_text() == 'abc' - - d.getText.return_value = 'abc', False - assert get_text() is None diff --git a/glue/utils/qt/tests/test_helpers.py b/glue/utils/qt/tests/test_helpers.py deleted file mode 100644 index 92e91efd3..000000000 --- a/glue/utils/qt/tests/test_helpers.py +++ /dev/null @@ -1,40 +0,0 @@ -from qtpy import QtWidgets -from ..helpers import update_combobox - - -def test_update_combobox(): - combo = QtWidgets.QComboBox() - update_combobox(combo, [('a', 1), ('b', 2)]) - update_combobox(combo, [('c', 3)]) - - -def test_update_combobox_indexchanged(): - - # Regression test for bug that caused currentIndexChanged to not be - # emitted if the new index happened to be the same as the old one but the - # label data was different. - - class MyComboBox(QtWidgets.QComboBox): - - def __init__(self, *args, **kwargs): - self.change_count = 0 - super(MyComboBox, self).__init__(*args, **kwargs) - self.currentIndexChanged.connect(self.changed) - - def changed(self): - self.change_count += 1 - - combo = MyComboBox() - update_combobox(combo, [('a', 1), ('b', 2)]) - update_combobox(combo, [('c', 3)]) - - assert combo.change_count == 2 - assert combo.currentIndex() == 0 - - combo = MyComboBox() - update_combobox(combo, [('a', 1), ('b', 2)]) - update_combobox(combo, [('a', 1), ('b', 3)]) - update_combobox(combo, [('a', 3), ('b', 1)]) - - assert combo.change_count == 3 - assert combo.currentIndex() == 1 diff --git a/glue/utils/qt/tests/test_mime.py b/glue/utils/qt/tests/test_mime.py deleted file mode 100644 index 7a889f530..000000000 --- a/glue/utils/qt/tests/test_mime.py +++ /dev/null @@ -1,109 +0,0 @@ -# import pytest -# -# from qtpy.QtTest import QTest -# from qtpy.QtCore import Qt -# from qtpy import QtWidgets - -from .. import mime - - -INSTANCE_MIME_TYPE = mime.PyMimeData.MIME_TYPE -TEST_MIME_TYPE_1 = 'test1/test1' -TEST_MIME_TYPE_2 = 'test2/test2' - - -class TestMime(): - - def test_formats(self): - d = mime.PyMimeData() - assert set(d.formats()) == set([INSTANCE_MIME_TYPE]) - - d = mime.PyMimeData(**{'text/plain': 'hello'}) - assert set(d.formats()) == set([INSTANCE_MIME_TYPE, 'text/plain']) - - def test_empty_has_format(self): - d = mime.PyMimeData() - assert d.hasFormat(INSTANCE_MIME_TYPE) - assert not d.hasFormat(TEST_MIME_TYPE_1) - assert not d.hasFormat(TEST_MIME_TYPE_2) - - def test_instance_format(self): - d = mime.PyMimeData(5) - assert d.hasFormat(INSTANCE_MIME_TYPE) - assert not d.hasFormat(TEST_MIME_TYPE_1) - assert not d.hasFormat(TEST_MIME_TYPE_2) - - def test_layer_format(self): - d = mime.PyMimeData(5, **{TEST_MIME_TYPE_1: 10}) - assert d.hasFormat(INSTANCE_MIME_TYPE) - assert d.hasFormat(TEST_MIME_TYPE_1) - assert not d.hasFormat(TEST_MIME_TYPE_2) - - def test_layers_format(self): - d = mime.PyMimeData(5, **{TEST_MIME_TYPE_2: 10}) - assert d.hasFormat(INSTANCE_MIME_TYPE) - assert d.hasFormat(TEST_MIME_TYPE_2) - assert not d.hasFormat(TEST_MIME_TYPE_1) - - def test_retrieve_instance(self): - d = mime.PyMimeData(10) - assert d.data(INSTANCE_MIME_TYPE) == 10 - - def test_retrieve_layer(self): - d = mime.PyMimeData(**{TEST_MIME_TYPE_2: 12}) - assert d.data(TEST_MIME_TYPE_2) == 12 - - d = mime.PyMimeData(**{TEST_MIME_TYPE_1: 12}) - assert d.data(TEST_MIME_TYPE_1) == 12 - - def test_retrieve_not_present_returns_null(self): - d = mime.PyMimeData() - assert d.data('not-a-format').size() == 0 - - -# class TestWidget(QtWidgets.QWidget): -# def __init__(self, out_mime, parent=None): -# super(TestWidget, self).__init__(parent) -# self.setAcceptDrops(True) -# -# self.last_mime = None -# self.out_mime = out_mime -# -# def dragEnterEvent(self, event): -# print('drag enter') -# event.accept() -# -# def dropEvent(self, event): -# print('drop') -# self.last_mime = event.mimeData() -# -# def mousePressEvent(self, event): -# print('mouse event') -# drag = QtWidgets.QDrag(self) -# drag.setMimeData(self.out_mime) -# drop_action = drag.exec_() -# print(drop_action) -# event.accept() -# -# -# class TestMimeDragAndDrop(object): -# -# def setup_method(self, method): -# -# m1 = mime.PyMimeData(1, **{'text/plain': 'hi', 'test': 4}) -# m2 = mime.PyMimeData(1, **{'test': 5}) -# -# w1 = TestWidget(m1) -# w2 = TestWidget(m2) -# -# self.w1 = w1 -# self.w2 = w2 -# self.m1 = m1 -# self.m2 = m2 -# -# def test_drag_drop(self): -# QTest.mousePress(self.w1, Qt.LeftButton) -# QTest.mouseMove(self.w2) -# QTest.mouseRelease(self.w2, Qt.LeftButton) -# -# assert self.w2.last_mime == self.m1 diff --git a/glue/utils/qt/tests/test_python_list_model.py b/glue/utils/qt/tests/test_python_list_model.py deleted file mode 100644 index e3aa17022..000000000 --- a/glue/utils/qt/tests/test_python_list_model.py +++ /dev/null @@ -1,84 +0,0 @@ -import pytest - -from qtpy.QtCore import Qt - -from ..python_list_model import PythonListModel - - -class TestListModel(object): - - def test_row_count(self): - assert PythonListModel([]).rowCount() == 0 - assert PythonListModel([1]).rowCount() == 1 - assert PythonListModel([1, 2]).rowCount() == 2 - - def test_data_display(self): - m = PythonListModel([1, 'a']) - i = m.index(0) - assert m.data(i, role=Qt.DisplayRole) == '1' - - i = m.index(1) - assert m.data(i, role=Qt.DisplayRole) == 'a' - - def test_data_edit(self): - m = PythonListModel([1, 'a']) - i = m.index(0) - assert m.data(i, role=Qt.EditRole) == '1' - - i = m.index(1) - assert m.data(i, role=Qt.EditRole) == 'a' - - def test_data_user(self): - m = PythonListModel([1, 'a']) - i = m.index(0) - assert m.data(i, role=Qt.UserRole) == 1 - - i = m.index(1) - assert m.data(i, role=Qt.UserRole) == 'a' - - def test_itemget(self): - m = PythonListModel([1, 'a']) - assert m[0] == 1 - assert m[1] == 'a' - - def test_itemset(self): - m = PythonListModel([1, 'a']) - m[0] = 'b' - assert m[0] == 'b' - - @pytest.mark.parametrize('items', ([], [1, 2, 3], [1])) - def test_len(self, items): - assert len(PythonListModel(items)) == len(items) - - def test_pop(self): - m = PythonListModel([1, 2, 3]) - assert m.pop() == 3 - assert len(m) == 2 - assert m.pop(0) == 1 - assert len(m) == 1 - assert m[0] == 2 - - def test_append(self): - m = PythonListModel([]) - m.append(2) - assert m[0] == 2 - m.append(3) - assert m[1] == 3 - m.pop() - m.append(4) - assert m[1] == 4 - - def test_extend(self): - m = PythonListModel([]) - m.extend([2, 3]) - assert m[0] == 2 - assert m[1] == 3 - - def test_insert(self): - m = PythonListModel([1, 2, 3]) - m.insert(1, 5) - assert m[1] == 5 - - def test_iter(self): - m = PythonListModel([1, 2, 3]) - assert list(m) == [1, 2, 3] diff --git a/glue/utils/qt/tests/test_widget_properties.py b/glue/utils/qt/tests/test_widget_properties.py deleted file mode 100644 index 1bcd87c26..000000000 --- a/glue/utils/qt/tests/test_widget_properties.py +++ /dev/null @@ -1,180 +0,0 @@ -import pytest - -from qtpy import QtWidgets - -from echo.qt.connect import UserDataWrapper - -from ..widget_properties import (CurrentComboDataProperty, - CurrentComboTextProperty, - CurrentTabProperty, - TextProperty, - ButtonProperty, - FloatLineProperty, - ValueProperty) - - -def test_combo_data(): - - class TestClass(object): - - co = CurrentComboDataProperty('_combo') - - def __init__(self): - self._combo = QtWidgets.QComboBox() - self._combo.addItem('a', UserDataWrapper('a')) - self._combo.addItem('b', UserDataWrapper('b')) - - tc = TestClass() - - tc.co = 'a' - assert tc.co == 'a' - assert tc._combo.currentIndex() == 0 - - tc.co = 'b' - assert tc.co == 'b' - assert tc._combo.currentIndex() == 1 - - with pytest.raises(ValueError) as exc: - tc.co = 'c' - assert exc.value.args[0] == "Cannot find data 'c' in combo box" - - -def test_combo_text(): - - class TestClass(object): - - co = CurrentComboTextProperty('_combo') - - def __init__(self): - self._combo = QtWidgets.QComboBox() - self._combo.addItem('a') - self._combo.addItem('b') - - tc = TestClass() - - tc.co = 'a' - assert tc.co == 'a' - assert tc._combo.currentIndex() == 0 - - tc.co = 'b' - assert tc.co == 'b' - assert tc._combo.currentIndex() == 1 - - with pytest.raises(ValueError) as exc: - tc.co = 'c' - assert exc.value.args[0] == "Cannot find text 'c' in combo box" - - tc.co = None - assert tc.co is None - assert tc._combo.currentIndex() == -1 - - -def test_text(): - - class TestClass(object): - lab = TextProperty('_label') - - def __init__(self): - self._label = QtWidgets.QLabel() - - tc = TestClass() - tc.lab = 'hello' - assert tc.lab == 'hello' - assert tc._label.text() == 'hello' - - -def test_button(): - - class TestClass(object): - but = ButtonProperty('_button') - - def __init__(self): - self._button = QtWidgets.QCheckBox() - - tc = TestClass() - - assert tc.but == tc._button.isChecked() - - tc.but = True - assert tc._button.isChecked() - - tc.but = False - assert not tc._button.isChecked() - - tc._button.setChecked(True) - assert tc.but - - tc._button.setChecked(False) - assert not tc.but - - -def test_float(): - - class TestClass(object): - flt = FloatLineProperty('_float') - - def __init__(self): - self._float = QtWidgets.QLineEdit() - - tc = TestClass() - - tc.flt = 1.0 - assert float(tc._float.text()) == 1.0 - - tc._float.setText('10') - assert tc.flt == 10.0 - - tc._float.setText('') - assert tc.flt == 0.0 - - -def test_value(): - - class TestClass(object): - val1 = ValueProperty('_slider') - val2 = ValueProperty('_slider', value_range=(0, 10)) - val3 = ValueProperty('_slider', value_range=(0.01, 100), log=True) - - def __init__(self): - self._slider = QtWidgets.QSlider() - self._slider.setMinimum(0) - self._slider.setMaximum(100) - - tc = TestClass() - - tc.val1 = 2.0 - assert tc.val1 == 2.0 - assert tc._slider.value() == 2.0 - - tc.val2 = 3.2 - assert tc.val2 == 3.2 - assert tc._slider.value() == 32 - - tc.val3 = 10 - assert tc.val3 == 10 - assert tc._slider.value() == 75 - - -def test_tab(): - - class TestClass(object): - tab = CurrentTabProperty('_tab') - - def __init__(self): - self._tab = QtWidgets.QTabWidget() - self._tab.addTab(QtWidgets.QWidget(), 'tab1') - self._tab.addTab(QtWidgets.QWidget(), 'tab2') - - tc = TestClass() - - tc.tab = 'tab1' - assert tc.tab == 'tab1' - assert tc._tab.currentIndex() == 0 - - tc.tab = 'tab2' - assert tc.tab == 'tab2' - assert tc._tab.currentIndex() == 1 - - with pytest.raises(ValueError) as exc: - tc.tab = 'tab3' - assert exc.value.args[0] == "Cannot find value 'tab3' in tabs" diff --git a/glue/utils/qt/threading.py b/glue/utils/qt/threading.py index a0dfade82..99e05300c 100644 --- a/glue/utils/qt/threading.py +++ b/glue/utils/qt/threading.py @@ -1,45 +1,4 @@ -import sys - -from qtpy import QtCore - -__all__ = ['Worker'] - - -class Worker(QtCore.QThread): - - result = QtCore.Signal(object) - error = QtCore.Signal(object) - - def __init__(self, func, *args, **kwargs): - """ - Execute a function call on a different thread. - - Parameters - ---------- - func : callable - The function object to call - args - Positional arguments to pass to the function - kwargs - Keyword arguments to pass to the function - """ - super(Worker, self).__init__() - self.func = func - self.args = args or () - self.kwargs = kwargs or {} - - def run(self): - """ - Invoke the function. Upon successful completion, the result signal - will be fired with the output of the function If an exception - occurs, the error signal will be fired with the result form - ``sys.exc_info()``. - """ - try: - self.running = True - result = self.func(*self.args, **self.kwargs) - self.running = False - self.result.emit(result) - except Exception: - self.running = False - self.error.emit(sys.exc_info()) +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.utils.qt.threading is deprecated, use glue_qt.utils.threading instead', GlueDeprecationWarning) +from glue_qt.utils.threading import * # noqa diff --git a/glue/utils/qt/widget_properties.py b/glue/utils/qt/widget_properties.py index ad3d707d2..6bb8f6b32 100644 --- a/glue/utils/qt/widget_properties.py +++ b/glue/utils/qt/widget_properties.py @@ -1,271 +1,4 @@ -""" -The classes in this module provide a property-like interface -to widget instance variables in a class. These properties translate -essential pieces of widget state into more convenient python objects -(for example, the check state of a button to a bool). - -Example Use:: - - class Foo(object): - bar = ButtonProperty('_button') - - def __init__(self): - self._button = QtWidgets.QCheckBox() - - f = Foo() - f.bar = True # equivalent to f._button.setChecked(True) - assert f.bar == True -""" - -import math -from functools import reduce - -from glue.logger import logger -from glue.utils.array import pretty_number - -from echo.qt.connect import _find_combo_data, UserDataWrapper - -# Backward-compatibility -from echo.qt import (connect_checkable_button as connect_bool_button, # noqa - connect_combo_data as connect_current_combo, # noqa - connect_combo_text as connect_current_combo_text, # noqa - connect_float_text as connect_float_edit, # noqa - connect_value, connect_text) # noqa - -connect_int_spin = connect_value - -__all__ = ['WidgetProperty', 'CurrentComboDataProperty', - 'CurrentComboTextProperty', 'CurrentTabProperty', 'TextProperty', - 'ButtonProperty', 'FloatLineProperty', 'ValueProperty'] - - -class WidgetProperty(object): - """ - Base class for widget properties - - Subclasses implement, at a minimum, the "get" and "set" methods, - which translate between widget states and python variables - - Parameters - ---------- - att : str - The location, within a class instance, of the widget to wrap around. - If the widget is nested inside another variable, normal '.' syntax - can be used (e.g. 'sub_window.button') - docstring : str, optional - Optional short summary for the property. Used by sphinx. Should be 1 - sentence or less. - """ - - def __init__(self, att, docstring=''): - self.__doc__ = docstring - self._att = att.split('.') - - def __get__(self, instance, type=None): - # Under certain circumstances, PyQt will try and access these properties - # while loading the ui file, so we have to be robust to failures. - # However, we print out a warning if things fail. - try: - widget = reduce(getattr, [instance] + self._att) - return self.getter(widget) - except Exception: - logger.info("An error occured when accessing attribute {0} of {1}. Returning None.".format('.'.join(self._att), instance)) - return None - - def __set__(self, instance, value): - widget = reduce(getattr, [instance] + self._att) - self.setter(widget, value) - - def getter(self, widget): - """ Return the state of a widget. Depends on type of widget, - and must be overridden""" - raise NotImplementedError() # pragma: no cover - - def setter(self, widget, value): - """ Set the state of a widget to a certain value""" - raise NotImplementedError() # pragma: no cover - - -class CurrentComboDataProperty(WidgetProperty): - """ - Wrapper around the data in QComboBox. - """ - - def getter(self, widget): - """ - Return the itemData stored in the currently-selected item - """ - if widget.currentIndex() == -1: - return None - else: - data = widget.itemData(widget.currentIndex()) - if isinstance(data, UserDataWrapper): - return data.data - else: - return data - - def setter(self, widget, value): - """ - Update the currently selected item to the one which stores value in - its itemData - """ - # Note, we don't use findData here because it doesn't work - # well with non-str data - try: - idx = _find_combo_data(widget, value) - except ValueError: - if value is None: - idx = -1 - else: - raise ValueError("Cannot find data '{0}' in combo box".format(value)) - widget.setCurrentIndex(idx) - - -CurrentComboProperty = CurrentComboDataProperty - - -class CurrentComboTextProperty(WidgetProperty): - """ - Wrapper around the text in QComboBox. - """ - - def getter(self, widget): - """ - Return the itemData stored in the currently-selected item - """ - if widget.currentIndex() == -1: - return None - else: - return widget.itemText(widget.currentIndex()) - - def setter(self, widget, value): - """ - Update the currently selected item to the one which stores value in - its itemData - """ - idx = widget.findText(value) - if idx == -1: - if value is not None: - raise ValueError("Cannot find text '{0}' in combo box".format(value)) - widget.setCurrentIndex(idx) - - -class CurrentTabProperty(WidgetProperty): - """ - Wrapper around QTabWidget. - """ - - def getter(self, widget): - """ - Return the itemData stored in the currently-selected item - """ - return widget.tabText(widget.currentIndex()) - - def setter(self, widget, value): - """ - Update the currently selected item to the one which stores value in - its itemData - """ - for idx in range(widget.count()): - if widget.tabText(idx) == value: - break - else: - raise ValueError("Cannot find value '{0}' in tabs".format(value)) - - widget.setCurrentIndex(idx) - - -class TextProperty(WidgetProperty): - """ - Wrapper around the text() and setText() methods for QLabel etc - """ - - def getter(self, widget): - return widget.text() - - def setter(self, widget, value): - widget.setText(value) - if hasattr(widget, 'editingFinished'): - widget.editingFinished.emit() - - -class ButtonProperty(WidgetProperty): - """ - Wrapper around the check state for QAbstractButton widgets - """ - - def getter(self, widget): - return widget.isChecked() - - def setter(self, widget, value): - widget.setChecked(value) - - -class FloatLineProperty(WidgetProperty): - """ - Wrapper around the text state for QLineEdit widgets. - - Assumes that the text is a floating-point number - """ - - def getter(self, widget): - try: - return float(widget.text()) - except ValueError: - return 0 - - def setter(self, widget, value): - widget.setText(pretty_number(value)) - widget.editingFinished.emit() - - -class ValueProperty(WidgetProperty): - """ - Wrapper around widgets with value() and setValue() - - Parameters - ---------- - att : str - The location, within a class instance, of the widget to wrap around. - If the widget is nested inside another variable, normal '.' syntax - can be used (e.g. 'sub_window.button') - docstring : str, optional - Optional short summary for the property. Used by sphinx. Should be 1 - sentence or less. - value_range : tuple, optional - If set, the slider values are mapped to this range. - log : bool, optional - If `True`, the mapping is assumed to be logarithmic instead of - linear. - """ - - def __init__(self, att, docstring='', value_range=None, log=False): - super(ValueProperty, self).__init__(att, docstring=docstring) - - if log: - if value_range is None: - raise ValueError("log option can only be set if value_range is given") - else: - value_range = math.log10(value_range[0]), math.log10(value_range[1]) - - self.log = log - self.value_range = value_range - - def getter(self, widget): - val = widget.value() - if self.value_range is not None: - imin, imax = widget.minimum(), widget.maximum() - vmin, vmax = self.value_range - val = (val - imin) / (imax - imin) * (vmax - vmin) + vmin - if self.log: - val = 10 ** val - return val - - def setter(self, widget, val): - if self.log: - val = math.log10(val) - if self.value_range is not None: - imin, imax = widget.minimum(), widget.maximum() - vmin, vmax = self.value_range - val = (val - vmin) / (vmax - vmin) * (imax - imin) + imin - widget.setValue(int(val)) +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.utils.qt.widget_properties is deprecated, use glue_qt.utils.widget_properties instead', GlueDeprecationWarning) +from glue_qt.utils.widget_properties import * # noqa diff --git a/glue/utils/tests/test_matplotlib.py b/glue/utils/tests/test_matplotlib.py index 2631d969a..c2e1d2ef8 100644 --- a/glue/utils/tests/test_matplotlib.py +++ b/glue/utils/tests/test_matplotlib.py @@ -7,6 +7,7 @@ from matplotlib.artist import Artist from numpy.testing import assert_allclose from matplotlib.backends.backend_agg import FigureCanvasAgg +from matplotlib.backend_bases import ResizeEvent from glue.tests.helpers import requires_scipy, requires_skimage from glue.utils.misc import DeferredMethod @@ -152,7 +153,8 @@ def test_freeze_margins(): # np.testing.assert_allclose(bbox.x1, 0.9) # np.testing.assert_allclose(bbox.y1, 0.9) - fig.canvas.resize_event() + resize_event = ResizeEvent("resize_event", fig.canvas) + fig.canvas.callbacks.process(resize_event.name, resize_event) bbox = ax.get_position() np.testing.assert_allclose(bbox.x0, 0.25) @@ -161,7 +163,8 @@ def test_freeze_margins(): np.testing.assert_allclose(bbox.y1, 0.75) fig.set_size_inches(8, 8) - fig.canvas.resize_event() + resize_event = ResizeEvent("resize_event", fig.canvas) + fig.canvas.callbacks.process(resize_event.name, resize_event) bbox = ax.get_position() np.testing.assert_allclose(bbox.x0, 0.125) @@ -170,7 +173,8 @@ def test_freeze_margins(): np.testing.assert_allclose(bbox.y1, 0.875) ax.resizer.margins = [0, 1, 2, 4] - fig.canvas.resize_event() + resize_event = ResizeEvent("resize_event", fig.canvas) + fig.canvas.callbacks.process(resize_event.name, resize_event) bbox = ax.get_position() np.testing.assert_allclose(bbox.x0, 0.) diff --git a/glue/viewers/common/qt/__init__.py b/glue/viewers/common/qt/__init__.py index 678c40862..63c3cdd0c 100644 --- a/glue/viewers/common/qt/__init__.py +++ b/glue/viewers/common/qt/__init__.py @@ -1 +1,4 @@ -from . import tools # noqa +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.viewers.common.qt is deprecated, use glue_qt.viewers.common instead', GlueDeprecationWarning) +from glue_qt.viewers.common import * # noqa diff --git a/glue/viewers/common/qt/base_widget.py b/glue/viewers/common/qt/base_widget.py index 354c2ad7a..a863189d7 100644 --- a/glue/viewers/common/qt/base_widget.py +++ b/glue/viewers/common/qt/base_widget.py @@ -1,241 +1,4 @@ -import os - -from qtpy.QtCore import Qt -from qtpy import QtCore, QtWidgets -from glue.utils.qt import get_qapp -from glue.core.qt.mime import LAYERS_MIME_TYPE, LAYER_MIME_TYPE - -__all__ = ['BaseQtViewerWidget'] - - -class BaseQtViewerWidget(QtWidgets.QMainWindow): - """ - Base Qt class for all DataViewer widgets. This is not a viewer class in - itself but is the base widget that should be used for any Qt viewer that - is to appear inside the MDI area. - """ - - window_closed = QtCore.Signal() - toolbar_added = QtCore.Signal() - - _closed = False - - def __init__(self, parent=None): - """ - :type session: :class:`~glue.core.session.Session` - """ - - super(BaseQtViewerWidget, self).__init__(parent) - - self.setWindowIcon(get_qapp().windowIcon()) - - status_bar = self.statusBar() - status_bar.setSizeGripEnabled(False) - status_bar.setStyleSheet("QStatusBar{font-size:10px}") - - self.setFocusPolicy(Qt.StrongFocus) - self.setAttribute(Qt.WA_DeleteOnClose) - self.setAcceptDrops(True) - self.setAnimated(False) - self.setContentsMargins(2, 2, 2, 2) - - self._mdi_wrapper = None # GlueMdiSubWindow that self is embedded in - self._warn_close = True - - def dragEnterEvent(self, event): - """ - Accept drag-and-drop of data or subset objects. - """ - if event.mimeData().hasFormat(LAYER_MIME_TYPE): - event.accept() - elif event.mimeData().hasFormat(LAYERS_MIME_TYPE): - event.accept() - else: - event.ignore() - - def dropEvent(self, event): - """ - Accept drag-and-drop of data or subset objects. - """ - if event.mimeData().hasFormat(LAYER_MIME_TYPE): - self.request_add_layer(event.mimeData().data(LAYER_MIME_TYPE)) - - assert event.mimeData().hasFormat(LAYERS_MIME_TYPE) - - for layer in event.mimeData().data(LAYERS_MIME_TYPE): - self.request_add_layer(layer) - - event.accept() - - def mousePressEvent(self, event): - """ - Consume mouse press events, and prevent them from propagating - down to the MDI area. - """ - event.accept() - - def close(self, warn=True): - - if self._closed: - return - - if warn and not self._confirm_close(): - return - - self._warn_close = False - - if getattr(self, '_mdi_wrapper', None) is not None: - self._mdi_wrapper.close() - self._mdi_wrapper = None - else: - try: - QtWidgets.QMainWindow.close(self) - except RuntimeError: - # In some cases the above can raise a "wrapped C/C++ object of - # type ... has been deleted" error, in which case we can just - # ignore and carry on. - pass - - self._closed = True - - def mdi_wrap(self): - """ - Wrap this object in a GlueMdiSubWindow - """ - from glue.app.qt.mdi_area import GlueMdiSubWindow - sub = GlueMdiSubWindow() - sub.setWidget(self) - self.destroyed.connect(sub.close) - sub.resize(self.size()) - self._mdi_wrapper = sub - return sub - - @property - def position(self): - """ - The location of the viewer as a tuple of ``(x, y)`` - """ - target = self._mdi_wrapper or self - pos = target.pos() - return pos.x(), pos.y() - - @position.setter - def position(self, xy): - x, y = xy - self.move(x, y) - - def move(self, x=None, y=None): - """ - Move the viewer to a new XY pixel location - - You can also set the position attribute to a new tuple directly. - - Parameters - ---------- - x : int (optional) - New x position - y : int (optional) - New y position - """ - x0, y0 = self.position - if x is None: - x = x0 - if y is None: - y = y0 - if self._mdi_wrapper is not None: - self._mdi_wrapper.move(x, y) - else: - QtWidgets.QMainWindow.move(self, x, y) - - @property - def viewer_size(self): - """ - Size of the viewer as a tuple of ``(width, height)`` - """ - if self._mdi_wrapper is not None: - sz = self._mdi_wrapper.size() - else: - sz = self.size() - return sz.width(), sz.height() - - @viewer_size.setter - def viewer_size(self, value): - width, height = value - if self._mdi_wrapper is None: - self.resize(width, height) - else: - self._mdi_wrapper.resize(width, height) - - def closeEvent(self, event): - """ - Call unregister on window close - """ - - if self._warn_close and not self._confirm_close(): - event.ignore() - return - - super(BaseQtViewerWidget, self).closeEvent(event) - event.accept() - - self.window_closed.emit() - - def isVisible(self): - # Override this so as to catch RuntimeError: wrapped C/C++ object of - # type ... has been deleted - try: - return self.isVisible() - except RuntimeError: - return False - - def _confirm_close(self): - """Ask for close confirmation - - :rtype: bool. True if user wishes to close. False otherwise - """ - if self._warn_close and not os.environ.get('GLUE_TESTING'): - buttons = QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel - dialog = QtWidgets.QMessageBox.warning(self, "Confirm Close", - "Do you want to close this window?", - buttons=buttons, - defaultButton=QtWidgets.QMessageBox.Cancel) - return dialog == QtWidgets.QMessageBox.Ok - return True - - def layer_view(self): - return QtWidgets.QWidget() - - def options_widget(self): - return QtWidgets.QWidget() - - def set_focus(self, state): - if state: - css = """ - DataViewer - { - border: 2px solid; - border-color: rgb(56, 117, 215); - } - """ - else: - css = """ - DataViewer - { - border: none; - } - """ - self.setStyleSheet(css) - - @property - def window_title(self): - return str(self) - - def update_window_title(self): - try: - self.setWindowTitle(self.window_title) - except RuntimeError: # Avoid C/C++ errors when closing viewer - pass - - def set_status(self, message): - sb = self.statusBar() - sb.showMessage(message) +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.viewers.common.qt.base_widget is deprecated, use glue_qt.viewers.common.base_widget instead', GlueDeprecationWarning) +from glue_qt.viewers.common.base_widget import * # noqa diff --git a/glue/viewers/common/qt/contrastlimits.ui b/glue/viewers/common/qt/contrastlimits.ui deleted file mode 100644 index 3a26c09f0..000000000 --- a/glue/viewers/common/qt/contrastlimits.ui +++ /dev/null @@ -1,169 +0,0 @@ - - - min_max - - - Qt::WindowModal - - - - 0 - 0 - 250 - 150 - - - - - 0 - 0 - - - - Dialog - - - false - - - false - - - - 3 - - - - - - - 6 - - - - - - 0 - 35 - - - - Choose Intensity Limits - - - Qt::AlignHCenter|Qt::AlignTop - - - - - - - - - - 50 - 0 - - - - Low - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Data value to set to black - - - 100 - - - - - - - - - - - - 50 - 0 - - - - High - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Data value to set to white - - - 100 - - - - - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - - - - - - buttonBox - accepted() - min_max - accept() - - - 248 - 254 - - - 157 - 274 - - - - - buttonBox - rejected() - min_max - reject() - - - 316 - 260 - - - 286 - 274 - - - - - diff --git a/glue/viewers/common/qt/data_slice_widget.py b/glue/viewers/common/qt/data_slice_widget.py index add7f3999..edad718b0 100644 --- a/glue/viewers/common/qt/data_slice_widget.py +++ b/glue/viewers/common/qt/data_slice_widget.py @@ -1,212 +1,4 @@ -import os - -import numpy as np - -from qtpy import QtCore, QtWidgets -from glue.utils.qt import load_ui -from glue.utils import nonpartial, format_minimal -from glue.icons.qt import get_icon -from glue.core.state_objects import State, CallbackProperty -from echo.qt import autoconnect_callbacks_to_qt - - -class SliceState(State): - - label = CallbackProperty() - slider_label = CallbackProperty() - slider_unit = CallbackProperty() - slice_center = CallbackProperty() - use_world = CallbackProperty() - - -class SliceWidget(QtWidgets.QWidget): - - slice_changed = QtCore.Signal(int) - - def __init__(self, label='', world=None, lo=0, hi=10, - parent=None, world_unit=None, - world_warning=False): - - super(SliceWidget, self).__init__(parent) - - self.state = SliceState() - self.state.label = label - self.state.slice_center = (lo + hi) // 2 - - self._world = np.asarray(world) - self._world_warning = world_warning - self._world_unit = world_unit - - self.ui = load_ui('data_slice_widget.ui', self, - directory=os.path.dirname(__file__)) - - self._connections = autoconnect_callbacks_to_qt(self.state, self.ui) - - font = self.text_warning.font() - font.setPointSize(int(font.pointSize() * 0.75)) - self.text_warning.setFont(font) - - self.button_first.setStyleSheet('border: 0px') - self.button_first.setIcon(get_icon('playback_first')) - self.button_prev.setStyleSheet('border: 0px') - self.button_prev.setIcon(get_icon('playback_prev')) - self.button_back.setStyleSheet('border: 0px') - self.button_back.setIcon(get_icon('playback_back')) - self.button_stop.setStyleSheet('border: 0px') - self.button_stop.setIcon(get_icon('playback_stop')) - self.button_forw.setStyleSheet('border: 0px') - self.button_forw.setIcon(get_icon('playback_forw')) - self.button_next.setStyleSheet('border: 0px') - self.button_next.setIcon(get_icon('playback_next')) - self.button_last.setStyleSheet('border: 0px') - self.button_last.setIcon(get_icon('playback_last')) - - self.value_slice_center.setMinimum(lo) - self.value_slice_center.setMaximum(hi) - self.value_slice_center.valueChanged.connect(nonpartial(self.set_label_from_slider)) - - # Figure out the optimal format to use to show the world values. We do - # this by figuring out the precision needed so that when converted to - # a string, every string value is different. - - if world is not None and len(world) > 1: - self.label_fmt = format_minimal(world)[0] - else: - self.label_fmt = "{:g}" - - self.text_slider_label.setMinimumWidth(80) - self.state.slider_label = self.label_fmt.format(self.value_slice_center.value()) - self.text_slider_label.editingFinished.connect(nonpartial(self.set_slider_from_label)) - - self._play_timer = QtCore.QTimer() - self._play_timer.setInterval(500) - self._play_timer.timeout.connect(nonpartial(self._play_slice)) - - self.button_first.clicked.connect(nonpartial(self._browse_slice, 'first')) - self.button_prev.clicked.connect(nonpartial(self._browse_slice, 'prev')) - self.button_back.clicked.connect(nonpartial(self._adjust_play, 'back')) - self.button_stop.clicked.connect(nonpartial(self._adjust_play, 'stop')) - self.button_forw.clicked.connect(nonpartial(self._adjust_play, 'forw')) - self.button_next.clicked.connect(nonpartial(self._browse_slice, 'next')) - self.button_last.clicked.connect(nonpartial(self._browse_slice, 'last')) - - self.bool_use_world.toggled.connect(nonpartial(self.set_label_from_slider)) - - if world is None: - self.state.use_world = False - self.bool_use_world.hide() - else: - self.state.use_world = not world_warning - - if world_unit: - self.state.slider_unit = world_unit - else: - self.state.slider_unit = '' - - self._play_speed = 0 - - self.set_label_from_slider() - - def set_label_from_slider(self): - value = self.state.slice_center - if self.state.use_world: - value = self._world[value] - if self._world_warning: - self.text_warning.show() - else: - self.text_warning.hide() - self.state.slider_unit = self._world_unit - self.state.slider_label = self.label_fmt.format(value) - else: - self.text_warning.hide() - self.state.slider_unit = '' - self.state.slider_label = str(value) - - def set_slider_from_label(self): - - # Ignore recursive calls - we do this rather than ignore_callback - # below when setting slider_label, otherwise we might be stopping other - # subscribers to that event from being correctly updated - if getattr(self, '_in_set_slider_from_label', False): - return - else: - self._in_set_slider_from_label = True - - text = self.text_slider_label.text() - if self.state.use_world: - # Don't want to assume world is sorted, pick closest value - value = np.argmin(np.abs(self._world - float(text))) - self.state.slider_label = self.label_fmt.format(self._world[value]) - else: - value = int(text) - self.value_slice_center.setValue(value) - - self._in_set_slider_from_label = False - - def _adjust_play(self, action): - if action == 'stop': - self._play_speed = 0 - elif action == 'back': - if self._play_speed > 0: - self._play_speed = -1 - else: - self._play_speed -= 1 - elif action == 'forw': - if self._play_speed < 0: - self._play_speed = +1 - else: - self._play_speed += 1 - if self._play_speed == 0: - self._play_timer.stop() - else: - self._play_timer.start() - self._play_timer.setInterval(500 / abs(self._play_speed)) - - def _play_slice(self): - if self._play_speed > 0: - self._browse_slice('next', play=True) - elif self._play_speed < 0: - self._browse_slice('prev', play=True) - - def _browse_slice(self, action, play=False): - - imin = self.value_slice_center.minimum() - imax = self.value_slice_center.maximum() - value = self.value_slice_center.value() - - # If this was not called from _play_slice, we should stop the - # animation. - if not play: - self._adjust_play('stop') - - if action == 'first': - value = imin - elif action == 'last': - value = imax - elif action == 'prev': - value = value - 1 - if value < imin: - value = imax - elif action == 'next': - value = value + 1 - if value > imax: - value = imin - else: - raise ValueError("Action should be one of first/prev/next/last") - - self.value_slice_center.setValue(value) - - -if __name__ == "__main__": - - from glue.utils.qt import get_qapp - - app = get_qapp() - - widget = SliceWidget(label='BANANA') - widget.show() - - widget = SliceWidget(world=[1, 2, 3, 4, 5, 6, 7], lo=1, hi=7) - widget.show() - - app.exec_() +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.viewers.common.qt.data_slice_widget is deprecated, use glue_qt.viewers.common.data_slice_widget instead', GlueDeprecationWarning) +from glue_qt.viewers.common.data_slice_widget import * # noqa diff --git a/glue/viewers/common/qt/data_slice_widget.ui b/glue/viewers/common/qt/data_slice_widget.ui deleted file mode 100644 index c920a6e5a..000000000 --- a/glue/viewers/common/qt/data_slice_widget.ui +++ /dev/null @@ -1,268 +0,0 @@ - - - Form - - - - 0 - 0 - 291 - 90 - - - - Form - - - - 0 - - - 0 - - - - - - - - 75 - true - - - - Main label - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Show real coordinates - - - true - - - false - - - - - - - - - color: rgb(255, 33, 28) - - - Warning: real coordinates are not aligned with pixel grid. The coordinate shown above is the value at the center of the slice. - - - Qt::AlignCenter - - - true - - - - - - - 10 - - - - - Qt::Horizontal - - - - - - - - - 2 - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Go to first slice - - - << - - - - 16 - 16 - - - - - - - - Go to previous slice - - - < - - - - 16 - 16 - - - - - - - - Play backwards (click multiple times to speed up) - - - < - - - - 16 - 16 - - - - - - - - Stop the playback - - - â–ª - - - - 16 - 16 - - - - - - - - Play forwards (click multiple times to speed up) - - - > - - - - 16 - 16 - - - - - - - - Go to next slice - - - > - - - - 16 - 16 - - - - true - - - - - - - Go to last slice - - - >> - - - - 16 - 16 - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 50 - 16777215 - - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - TextLabel - - - - - - - - - - diff --git a/glue/viewers/common/qt/data_viewer.py b/glue/viewers/common/qt/data_viewer.py index 001a4ebbb..8e9076d65 100644 --- a/glue/viewers/common/qt/data_viewer.py +++ b/glue/viewers/common/qt/data_viewer.py @@ -1,239 +1,4 @@ -from qtpy.QtCore import Qt -from qtpy import QtWidgets -from echo import add_callback -from glue.core.qt.layer_artist_model import QtLayerArtistContainer, LayerArtistWidget -from glue.utils.qt import set_cursor, messagebox_on_error -from glue.core.qt.dialogs import warn -from glue.utils.noconflict import classmaker -from glue.config import viewer_tool -from glue.viewers.common.qt.base_widget import BaseQtViewerWidget -from glue.viewers.common.tool import SimpleToolMenu -from glue.viewers.common.qt.toolbar import BasicToolbar -from glue.viewers.common.viewer import Viewer -from glue.viewers.common.utils import get_viewer_tools - -__all__ = ['DataViewer', 'get_viewer_tools'] - - -class ToolbarInitializer(object): - """ - This is a meta-class which ensures that initialize_toolbar is always called - on DataViewer instances and sub-class instances after all the __init__ code - has been executed. We need to do this, because often the toolbar can only - be initialized after everything else (e.g. canvas, etc.) has been set up, - so we can't do it in DataViewer.__init__. - """ - - def __call__(cls, *args, **kwargs): - obj = type.__call__(cls, *args, **kwargs) - obj.initialize_toolbar() - return obj - - -@viewer_tool -class SaveTool(SimpleToolMenu): - """ - A generic 'save/export' tool that plugins can register new save/export tools - with. - - To register a new save/export option, add an entry to the viewer - ``subtools['save']`` list. - """ - tool_id = 'save' - icon = 'glue_filesave' - tool_tip = 'Save/export the plot' - - -# Note: we need to use classmaker here because otherwise we run into issues when -# trying to use the meta-class with the Qt class. -class DataViewer(Viewer, BaseQtViewerWidget, - metaclass=classmaker(left_metas=(ToolbarInitializer,))): - """ - Base class for all Qt DataViewer widgets. - - This defines a minimal interface, and implements the following:: - - * An automatic call to unregister on window close - * Drag and drop support for adding data - """ - - _layer_artist_container_cls = QtLayerArtistContainer - _layer_style_widget_cls = None - - _toolbar_cls = BasicToolbar - - # This defines the mouse mode to be used when no toolbar modes are active - _default_mouse_mode_cls = None - - inherit_tools = True - tools = ['save', 'window'] - subtools = { - 'save': [], - 'window': ['window:movetab', 'window:title'] - } - - _close_on_last_layer_removed = True - - _options_cls = None - - large_data_size = None - - def __init__(self, session, state=None, parent=None): - """ - :type session: :class:`~glue.core.session.Session` - """ - - BaseQtViewerWidget.__init__(self, parent) - Viewer.__init__(self, session, state=state) - - self._view = LayerArtistWidget(layer_style_widget_cls=self._layer_style_widget_cls, - hub=session.hub) - self._view.layer_list.setModel(self._layer_artist_container.model) - - # Set up the options widget, which will include options that control the - # viewer state - if self._options_cls is None: - self.options = QtWidgets.QWidget() - else: - self.options = self._options_cls(viewer_state=self.state, - session=session) - - self._tb_vis = {} # store whether toolbars are enabled - self.toolbar = None - self._toolbars = [] - self._warn_close = True - - # close window when last plot layer deleted - if self._close_on_last_layer_removed: - self._layer_artist_container.on_empty(self._close_nowarn) - self._layer_artist_container.on_changed(self.update_window_title) - - add_callback(self.state, 'title', self._on_title_change) - - self.update_window_title() - - @property - def selected_layer(self): - return self._view.layer_list.current_artist() - - @set_cursor(Qt.WaitCursor) - def apply_roi(self, roi): - pass - - def warn(self, message, *args, **kwargs): - return warn(message, *args, **kwargs) - - def _close_nowarn(self): - return self.close(warn=False) - - def closeEvent(self, event): - super(DataViewer, self).closeEvent(event) - Viewer.cleanup(self) - # We tell the toolbar to do cleanup to make sure we get rid of any - # circular references - if self.toolbar: - self.toolbar.cleanup() - - def _on_title_change(self, title): - self.update_window_title() - - def layer_view(self): - return self._view - - def addToolBar(self, tb): - super(DataViewer, self).addToolBar(tb) - self._toolbars.append(tb) - self._tb_vis[tb] = True - - def remove_toolbar(self, tb): - self._toolbars.remove(tb) - self._tb_vis.pop(tb, None) - super(DataViewer, self).removeToolBar(tb) - - def remove_all_toolbars(self): - for tb in reversed(self._toolbars): - self.remove_toolbar(tb) - - def initialize_toolbar(self): - - from glue.config import viewer_tool - - self.toolbar = self._toolbar_cls(self, default_mouse_mode_cls=self._default_mouse_mode_cls) - - # Need to include tools and subtools declared by parent classes unless - # specified otherwise - tool_ids, subtool_ids = get_viewer_tools(self.__class__) - - for tool_id in tool_ids: - mode_cls = viewer_tool.members[tool_id] - if tool_id in subtool_ids: - subtools = [] - for subtool_id in subtool_ids[tool_id]: - subtools.append(viewer_tool.members[subtool_id](self)) - mode = mode_cls(self, subtools=subtools) - else: - mode = mode_cls(self) - self.toolbar.add_tool(mode) - - self.addToolBar(self.toolbar) - - self.toolbar_added.emit() - - def show_toolbars(self): - """ - Re-enable any toolbars that were hidden with `hide_toolbars()` - - Does not re-enable toolbars that were hidden by other means - """ - for tb in self._toolbars: - if self._tb_vis.get(tb, False): - tb.setEnabled(True) - - def hide_toolbars(self): - """ - Disable all the toolbars in the viewer. - - This action can be reversed by calling `show_toolbars()` - """ - for tb in self._toolbars: - self._tb_vis[tb] = self._tb_vis.get(tb, False) or tb.isVisible() - tb.setEnabled(False) - - def set_focus(self, state): - super(DataViewer, self).set_focus(state) - if state: - self.show_toolbars() - else: - self.hide_toolbars() - - def __gluestate__(self, context): - state = Viewer.__gluestate__(self, context) - state['size'] = self.viewer_size - state['pos'] = self.position - state['_protocol'] = 1 - return state - - def update_viewer_state(rec, context): - pass - - @classmethod - def __setgluestate__(cls, rec, context): - - if rec.get('_protocol', 0) < 1: - cls.update_viewer_state(rec, context) - - viewer = super(DataViewer, cls).__setgluestate__(rec, context) - - viewer.viewer_size = rec['size'] - x, y = rec['pos'] - viewer.move(x=x, y=y) - - return viewer - - @messagebox_on_error("Failed to add data") - def add_data(self, data): - return super(DataViewer, self).add_data(data) - - @messagebox_on_error("Failed to add subset") - def add_subset(self, subset): - return super(DataViewer, self).add_subset(subset) +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.viewers.common.qt.data_viewer is deprecated, use glue_qt.viewers.common.data_viewer instead', GlueDeprecationWarning) +from glue_qt.viewers.common.data_viewer import * # noqa diff --git a/glue/viewers/common/qt/data_viewer_with_state.py b/glue/viewers/common/qt/data_viewer_with_state.py index 4d02c0aa5..1e56224d8 100644 --- a/glue/viewers/common/qt/data_viewer_with_state.py +++ b/glue/viewers/common/qt/data_viewer_with_state.py @@ -1,7 +1,4 @@ -from glue.viewers.common.qt.data_viewer import DataViewer - -__all__ = ['DataViewerWithState'] - - -class DataViewerWithState(DataViewer): - pass +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.viewers.common.qt.data_viewer_with_state is deprecated, use glue_qt.viewers.common.data_viewer_with_state instead', GlueDeprecationWarning) +from glue_qt.viewers.common.data_viewer_with_state import * # noqa diff --git a/glue/viewers/common/qt/tests/__init__.py b/glue/viewers/common/qt/tests/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/glue/viewers/common/qt/tests/test_data_slice_widget.py b/glue/viewers/common/qt/tests/test_data_slice_widget.py deleted file mode 100644 index cf5007ee3..000000000 --- a/glue/viewers/common/qt/tests/test_data_slice_widget.py +++ /dev/null @@ -1,64 +0,0 @@ -from ..data_slice_widget import SliceWidget - - -class TestSliceWidget(object): - - def test_slice_center(self): - s = SliceWidget(lo=0, hi=10) - assert s.state.slice_center == 5 - - def test_browse_slice(self): - s = SliceWidget(lo=0, hi=10) - assert s.state.slice_center == 5 - s.button_prev.click() - assert s.state.slice_center == 4 - s.button_next.click() - s.button_next.click() - assert s.state.slice_center == 6 - s.button_first.click() - assert s.state.slice_center == 0 - s.button_prev.click() - assert s.state.slice_center == 10 - s.button_next.click() - assert s.state.slice_center == 0 - s.button_last.click() - assert s.state.slice_center == 10 - s.button_next.click() - assert s.state.slice_center == 0 - s.button_prev.click() - assert s.state.slice_center == 10 - s.button_prev.click() - assert s.state.slice_center == 9 - - def test_slice_world(self): - - s = SliceWidget(lo=0, hi=5, world=[1, 3, 5, 5.5, 8, 12]) - - # Check switching between world and pixel coordinates - s.state.slice_center = 0 - assert s.state.slider_label == '1.0' - s.state.use_world = False - assert s.state.slider_label == '0' - s.state.slice_center = 3 - assert s.state.slider_label == '3' - s.state.use_world = True - assert s.state.slider_label == '5.5' - - # Round to nearest - s.state.slider_label = '11' - assert s.state.slice_center == 5 - assert s.state.slider_label == '12.0' - - # Make sure out of bound values work - s.state.slider_label = '20' - assert s.state.slice_center == 5 - assert s.state.slider_label == '12.0' - s.state.slider_label = '-10' - assert s.state.slice_center == 0 - assert s.state.slider_label == '1.0' - - # And disable world and try and set by pixel - s.state.use_world = False - s.state.slider_label = '4' - assert s.state.slice_center == 4 - assert s.state.slider_label == '4' diff --git a/glue/viewers/common/qt/tests/test_data_viewer.py b/glue/viewers/common/qt/tests/test_data_viewer.py deleted file mode 100644 index 73fbefb73..000000000 --- a/glue/viewers/common/qt/tests/test_data_viewer.py +++ /dev/null @@ -1,134 +0,0 @@ -# pylint: disable=I0011,W0613,W0201,W0212,E1101,E1103 - -import numpy as np -from unittest.mock import MagicMock, patch - -from glue.core import Data, DataCollection -from glue.app.qt import GlueApplication - -from glue.core.tests.util import simple_session -from glue.viewers.histogram.qt import HistogramViewer -from glue.viewers.image.qt import ImageViewer -from glue.viewers.scatter.qt import ScatterViewer -from glue.utils.qt import process_events - - -# TODO: We should maybe consider running these tests for all -# registered Qt viewers. - - -class BaseTestDataViewer(object): - - ndim = 1 - - def test_unregister_on_close(self): - session = simple_session() - hub = session.hub - - w = self.widget_cls(session) - w.register_to_hub(hub) - with patch.object(w, 'unregister') as unregister: - w.close() - unregister.assert_called_once_with(hub) - - def test_single_draw_call_on_create(self): - d = Data(x=np.random.random((2,) * self.ndim)) - dc = DataCollection([d]) - app = GlueApplication(dc) - - try: - from glue.viewers.matplotlib.qt.widget import MplCanvas - draw = MplCanvas.draw - MplCanvas.draw = MagicMock() - - app.new_data_viewer(self.widget_cls, data=d) - - # each Canvas instance gives at most 1 draw call - selfs = [c[0][0] for c in MplCanvas.draw.call_arg_list] - assert len(set(selfs)) == len(selfs) - finally: - MplCanvas.draw = draw - app.close() - - def test_close_on_last_layer_remove(self): - - # regression test for 391 - - d1 = Data(x=np.random.random((2,) * self.ndim)) - d2 = Data(y=np.random.random((2,) * self.ndim)) - dc = DataCollection([d1, d2]) - app = GlueApplication(dc) - w = app.new_data_viewer(self.widget_cls, data=d1) - w.add_data(d2) - process_events() - assert len(app.viewers[0]) == 1 - dc.remove(d1) - process_events() - assert len(app.viewers[0]) == 1 - dc.remove(d2) - process_events() - assert len(app.viewers[0]) == 0 - app.close() - - def test_viewer_size(self, tmpdir): - - # regression test for #781 - # viewers were not restored with the right size - - d1 = Data(x=np.random.random((2,) * self.ndim)) - d2 = Data(x=np.random.random((2,) * self.ndim)) - dc = DataCollection([d1, d2]) - app = GlueApplication(dc) - w = app.new_data_viewer(self.widget_cls, data=d1) - w.viewer_size = (300, 400) - - filename = tmpdir.join('session.glu').strpath - app.save_session(filename, include_data=True) - - app2 = GlueApplication.restore_session(filename) - - for viewer in app2.viewers: - assert viewer[0].viewer_size == (300, 400) - - app.close() - app2.close() - - def test_correct_window_title(self): - - # check that the viewer is displaying the correct title - - d = Data(x=np.random.random((2,) * self.ndim)) - dc = DataCollection([d]) - app = GlueApplication(dc) - w = app.new_data_viewer(self.widget_cls, data=d) - w.state.title = "My Viewer" - assert w.parent().windowTitle() == "My Viewer" - - def test_viewer_title_tool(self): - - # check that the viewer title tool correctly updates the title - - d = Data(x=np.random.random((2,) * self.ndim)) - dc = DataCollection([d]) - app = GlueApplication(dc) - w = app.new_data_viewer(self.widget_cls, data=d) - - tool = w.toolbar.tools["window"].subtools[1] - with patch('glue.viewers.common.qt.tools.get_text') as gt: - gt.return_value = "My Viewer" - tool.activate() - assert w.state.title == "My Viewer" - assert w.parent().windowTitle() == "My Viewer" - - -class TestDataViewerScatter(BaseTestDataViewer): - widget_cls = ScatterViewer - - -class TestDataViewerImage(BaseTestDataViewer): - ndim = 2 - widget_cls = ImageViewer - - -class TestDataViewerHistogram(BaseTestDataViewer): - widget_cls = HistogramViewer diff --git a/glue/viewers/common/qt/tests/test_toolbar.py b/glue/viewers/common/qt/tests/test_toolbar.py deleted file mode 100644 index b804a1eb6..000000000 --- a/glue/viewers/common/qt/tests/test_toolbar.py +++ /dev/null @@ -1,43 +0,0 @@ -# pylint: disable=I0011,W0613,W0201,W0212,E1101,E1103 - -import pytest -from glue.config import viewer_tool -from glue.viewers.common.qt.data_viewer import DataViewer -from glue.viewers.common.tool import Tool -from glue.viewers.common.qt.toolbar import BasicToolbar -from glue.core.tests.util import simple_session - - -@viewer_tool -class ExampleTool1(Tool): - - tool_id = 'TEST1' - tool_tip = 'tes1' - icon = 'glue_square' - shortcut = 'A' - - -@viewer_tool -class ExampleTool2(Tool): - - tool_id = 'TEST2' - tool_tip = 'tes2' - icon = 'glue_square' - shortcut = 'A' - - -class ExampleViewer2(DataViewer): - - _toolbar_cls = BasicToolbar - tools = ['TEST1', 'TEST2'] - - def __init__(self, session, parent=None): - super(ExampleViewer2, self).__init__(session, parent=parent) - - -def test_duplicate_shortcut(): - session = simple_session() - expected_warning = ("Tools 'TEST1' and 'TEST2' have the same " - r"shortcut \('A'\). Ignoring shortcut for 'TEST2'") - with pytest.warns(UserWarning, match=expected_warning): - ExampleViewer2(session) diff --git a/glue/viewers/common/qt/tool.py b/glue/viewers/common/qt/tool.py index cd42f2b36..00e222683 100644 --- a/glue/viewers/common/qt/tool.py +++ b/glue/viewers/common/qt/tool.py @@ -1,6 +1,4 @@ import warnings - -from glue.viewers.common.tool import * # noqa - -warnings.warn('glue.viewers.common.qt.tool is deprecated, use ' - 'glue.viewers.common.tool instead', UserWarning) +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.viewers.common.qt.tool is deprecated, use glue_qt.viewers.common.tool instead', GlueDeprecationWarning) +from glue_qt.viewers.common.tool import * # noqa diff --git a/glue/viewers/common/qt/toolbar.py b/glue/viewers/common/qt/toolbar.py index e7c51b124..a8024f693 100644 --- a/glue/viewers/common/qt/toolbar.py +++ b/glue/viewers/common/qt/toolbar.py @@ -1,216 +1,4 @@ -import os import warnings - -from qtpy import QtCore, QtGui, QtWidgets -from qtpy.QtCore import Qt - -from glue.core.callback_property import add_callback -from glue.viewers.common.tool import CheckableTool, DropdownTool -from glue.icons.qt import get_icon - -__all__ = ['BasicToolbar'] - - -class BasicToolbar(QtWidgets.QToolBar): - - tool_activated = QtCore.Signal() - tool_deactivated = QtCore.Signal() - - def __init__(self, parent, default_mouse_mode_cls=None): - """ - Create a new toolbar object - """ - - super(BasicToolbar, self).__init__(parent=parent) - - self.actions = {} - self.tools = {} - self.setIconSize(QtCore.QSize(25, 25)) - self.layout().setSpacing(1) - self.setFocusPolicy(Qt.StrongFocus) - self._active_tool = None - self._default_mouse_mode_cls = default_mouse_mode_cls - self._default_mouse_mode = None - self.setup_default_modes() - - def setup_default_modes(self): - if self._default_mouse_mode_cls is not None: - self._default_mouse_mode = self._default_mouse_mode_cls(self.parent()) - self._default_mouse_mode.activate() - - @property - def active_tool(self): - return self._active_tool - - @active_tool.setter - def active_tool(self, new_tool): - - if isinstance(new_tool, str): - if new_tool in self.tools: - new_tool = self.tools[new_tool] - else: - raise ValueError("Unrecognized tool '{0}', should be one of {1}" - .format(new_tool, ", ".join(sorted(self.tools)))) - - old_tool = self._active_tool - - # If the tool is as before, we don't need to do anything - if old_tool is new_tool: - return - - # Otheriwse, if the tool changes, then we need to disable the previous - # tool... - if old_tool is not None: - self.deactivate_tool(old_tool) - if isinstance(old_tool, CheckableTool): - button = self.actions[old_tool.tool_id] - if button.isChecked(): - button.blockSignals(True) - button.setChecked(False) - button.blockSignals(False) - - # We need to then set that no tool is set so that if the next tool - # opens a viewer that needs to check whether a tool is active, we - # know that it isn't. - self._active_tool = None - - # ... and enable the new one - if new_tool is not None: - self.activate_tool(new_tool) - if isinstance(new_tool, CheckableTool): - button = self.actions[new_tool.tool_id] - if not button.isChecked(): - button.blockSignals(True) - button.setChecked(True) - button.blockSignals(False) - - if isinstance(new_tool, CheckableTool): - self._active_tool = new_tool - self.parent().set_status(new_tool.status_tip) - self.tool_activated.emit() - else: - self.parent().set_status('') - self.tool_deactivated.emit() - - def activate_tool(self, tool): - if isinstance(tool, CheckableTool) and self._default_mouse_mode is not None: - self._default_mouse_mode.deactivate() - tool.activate() - - def deactivate_tool(self, tool): - if isinstance(tool, CheckableTool): - tool.deactivate() - if self._default_mouse_mode is not None: - self._default_mouse_mode.activate() - - def _make_action(self, tool, menu=None): - - parent = QtWidgets.QToolBar.parent(self) - - if isinstance(tool.icon, str): - if os.path.exists(tool.icon): - icon = QtGui.QIcon(tool.icon) - else: - icon = get_icon(tool.icon) - else: - icon = tool.icon - - if isinstance(tool, DropdownTool): - # We use a QToolButton here explicitly so that we can make sure - # that the whole button opens the pop-up. - button = QtWidgets.QToolButton() - if tool.action_text: - button.setText(tool.action_text) - if icon: - button.setIcon(icon) - button.setPopupMode(button.InstantPopup) - button.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) - action = self.addWidget(button) - if menu: - button.setMenu(menu) - button.clicked.connect(button.showMenu) - else: - if icon: - action = QtWidgets.QAction(icon, tool.action_text, parent) - else: - action = QtWidgets.QAction(tool.action_text, parent) - - def toggle(checked): - if checked: - self.active_tool = tool - else: - self.active_tool = None - - def trigger(): - self.active_tool = tool - - if isinstance(tool, CheckableTool): - action.toggled.connect(toggle) - else: - action.triggered.connect(trigger) - - shortcut = None - - if tool.shortcut is not None: - - # Make sure that the keyboard shortcut is unique - for m in self.tools.values(): - if tool.shortcut == m.shortcut: - warnings.warn("Tools '{0}' and '{1}' have the same shortcut " - "('{2}'). Ignoring shortcut for " - "'{1}'".format(m.tool_id, tool.tool_id, tool.shortcut)) - break - else: - shortcut = tool.shortcut - action.setShortcut(tool.shortcut) - action.setShortcutContext(Qt.WidgetShortcut) - - if shortcut is None: - action.setToolTip(tool.tool_tip) - else: - action.setToolTip(tool.tool_tip + " [shortcut: {0}]".format(shortcut)) - - action.setCheckable(isinstance(tool, CheckableTool)) - - return action - - def add_tool(self, tool): - - if isinstance(tool, DropdownTool) and len(tool.subtools) > 0: - menu = QtWidgets.QMenu(self) - for t in tool.subtools: - action = self._make_action(t) - menu.addAction(action) - elif len(tool.menu_actions()) > 0: - menu = QtWidgets.QMenu(self) - for ma in tool.menu_actions(): - ma.setParent(self) - menu.addAction(ma) - else: - menu = None - - action = self._make_action(tool, menu=menu) - - self.addAction(action) - - self.actions[tool.tool_id] = action - - # Bind tool visibility to tool.enabled - def toggle(state): - action.setVisible(state) - action.setEnabled(state) - - add_callback(tool, 'enabled', toggle) - - self.tools[tool.tool_id] = tool - - return action - - def cleanup(self): - # We need to make sure we set _default_mouse_mode to None otherwise - # we keep a reference to the viewer (parent) inside the mouse mode, - # creating a circular reference. - if self._default_mouse_mode is not None: - self._default_mouse_mode.deactivate() - self._default_mouse_mode = None - self.active_tool = None +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.viewers.common.qt.toolbar is deprecated, use glue_qt.viewers.common.toolbar instead', GlueDeprecationWarning) +from glue_qt.viewers.common.toolbar import * # noqa diff --git a/glue/viewers/common/qt/tools.py b/glue/viewers/common/qt/tools.py index 3408f0457..fcfdba44e 100644 --- a/glue/viewers/common/qt/tools.py +++ b/glue/viewers/common/qt/tools.py @@ -1,47 +1,4 @@ -from glue.config import viewer_tool -from glue.utils.qt import get_text, pick_item -from glue.viewers.common.tool import Tool, SimpleToolMenu - -__all__ = ['MoveTabTool', 'WindowTool'] - - -@viewer_tool -class WindowTool(SimpleToolMenu): - """ - A generic "window operations" tool that the Qt app and plugins - can register tools for windowing operations with. - """ - - tool_id = 'window' - icon = 'windows' - tool_tip = 'Modify the viewer window' - - -@viewer_tool -class MoveTabTool(Tool): - - icon = 'window_tab' - tool_id = 'window:movetab' - action_text = 'Move to another tab' - tool_tip = 'Move viewer to another tab' - - def activate(self): - app = self.viewer.session.application - default = 1 if (app.tab_count > 1 and app.current_tab == app.tab(0)) else 0 - tab = pick_item(range(app.tab_count), app.tab_names, title="Move Viewer", label="Select a tab", default=default) - if tab is not None: - app.move_viewer_to_tab(self.viewer, tab) - - -@viewer_tool -class ChangeTitleTool(Tool): - - icon = 'window_title' - tool_id = 'window:title' - action_text = 'Change viewer title' - tool_tip = 'Change the viewer title' - - def activate(self): - title = get_text(title="Enter a new title") - if title: - self.viewer.state.title = title +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.viewers.common.qt.tools is deprecated, use glue_qt.viewers.common.tools instead', GlueDeprecationWarning) +from glue_qt.viewers.common.tools import * # noqa diff --git a/glue/viewers/custom/helper.py b/glue/viewers/custom/helper.py index f6e802280..ec089fd78 100644 --- a/glue/viewers/custom/helper.py +++ b/glue/viewers/custom/helper.py @@ -71,11 +71,9 @@ def make_selector_func(roi): but uses the keys as dropdown labels and values as the setting passed to viewer functions. * ``keyword='att(foo)'`` doesn't create any widget, but passes - in the attribute named ``foo`` to the viewer functions, as an - :class:`~glue.viewers.custom.qt.custom_viewer.AttributeWithInfo` object. + in the attribute named ``foo`` to the viewer functions. * ``keyword='att'`` creates a dropdown to let the user select - one of the attributes from the data. The selected attribute - is passed as an :class:`~glue.viewers.custom.qt.custom_viewer.AttributeWithInfo` + one of the attributes from the data. **Viewer Functions** @@ -96,5 +94,5 @@ def make_selector_func(roi): """ # For now we only support Qt, but this function should be extended to work # with non-Qt front-ends in future. - from glue.viewers.custom.qt import CustomViewer + from glue_qt.viewers.custom import CustomViewer return CustomViewer.create_new_subclass(name, **kwargs) diff --git a/glue/viewers/custom/qt/__init__.py b/glue/viewers/custom/qt/__init__.py index ec4ec3675..b3bf0b8ed 100644 --- a/glue/viewers/custom/qt/__init__.py +++ b/glue/viewers/custom/qt/__init__.py @@ -1 +1,4 @@ -from .custom_viewer import * # noqa +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.viewers.custom.qt is deprecated, use glue_qt.viewers.custom instead', GlueDeprecationWarning) +from glue_qt.viewers.custom import * # noqa diff --git a/glue/viewers/custom/qt/custom_viewer.py b/glue/viewers/custom/qt/custom_viewer.py index 1910bfba9..07313bde2 100644 --- a/glue/viewers/custom/qt/custom_viewer.py +++ b/glue/viewers/custom/qt/custom_viewer.py @@ -1,769 +1,4 @@ -""" -This module provides utilities for creating custom data viewers. The goal of -this module is to make it easy for users to make new data viewers by focusing on -matplotlib visualization logic, and not UI or event processing logic. - -The end user typically interacts with this code via :func:`glue.custom_viewer` -""" - -# Implementation notes: -# -# Here's a high-level summary of how this code works right now: -# -# The user creates a custom viewer using either of the following -# syntaxes: -# -# -# from glue import custom_viewer -# my_viewer = custom_viewer('my viewer', checked=True, x='att', ...) -# @my_viewer.plot_data -# def plot_data(x, checked, axes): -# if checked: -# axes.plot(x) -# ... -# -# or -# -# from glue.viewers.custom.qt import CustomViewer -# class MyViewer(CustomViewer): -# -# checked = True -# x = 'att' -# -# def plot_data(self, x, checked, axes): -# if checked: -# axes.plot(x) -# -# This code has two "magic" features: -# -# 1. Attributes like 'checked' and 'x', passed as kwargs to custom_viewer -# or set as class-level attributes in the subclass, are turned -# into widgets based on their value -# -# 2. Functions like plot_data can take these settings as input (as well -# as some general purpose arguments like axes). Glue takes care of -# passing the proper arguments to these functions by introspecting -# their call signature. Furthermore, it extracts the current -# value of each setting (ie checked is set to True or False depending -# on what if the box is checked). -# -# The intention of all of this magic is to let a user write "simple" functions -# to draw custom plots, without having to use Glue or Qt logic directly. -# -# Internally, Glue accomlishes this magic as follows: -# -# `FormElement`s are created for each attribute in (1). They build the widget -# and have a method of extracting the current value of the widget -# -# Functions like `plot_data` that are designed to be overriden by users -# are defined as custom descriptors -- when called at the class level, -# they become decorators that wrap and register the user-defined function. -# When called at the instance level, they become dispatch functions which -# deal with the logic in (2). The metaclass deals with registering -# UDFs when they are overridden in a subclass. - -from inspect import getmodule -from functools import partial - -from inspect import getfullargspec - -from types import FunctionType, MethodType - -import numpy as np - -from qtpy.QtWidgets import QWidget, QGridLayout, QLabel - -from echo.qt import autoconnect_callbacks_to_qt - -from glue.config import qt_client - -from glue.core import BaseData -from glue.core.subset import SubsetState -from glue.core.data_combo_helper import ComponentIDComboHelper -from glue.core.component_id import ComponentID - -from glue.utils import as_list, all_artists, new_artists, categorical_ndarray, defer_draw - -from glue.viewers.matplotlib.qt.data_viewer import MatplotlibDataViewer -from glue.viewers.matplotlib.state import MatplotlibDataViewerState, MatplotlibLayerState -from glue.viewers.matplotlib.layer_artist import MatplotlibLayerArtist - -from glue.viewers.custom.qt.elements import (FormElement, - DynamicComponentIDProperty, - FixedComponentIDProperty) - -__all__ = ["AttributeWithInfo", "ViewerUserState", "UserDefinedFunction", - "CustomViewer", "CustomViewerMeta", "CustomSubsetState", - "CustomViewer", "CustomLayerArtist", "CustomMatplotlibDataViewer"] - - -class AttributeWithInfo(np.ndarray): - """ - An array subclass wrapping a Component of a dataset It is an array with the - following additional attributes: ``id`` contains the ComponentID or string - name of the Component, and ``categories`` is an array or `None`. For - categorical Components, it contains the distinct categories which are - integer-encoded in the AttributeWithInfo - """ - - @classmethod - def make(cls, id, values, categories=None): - values = np.asarray(values) - result = values.view(AttributeWithInfo) - result.id = id - result.values = values - result.categories = categories - return result - - @classmethod - def from_layer(cls, layer, cid, view=None): - """ - Build an AttributeWithInfo out of a subset or dataset. - - Parameters - ---------- - layer : :class:`~glue.core.data.Data` or :class:`~glue.core.subset.Subset` - The data to use - cid : ComponentID - The ComponentID to use - view : numpy-style view (optional) - What slice into the data to use - """ - values = layer[cid, view] - if isinstance(values, categorical_ndarray): - categories = values.categories - values = values.codes - else: - categories = None - return cls.make(cid, values, categories) - - def __gluestate__(self, context): - return dict(cid=context.id(self.id)) - - @classmethod - def __setgluestate__(cls, rec, context): - return cls.make(context.object(rec['cid']), [], None) - - -class ViewerUserState(object): - """ - Empty object for users to store data inside. - """ - - def __gluestate__(self, context): - return dict(data=[(k, context.id(v)) for k, v in self.__dict__.items()]) - - @classmethod - def __setgluestate__(cls, rec, context): - result = cls() - rec = rec['data'] - for k in rec: - setattr(result, k, context.object(rec[k])) - return result - - -class UserDefinedFunction(object): - """ - Descriptor to specify a UserDefinedFunction. - - Defined in CustomViewer like this:: - - class CustomViewer(object): - ... - plot_data = UserDefinedFunction('plot_data') - - The descriptor gives CustomViewer.plot_data a dual functionality. - When accessed at the class level, it behaves as a decorator to - register new UDFs:: - - cv = custom_viewer(...) - @cv.plot_data # becomes a decorator - def plot_data_implementation(...): - ... - - When accessed at the instance level, it becomes a dispatch function - that calls `plot_data_implementation` with the proper arguments - - Alternatively, plot_data_implementation can be specified by explicitly - overriding plot_data in a subclass. A metaclass takes care of registering - the UDF in that case, so you can define plot_data as a normal - (non-decorator, non-descriptor) method. - """ - - def __init__(self, name): - self.name = name - - def __get__(self, instance, cls=None): - if instance is None: - # accessed from class level, return a decorator - # to wrap a custom UDF - return partial(cls._register_override_method, self.name) - - # method called at instance level, - # return a dispatcher to the UDF - return partial(instance._call_udf, self.name) - - -def introspect_and_call(func, state, override): - """ - Introspect a function for its arguments, extract values for those - arguments from a state class, and call the function - - Parameters - ---------- - func : function - A function to call. It should not define any keywords - state : State - A state class containing the values to pass - override : dict - A dictionary containing values that should override the state - - Returns - ------- - The result of calling func with the proper arguments - - *Example* - - def a(x, y): - return x, y - - introspect_and_call(a, state) will return - - a(state.x, state.y) - - Attributes will be used from ``override`` before ``state``. - """ - - a, k = getfullargspec(func)[:2] - - args = [] - for item in a: - if item in override: - args.append(override[item]) - elif hasattr(state, item): - args.append(getattr(state, item)) - else: - setting_list = "\n -".join(state.callback_properties() + list(override)) - raise MissingSettingError("This custom viewer is trying to use an " - "unrecognized variable named %s\n. Valid " - "variable names are\n -%s" % - (item, setting_list)) - - k = k or {} - - return func(*args, **k) - - -class MissingSettingError(KeyError): - pass - - -class CustomViewerMeta(type): - """ - Metaclass to construct CustomViewer and subclasses - - The metaclass does two things when constructing new - classes: - - - it finds the class-level attributes that describe - ui elements (eg `checked=False`). It bundles these - into a `ui` dict attribute, later used to construct - the FormElements and widgets to represent each setting - - It creates the qt DataViewer widget class associated with this class. - - It looks for overridden user-defined methods like `plot_subset`, - and registers them for later use. - """ - def __new__(cls, name, bases, attrs): - - # don't muck with the base class - if name == 'CustomViewer': - return type.__new__(cls, name, bases, attrs) - - # Find ui elements - ui = {} - for key, value in list(attrs.items()): - if key.startswith('_') or key in CustomViewer.__dict__: - continue - if not isinstance(value, (MethodType, FunctionType)): - ui[key] = attrs.pop(key) - - attrs['ui'] = ui - attrs.setdefault('name', name) - - # collect the user defined functions - - udfs = {} - - for nm, value in list(attrs.items()): - dscr = CustomViewer.__dict__.get(nm, None) - - if isinstance(dscr, UserDefinedFunction): - # remove them as class method - # register them below instead - udfs[nm] = attrs.pop(nm) - - result = type.__new__(cls, name, bases, attrs) - result._custom_functions = {} - - # now wrap the custom user defined functions using the descriptors - for k, v in udfs.items(): - # register UDF by mimicing the decorator syntax - udf_decorator = getattr(result, k) - udf_decorator(v) - - result._build_data_viewer() - - return result - - -class CustomSubsetState(SubsetState): - """ - A SubsetState subclass that uses a CustomViewer's "select" function - """ - - def __init__(self, coordinator, roi): - super(CustomSubsetState, self).__init__() - self._coordinator = coordinator - self._roi = roi - - def to_mask(self, data, view=None): - return self._coordinator.select(layer=data, roi=self._roi, view=view) - - def copy(self): - return CustomSubsetState(self._coordinator, self._roi) - - def __gluestate__(self, context): - result = {} - result['viewer'] = context.id(self._coordinator.viewer) - result['roi'] = context.id(self._roi) - return result - - @classmethod - def __setgluestate__(cls, rec, context): - roi = context.object(rec['roi']) - subset_state = cls(None, roi) - subset_state._viewer_rec = rec['viewer'] - return subset_state - - def __setgluestate_callback__(self, context): - # When __setgluestate__ is created, the viewers might not yet be - # deserialized, and these depend on the Data and Subsets existing so - # we need to deserialize the viewer in a callback so it can be called - # later on. - viewer = context.object(self._viewer_rec) - self._coordinator = viewer._coordinator - self._viewer_rec = None - - -class BaseCustomOptionsWidget(QWidget): - """ - Base class for the Qt widget which will be used to show the options. - """ - - _widgets = None - - def __init__(self, viewer_state=None, session=None): - - super(BaseCustomOptionsWidget, self).__init__() - - layout = QGridLayout() - for row, (name, (prefix, viewer_cls)) in enumerate(self._widgets.items()): - widget = viewer_cls() - setattr(self, prefix + name, widget) - layout.addWidget(QLabel(name.capitalize()), row, 0) - layout.addWidget(widget, row, 1) - if len(self._widgets) > 0: - layout.setRowStretch(row + 1, 10) - self.setLayout(layout) - - self.viewer_state = viewer_state - self.session = session - - self._connections = autoconnect_callbacks_to_qt(self.viewer_state, self) - - -class CustomViewer(object, metaclass=CustomViewerMeta): - - """ - Base class for custom data viewers. - - - Users can either subclass this class and override - one or more custom methods listed below, or use the - :func:`glue.custom_viewer` function and decorate custom - plot functions. - - - *Custom Plot Methods* - - The following methods can be overridden: - - - :meth:`CustomViewer.setup` - - :meth:`CustomViewer.plot_data` - - :meth:`CustomViewer.plot_subset` - - :meth:`CustomViewer.settings_changed` - - :meth:`CustomViewer.make_selector` - - :meth:`CustomViewer.select` - - *Method Signatures* - - Custom methods should use argument names from the following list: - - - The name of a UI element (e.g. keywords passed to :func:`glue.custom_viewer`, - or class-level variables in subclasses). The value assigned to this - argument will be the current UI setting (e.g. booleans for checkboxes). - - ``axes`` will contain a matplotlib Axes object - - ``roi`` will contain the ROI a user has drawn (only available for ``make_selector``) - - ``state`` will contain a general-purpose object to store other data - - ``style`` contains the :class:`~glue.core.visual.VisualAttributes` describing - a subset or dataset. Only available for ``plot_data`` and `plot_subset`` - - ``subset`` will contain the relevant :class:`~glue.core.subset.Subset` object. - Only available for ``plot_subset`` - - - *Defining the UI* - - Simple widget-based UIs can be specified by providing keywords to :func:`~glue.custom_viewer` - or class-level variables to subsets. The kind of widget to associate with each - UI element is determined from it's type. - - - *Example decorator* - - :: - - v = custom_viewer('Example', checkbox=False) - - @v.plot_data - def plot(checkbox, axes): - axes.plot([1, 2, 3]) - - *Example subclass* - - :: - - class CustomViewerSubset(CustomViewer): - checkbox = False - - def plot_data(self, checkbox, axes): - axes.plot([1, 2, 3]) - - The order of arguments can be listed in any order. - """ - - # Label to give this widget in the GUI - name = '' - - # Container to hold user descriptions of desired FormElements to create - ui = {} - - # map, e.g., 'plot_data' -> user defined function - we also make sure we - # override this in sub-classes in CustomViewerMeta - _custom_functions = {} - - def __init__(self, viewer): - self.viewer = viewer - self.state = ViewerUserState() - self.setup() - - @property - def selections_enabled(self): - return 'make_selector' in self._custom_functions or 'select' in self._custom_functions - - @classmethod - def create_new_subclass(cls, name, **kwargs): - """ - Convenience method to build a new CustomViewer subclass. - - This is used by the custom_viewer function. - - Parameters - ---------- - name : str - Name of the new viewer - kwargs - UI elements in the subclass - """ - kwargs = kwargs.copy() - kwargs['name'] = name - # each subclass needs its own dict - kwargs['_custom_functions'] = {} - name = name.replace(' ', '') - return CustomViewerMeta(name, (CustomViewer,), kwargs) - - @classmethod - def _build_data_viewer(cls): - """ - Build the DataViewer subclass for this viewer. - """ - - # At this point, the metaclass has put all the user options in a dict - # called .ui, so we go over this dictionary and find the widgets and - # callback properties for each of them. - - widgets = {} - properties = {} - - for name in sorted(cls.ui): - - value = cls.ui[name] - prefix, widget, property = FormElement.auto(value).ui_and_state() - - if widget is not None: - widgets[name] = prefix, widget - - properties[name] = property - - options_cls = type(cls.__name__ + 'OptionsWidget', - (BaseCustomOptionsWidget,), {'_widgets': widgets}) - - state_cls = type(cls.__name__ + 'ViewerState', (CustomMatplotlibViewerState,), properties) - - widget_dict = {'LABEL': cls.name, - 'ui': cls.ui, - '_options_cls': options_cls, - '_state_cls': state_cls, - '_coordinator_cls': cls} - - viewer_cls = type(cls.__name__ + 'DataViewer', - (CustomMatplotlibDataViewer,), - widget_dict) - - cls._viewer_cls = viewer_cls - qt_client.add(viewer_cls) - - # add new classes to module namespace - # needed for proper state saving/restoring - for c in [viewer_cls, cls]: - mod = getmodule(ViewerUserState) - w = getattr(mod, c.__name__, None) - if w is not None: - raise RuntimeError("Duplicate custom viewer detected %s" % c) - setattr(mod, c.__name__, c) - c.__module__ = mod.__name__ - - @classmethod - def _register_override_method(cls, name, func): - """ - Register a new custom method like "plot_data" - - Users need not call this directly - it is called when a method is - overridden or decorated - """ - cls._custom_functions[name] = func - - def _build_subset_state(self, roi): - - if 'make_selector' in self._custom_functions: - return self.make_selector(roi=roi) - - if 'select' in self._custom_functions: - return CustomSubsetState(self, roi) - - raise RuntimeError("Selection not supported for this viewer.") - - # List of user-defined functions. - # Users can either use these as decorators to - # wrap custom functions, or override them in subclasses. - - setup = UserDefinedFunction('setup') - """ - Custom method called when plot is created - """ - - plot_subset = UserDefinedFunction('plot_subset') - """ - Custom method called to show a subset - """ - - plot_data = UserDefinedFunction('plot_data') - """ - Custom method called to show a dataset - """ - - make_selector = UserDefinedFunction('make_selector') - """ - Custom method called to build a :class:`~glue.core.subset.SubsetState` from an ROI. - - See :meth:`~CustomViewer.select` for an alternative way to define selections, - by returning Boolean arrays instead of SubsetStates. - - Functions have access to the roi by accepting an ``roi`` - argument to this function - """ - - settings_changed = UserDefinedFunction('settings_changed') - """ - Custom method called when UI settings change. - """ - - select = UserDefinedFunction('select') - """ - Custom method called to filter data using an ROI. - - This is an alternative function to :meth:`~CustomViewer.make_selector`, - which returns a numpy boolean array instead of a SubsetState. - - Functions have access to the roi by accepting an ``roi`` - argument to this function - """ - - def _call_udf(self, method_name, **kwargs): - """ - Call a user-defined function stored in the _custom_functions dict - - Parameters - ---------- - method_name : str - The name of the user-defined method to setup a dispatch for - use_cid : bool, optional - Whether to pass component IDs to the user function instead of the - data itself. - **kwargs : dict - Custom settings to pass to the UDF if they are requested by name - as input arguments - - Returns - ------- - The result of the UDF - - Notes - ----- - This function builds the necessary arguments to the - user-defined function. It also attempts to monitor - the state of the matplotlib plot, removing stale - artists and re-rendering the canvas as needed. - """ - - # get the custom function - try: - func = self._custom_functions[method_name] - except KeyError: - return [] - - override = kwargs.copy() - - if 'layer' not in override and len(self.viewer.state.layers) > 0: - override['layer'] = self.viewer.state.layers[0].layer - - if 'layer' in override: - - override.setdefault('style', override['layer'].style) - - # Dereference attributes - for name, property in self.viewer.state.iter_callback_properties(): - value = getattr(self.viewer.state, name) - if isinstance(value, ComponentID) or isinstance(property, FixedComponentIDProperty): - override[name] = AttributeWithInfo.from_layer(override['layer'], value, view=override.get('view', None)) - - # add some extra information that the user might want - override.setdefault('self', self) - override.setdefault('axes', self.viewer.axes) - override.setdefault('figure', self.viewer.axes.figure) - override.setdefault('state', self.state) - - # call method, keep track of newly-added artists - result = introspect_and_call(func, self.viewer.state, override) - - self.viewer.redraw() - - return result - - -class CustomLayerArtist(MatplotlibLayerArtist): - """ - LayerArtist for simple custom viewers that use Matplotlib - """ - - _layer_state_cls = MatplotlibLayerState - - def __init__(self, coordinator, *args, **kwargs): - super(CustomLayerArtist, self).__init__(*args, **kwargs) - self._coordinator = coordinator - self.state.add_global_callback(self.update) - self._viewer_state.add_global_callback(self.update) - - def update(self, *args, **kwargs): - - if not self._visible: - return - - self.clear() - - old = all_artists(self.axes.figure) - - if isinstance(self.state.layer, BaseData): - a = self._coordinator.plot_data(layer=self.state.layer) - else: - a = self._coordinator.plot_subset(layer=self.state.layer, subset=self.state.layer) - - # if user explicitly returns the newly-created artists, - # then use them. Otherwise, introspect to find the new artists - if a is None: - self.mpl_artists = list(new_artists(self.axes.figure, old)) - else: - self.mpl_artists = as_list(a) - - for a in self.mpl_artists: - a.set_zorder(self.state.zorder) - - -class CustomMatplotlibDataViewer(MatplotlibDataViewer): - """ - Base Qt widget class for simple custom viewers that use Matplotlib - """ - - LABEL = '' - tools = ['select:rectangle', 'select:polygon'] - - _state_cls = None - _options_cls = None - _layer_style_viewer_cls = None - _data_artist_cls = CustomLayerArtist - _subset_artist_cls = CustomLayerArtist - - _coordinator_cls = None - - def __init__(self, session, parent=None, **kwargs): - super(CustomMatplotlibDataViewer, self).__init__(session, parent, **kwargs) - self._coordinator = self._coordinator_cls(self) - self.state.add_global_callback(self._on_state_change) - self._on_state_change() - - def _on_state_change(self, *args, **kwargs): - self._coordinator.settings_changed() - - def get_layer_artist(self, cls, layer=None, layer_state=None): - return cls(self._coordinator, self.axes, self.state, layer=layer, layer_state=layer_state) - - @defer_draw - def apply_roi(self, roi): - - # Force redraw to get rid of ROI. We do this because applying the - # subset state below might end up not having an effect on the viewer, - # for example there may not be any layers, or the active subset may not - # be one of the layers. So we just explicitly redraw here to make sure - # a redraw will happen after this method is called. - self.redraw() - - if len(self.layers) == 0: - return - - subset_state = self._coordinator._build_subset_state(roi=roi) - self.apply_subset_state(subset_state) - - -class CustomMatplotlibViewerState(MatplotlibDataViewerState): - - def __init__(self, *args, **kwargs): - super(CustomMatplotlibViewerState, self).__init__(*args) - self._cid_helpers = [] - for name, property in self.iter_callback_properties(): - if isinstance(property, DynamicComponentIDProperty): - self._cid_helpers.append(ComponentIDComboHelper(self, name)) - self.add_callback('layers', self._on_layer_change) - self.update_from_dict(kwargs) - - def _on_layer_change(self, *args): - for helper in self._cid_helpers: - helper.set_multiple_data(self.layers_data) +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.viewers.custom.qt.custom_viewer is deprecated, use glue_qt.viewers.custom.custom_viewer instead', GlueDeprecationWarning) +from glue_qt.viewers.custom.custom_viewer import * # noqa diff --git a/glue/viewers/custom/qt/elements.py b/glue/viewers/custom/qt/elements.py index 5b3d71641..1c2d688c9 100644 --- a/glue/viewers/custom/qt/elements.py +++ b/glue/viewers/custom/qt/elements.py @@ -1,288 +1,4 @@ -from echo import CallbackProperty, SelectionCallbackProperty - -from qtpy.QtWidgets import (QSlider, QLineEdit, QComboBox, QWidget, - QLabel, QHBoxLayout, QCheckBox) -from qtpy.QtCore import Qt - -__all__ = ["FormElement", "NumberElement", "TextBoxElement", "FloatElement", - "BoolElement", "ChoiceElement", "FixedComponentIDProperty", - "FixedComponentElement", "ComponenentElement", "DynamicComponentIDProperty"] - - -class QLabeledSlider(QWidget): - """ - A labeled slider widget - """ - - range = None - integer = None - - def __init__(self, parent=None): - - super(QLabeledSlider, self).__init__(parent) - - self._range = range - - self._slider = QSlider() - self._slider.setMinimum(0) - self._slider.setMaximum(100) - self._slider.setOrientation(Qt.Horizontal) - - self._label = QLabel('') - self._layout = QHBoxLayout() - self._layout.setContentsMargins(2, 2, 2, 2) - self._layout.addWidget(self._slider) - self._layout.addWidget(self._label) - - self._slider.valueChanged.connect(self._update_label) - - self.setLayout(self._layout) - - def _update_label(self, *args): - self._label.setText(str(self.value())) - - @property - def valueChanged(self): - return self._slider.valueChanged - - def value(self, layer=None, view=None): - value = self._slider.value() / 100. * (self.range[1] - self.range[0]) + self.range[0] - if self.integer: - return int(value) - else: - return value - - _in_set_value = False - - def setValue(self, value): - if self._in_set_value: - return - self._in_set_value = True - value = int(100 * (value - self.range[0]) / (self.range[1] - self.range[0])) - self._slider.setValue(value) - self._in_set_value = False - - -class FormElement(object): - - """ - Base class for user-defined settings in a custom widget. - - Each form element has a value() and a widget. Subclasses - must override _build_ui, value, and recognizes. They - may override register_to_hub and add_data. - """ - - def __init__(self, params): - self.params = params - - @classmethod - def recognizes(cls, params): - """ - Returns whether or not a shorthand "params" object - can be passed to __init__ to construct an element - """ - raise NotImplementedError - - def ui_and_state(self): - """ - Build and return a widget to represent this setting. - """ - raise NotImplementedError() - - @staticmethod - def auto(params): - """ - Construct the appropriate FormElement subclass, - given a shorthand object. For examle, - FormElement.auto((0., 1.)) returns a NumberElement - """ - - def subclasses(cls): - return cls.__subclasses__() + [g for s in cls.__subclasses__() for g in subclasses(s)] - - for cls in subclasses(FormElement): - if cls.recognizes(params): - return cls(params) - raise ValueError("Unrecognzied UI Component: %s" % (params,)) - - -class NumberElement(FormElement): - - """ - A form element representing a number - - The shorthand is a tuple of 2 or 3 numbers: - (min, max) or (min, max default):: - - e = FormElement.auto((0., 1.)) - """ - - @classmethod - def recognizes(cls, params): - try: - if len(params) not in [2, 3]: - return False - return all(isinstance(p, (int, float)) for p in params) - except TypeError: - return False - - def ui_and_state(self): - - if len(self.params) == 3: - default = self.params[2] - else: - default = 0.5 * (self.params[0] + self.params[1]) - - # We can't initialize QLabeledSlider yet because this could get called - # before the Qt application has been initialized. So for now we just make - # a subclass of QLabeledSlider with the range we need - class CustomSlider(QLabeledSlider): - range = self.params[:2] - integer = isinstance(self.params[0], int) and isinstance(self.params[1], int) - - return 'value_', CustomSlider, CallbackProperty(default) - - -class TextBoxElement(FormElement): - """ - A form element representing a generic textbox - - The shorthand is any string starting with an _.:: - - e = FormElement.auto("_default") - - Everything after the underscore is taken as the default value. - """ - - @classmethod - def recognizes(cls, params): - try: - if isinstance(params, str) & params.startswith('_'): - return True - except AttributeError: - return None - - def ui_and_state(self): - default = self.params[1:] - return 'text_', QLineEdit, CallbackProperty(default) - - -class FloatElement(FormElement): - """ - A form element representing a generic number box. - - The shorthand is any number:: - - e = FormElement.auto(2) - - The number itself is taken as the default value. - """ - - @classmethod - def recognizes(cls, params): - return isinstance(params, (int, float)) and not isinstance(params, bool) - - def ui_and_state(self): - default = self.params - return 'valuetext_', QLineEdit, CallbackProperty(default) - - -class BoolElement(FormElement): - - """ - A checkbox representing a boolean setting - - The shorthand notation is True or False:: - - e = FormElement.auto(False) - """ - - @classmethod - def recognizes(cls, params): - return isinstance(params, bool) - - def ui_and_state(self): - default = self.params - return 'bool_', QCheckBox, CallbackProperty(default) - - -class ChoiceElement(FormElement): - - """ - A dropdown selector to choose between a set of items - - Shorthand notation is a sequence of strings or a dict:: - - e = FormElement.auto({'a':1, 'b':2}) - e = FormElement.auto(['a', 'b', 'c']) - """ - - @classmethod - def recognizes(cls, params): - if isinstance(params, str): - return False - try: - return all(isinstance(p, str) for p in params) - except TypeError: - return False - - def ui_and_state(self): - if isinstance(self.params, list): - choices = self.params - display_func = None - else: - params_inv = dict((value, key) for key, value in self.params.items()) - choices = list(params_inv.keys()) - display_func = params_inv.get - property = SelectionCallbackProperty(default_index=0, choices=choices, - display_func=display_func) - return 'combosel_', QComboBox, property - - -class FixedComponentIDProperty(CallbackProperty): - pass - - -class FixedComponentElement(FormElement): - - """ - An element for a Data Component. Does not have a widget - - The shorthand notation is 'att(comp_name)':: - - e = FormElement.auto('att(foo)') - """ - - @classmethod - def recognizes(cls, params): - try: - return params.startswith('att(') - except AttributeError: - return False - - def ui_and_state(self): - component_name = self.params.split('(')[-1][:-1] - return None, None, FixedComponentIDProperty(component_name) - - -class DynamicComponentIDProperty(SelectionCallbackProperty): - pass - - -class ComponenentElement(FormElement): - - """ - A dropdown selector to choose a component - - The shorthand notation is 'att':: - - e = FormElement.auto('att') - """ - - @classmethod - def recognizes(cls, params): - return params == 'att' - - def ui_and_state(self): - return 'combosel_', QComboBox, DynamicComponentIDProperty() +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.viewers.custom.qt.elements is deprecated, use glue_qt.viewers.custom.elements instead', GlueDeprecationWarning) +from glue_qt.viewers.custom.elements import * # noqa diff --git a/glue/viewers/custom/qt/tests/__init__.py b/glue/viewers/custom/qt/tests/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/glue/viewers/custom/qt/tests/test_custom_viewer.py b/glue/viewers/custom/qt/tests/test_custom_viewer.py deleted file mode 100644 index e40e5fb22..000000000 --- a/glue/viewers/custom/qt/tests/test_custom_viewer.py +++ /dev/null @@ -1,339 +0,0 @@ -from collections import OrderedDict - -import numpy as np -from matplotlib.axes import Axes -from unittest.mock import MagicMock -from numpy.testing import assert_array_equal - -from glue.core.tests.test_state import clone -from glue.core.tests.util import simple_session -from glue.core.subset import SubsetState -from glue.core import Data -from glue import custom_viewer - -from glue.app.qt import GlueApplication -from glue.app.qt.tests.test_application import check_clone_app -from ..custom_viewer import CustomViewer, CustomSubsetState, AttributeWithInfo - - -def _make_widget(viewer): - s = simple_session() - return viewer._viewer_cls(s) - - -viewer = custom_viewer('Testing Custom Viewer', - a=(0, 100), - b='att', - c='att(x)', - d=True, - e=False, - f=['a', 'b', 'c'], - g=OrderedDict(a=1, b=2, c=3), - h=64 - ) - - -setup = MagicMock() -settings_changed = MagicMock() -plot_subset = MagicMock() -plot_data = MagicMock() -make_selector = MagicMock() -make_selector.return_value = MagicMock(spec=SubsetState) -make_selector().copy.return_value = MagicMock(spec=SubsetState) -make_selector().copy().to_mask.return_value = np.array([False, True, True]) - - -@viewer.setup -def _setup(axes): - setup(axes) - - -@viewer.plot_data -def _plot_data(axes, a, b, g, h): - plot_data(axes=axes, a=a, b=b, g=g, h=h) - return [] - - -@viewer.plot_subset -def _plot_subset(b, c, d, e, f, style): - plot_subset(b=b, c=c, d=d, e=e, f=f, style=style) - return [] - - -@viewer.settings_changed -def _settings_changed(state): - settings_changed(state=state) - - -@viewer.make_selector -def _make_selector(roi, c): - make_selector(roi=roi, c=c) - return SubsetState() - - -def test_custom_classes_dont_share_methods(): - """Regression test for #479""" - a = custom_viewer('a') - b = custom_viewer('b') - assert a._custom_functions is not b._custom_functions - - -class ViewerSubclass(CustomViewer): - a = (0, 100) - b = 'att' - c = 'att(x)' - d = True - e = False - f = ['a', 'b', 'c'] - g = OrderedDict(a=1, b=2, c=3) - h = 64 - - def setup(self, axes): - return setup(axes) - - def plot_data(self, axes, a, b, g, h): - return plot_data(axes=axes, a=a, b=b, g=g, h=h) - - def plot_subset(self, b, c, d, e, f, style): - return plot_subset(b=b, c=c, d=d, e=e, f=f, style=style) - - def settings_changed(self, state): - return settings_changed(state=state) - - def make_selector(self, roi, c): - return make_selector(roi=roi, c=c) - - -class TestCustomViewer(object): - - def setup_class(self): - self.viewer = viewer - - def setup_method(self, method): - setup.reset_mock() - settings_changed.reset_mock() - plot_subset.reset_mock() - plot_data.reset_mock() - make_selector.reset_mock() - - self.data = Data(x=[1, 2, 3], y=[2, 3, 4]) - self.session = simple_session() - self.dc = self.session.data_collection - self.dc.append(self.data) - - def teardown_method(self, method): - if hasattr(self, 'w'): - self.w.unregister(self.session.hub) - - def build(self): - w = self.viewer._viewer_cls(self.session) - w.register_to_hub(self.session.hub) - self.w = w - return w - - def test_setup_called_on_init(self): - ct = setup.call_count - self.build() - assert setup.call_count == ct + 1 - - def test_separate_widgets_have_separate_state(self): - w1 = self.build() - w2 = self.build() - - assert w1._coordinator is not w2._coordinator - assert w1._coordinator.state is not w2._coordinator.state - - def test_plot_data(self): - w = self.build() - w.add_data(self.data) - - a, k = plot_data.call_args - assert isinstance(k['axes'], Axes) - assert set(k.keys()) == set(('axes', 'a', 'b', 'g', 'h')) - assert k['a'] == 50 - assert k['g'] == 1 - assert k['h'] == 64 - - def test_plot_subset(self): - w = self.build() - w.add_data(self.data) - - self.dc.new_subset_group(subset_state=self.data.id['x'] > 2) - - a, k = plot_subset.call_args - assert set(k.keys()) == set(('b', 'c', 'd', 'e', 'f', 'style')) - - assert_array_equal(k['b'], [3]) - assert_array_equal(k['c'], [3]) - assert k['d'] - assert not k['e'] - assert k['f'] == 'a' - - def test_make_selector(self): - w = self.build() - w.add_data(self.data) - roi = MagicMock() - w.apply_roi(roi) - - a, k = make_selector.call_args - - assert set(k.keys()) == set(('roi', 'c')) - assert k['roi'] is roi - - def test_settings_change(self): - w = self.build() - ct = settings_changed.call_count - w.options_widget().bool_d.setChecked(False) - assert settings_changed.call_count == ct + 1 - a, k = settings_changed.call_args - assert 'state' in k - - def test_component_autoupdate(self): - - w = self.build() - w.add_data(self.data) - - assert w.options_widget().combosel_b.count() == 2 - self.data.add_component([10, 20, 30], label='c') - assert w.options_widget().combosel_b.count() == 3 - - def test_settings_changed_called_on_init(self): - self.build() - assert settings_changed.call_count == 1 - - def test_selections_enabled(self): - w = self.build() - assert w._coordinator.selections_enabled - assert 'select:rectangle' in w.toolbar.tools - assert 'select:polygon' in w.toolbar.tools - - -def test_state_save(): - app = GlueApplication() - w = app.new_data_viewer(viewer._viewer_cls) # noqa - check_clone_app(app) - - -def test_state_save_with_data_layers(): - app = GlueApplication() - dc = app.data_collection - d = Data(x=[1, 2, 3], label='test') - dc.append(d) - w = app.new_data_viewer(viewer._viewer_cls) - w.add_data(d) - check_clone_app(app) - - -class TestCustomSelectMethod(object): - - def setup_class(self): - self.viewer = custom_viewer('CustomSelectViewer', - x='att(x)', flip=False) - - @self.viewer.select - def select(roi, x, flip): - if flip: - return x <= 1 - return x > 1 - - def setup_method(self, method): - self.data = Data(x=[1, 2, 3], y=[2, 3, 4]) - self.session = simple_session() - self.dc = self.session.data_collection - self.dc.append(self.data) - - def build(self): - return self.viewer._viewer_cls(self.session) - - def test_subset_state(self): - w = self.build() - v = w._coordinator - roi = MagicMock() - s = CustomSubsetState(v, roi) - assert_array_equal(s.to_mask(self.data), [False, True, True]) - - def test_subset_state_view(self): - w = self.build() - v = w._coordinator - roi = MagicMock() - s = CustomSubsetState(v, roi) - assert_array_equal(s.to_mask(self.data, view=slice(None, None, 2)), - [False, True]) - - def test_settings_frozen_at_creation(self): - w = self.build() - v = w._coordinator - roi = MagicMock() - s = CustomSubsetState(v, roi) - w.flip = True - assert_array_equal(s.to_mask(self.data), [False, True, True]) - - def test_save_load(self): - app = GlueApplication(session=self.session) - w = app.new_data_viewer(self.viewer._viewer_cls) - v = w._coordinator - roi = None - s = CustomSubsetState(v, roi) - app.data_collection.new_subset_group(subset_state=s, label='test') - app2 = clone(app) - s2 = app2.data_collection[0].subsets[0].subset_state - assert_array_equal(s2.to_mask(self.data), [False, True, True]) - - -class TestCustomViewerSubclassForm(TestCustomViewer): - - def setup_class(self): - self.viewer = ViewerSubclass - - -class TestAttributeWithInfo(object): - - def setup_method(self, method): - d = Data(x=[1, 2, 3, 4, 5], c=['a', 'b', 'a', 'a', 'b'], label='test') - s = d.new_subset() - s.subset_state = d.id['x'] > 2 - self.d = d - self.s = s - - def test_numerical(self): - v = AttributeWithInfo.from_layer(self.d, self.d.id['x']) - assert_array_equal(v, [1, 2, 3, 4, 5]) - assert v.id == self.d.id['x'] - assert v.categories is None - - def test_categorical(self): - v = AttributeWithInfo.from_layer(self.d, self.d.id['c']) - assert_array_equal(v, [0, 1, 0, 0, 1]) - assert v.id == self.d.id['c'] - assert_array_equal(v.categories, ['a', 'b']) - - def test_subset(self): - v = AttributeWithInfo.from_layer(self.s, self.d.id['x']) - assert_array_equal(v, [3, 4, 5]) - assert v.id == self.d.id['x'] - assert v.categories is None - - -def test_two_custom_viewer_classes(): - - class MyWidget1(CustomViewer): - - text_box1_Widget1 = '_Hello' - - def setup(self, text_box1_Widget1): - pass - - class MyWidget2(CustomViewer): - - text_box1_Widget2 = '_Hello' - text_box2_Widget2 = '_world' - - def setup(self, text_box1_Widget2, text_box2_Widget2): - pass - - app = GlueApplication() - dc = app.data_collection - d = Data(x=[1, 2, 3], label='test') - dc.append(d) - app.new_data_viewer(MyWidget1._viewer_cls) - app.new_data_viewer(MyWidget2._viewer_cls) diff --git a/glue/viewers/custom/qt/tests/test_elements.py b/glue/viewers/custom/qt/tests/test_elements.py deleted file mode 100644 index 076644d85..000000000 --- a/glue/viewers/custom/qt/tests/test_elements.py +++ /dev/null @@ -1,66 +0,0 @@ -import pytest - -from glue.core.state_objects import State -from ..elements import (FormElement, NumberElement, ChoiceElement, - FloatElement, TextBoxElement) - - -def get_value(element): - - prefix, widget_cls, property = element.ui_and_state() - - class TemporaryState(State): - a = property - - temp = TemporaryState() - return temp.a - - -class TestFormElements(object): - - def test_number_default_value(self): - e = FormElement.auto((0, 100, 30)) - assert get_value(e) == 30 - - def test_number_float(self): - e = FormElement.auto((0.0, 1.0, 0.3)) - assert get_value(e) == 0.3 - - def test_number_list(self): - e = FormElement.auto([0, 10]) - assert isinstance(e, NumberElement) - - def test_choice_list(self): - e = FormElement.auto(['a', 'b']) - assert isinstance(e, ChoiceElement) - - def test_choice_tuple(self): - e = FormElement.auto(('a', 'b')) - assert isinstance(e, ChoiceElement) - - def test_float(self): - e = FormElement.auto(1.2) - assert isinstance(e, FloatElement) - - e = FormElement.auto(2) - assert isinstance(e, FloatElement) - assert get_value(e) == 2 - - def test_textbox(self): - e = FormElement.auto('_str') - assert isinstance(e, TextBoxElement) - assert get_value(e) == 'str' - - def test_recognizes_subsubclasses(self): - - class SubClassFormElement(TextBoxElement): - @classmethod - def recognizes(cls, params): - return params == 'specific_class' - - e = FormElement.auto('specific_class') - assert isinstance(e, SubClassFormElement) - - def test_unrecognized(self): - with pytest.raises(ValueError): - FormElement.auto(None) diff --git a/glue/viewers/histogram/qt/__init__.py b/glue/viewers/histogram/qt/__init__.py index e9447c8a0..96226f2e9 100644 --- a/glue/viewers/histogram/qt/__init__.py +++ b/glue/viewers/histogram/qt/__init__.py @@ -1,6 +1,4 @@ -from .data_viewer import HistogramViewer # noqa - - -def setup(): - from glue.config import qt_client - qt_client.add(HistogramViewer) +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.viewers.histogram.qt is deprecated, use glue_qt.viewers.histogram instead', GlueDeprecationWarning) +from glue_qt.viewers.histogram import * # noqa diff --git a/glue/viewers/histogram/qt/data_viewer.py b/glue/viewers/histogram/qt/data_viewer.py index f1ee0457c..c294f9bfb 100644 --- a/glue/viewers/histogram/qt/data_viewer.py +++ b/glue/viewers/histogram/qt/data_viewer.py @@ -1,31 +1,4 @@ -from glue.utils import defer_draw, decorate_all_methods -from glue.viewers.matplotlib.qt.data_viewer import MatplotlibDataViewer -from glue.viewers.histogram.qt.layer_style_editor import HistogramLayerStyleEditor -from glue.viewers.histogram.qt.options_widget import HistogramOptionsWidget -from glue.viewers.histogram.qt.layer_artist import QThreadedHistogramLayerArtist -from glue.viewers.histogram.state import HistogramViewerState - -from glue.viewers.histogram.viewer import MatplotlibHistogramMixin - -__all__ = ['HistogramViewer'] - - -@decorate_all_methods(defer_draw) -class HistogramViewer(MatplotlibHistogramMixin, MatplotlibDataViewer): - - LABEL = '1D Histogram' - - _layer_style_widget_cls = HistogramLayerStyleEditor - _options_cls = HistogramOptionsWidget - - _state_cls = HistogramViewerState - _data_artist_cls = QThreadedHistogramLayerArtist - _subset_artist_cls = QThreadedHistogramLayerArtist - - large_data_size = 2e7 - - tools = ['select:xrange'] - - def __init__(self, session, parent=None, state=None): - super(HistogramViewer, self).__init__(session, parent=parent, state=state) - MatplotlibHistogramMixin.setup_callbacks(self) +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.viewers.histogram.qt.data_viewer is deprecated, use glue_qt.viewers.histogram.data_viewer instead', GlueDeprecationWarning) +from glue_qt.viewers.histogram.data_viewer import * # noqa diff --git a/glue/viewers/histogram/qt/layer_artist.py b/glue/viewers/histogram/qt/layer_artist.py index 5a1bfa13e..693af623e 100644 --- a/glue/viewers/histogram/qt/layer_artist.py +++ b/glue/viewers/histogram/qt/layer_artist.py @@ -1,61 +1,4 @@ -import time -from glue.utils import defer_draw -from glue.viewers.histogram.layer_artist import HistogramLayerArtist -from glue.viewers.matplotlib.qt.compute_worker import ComputeWorker - -__all__ = ['QThreadedHistogramLayerArtist'] - - -class QThreadedHistogramLayerArtist(HistogramLayerArtist): - - def __init__(self, axes, viewer_state, layer_state=None, layer=None): - - super(QThreadedHistogramLayerArtist, self).__init__(axes, viewer_state, - layer_state=layer_state, layer=layer) - - self.setup_thread() - - def wait(self): - # Wait 0.5 seconds to make sure that the computation has properly started - time.sleep(0.5) - while self._worker.running: - time.sleep(1 / 25) - from glue.utils.qt import process_events - process_events() - - def remove(self): - super(QThreadedHistogramLayerArtist, self).remove() - if self._worker is not None: - self._worker.work_queue.put('stop') - self._worker.exit() - # Need to wait otherwise the thread will be destroyed while still - # running, causing a segmentation fault - self._worker.wait() - self._worker = None - - @property - def is_computing(self): - return self._worker is not None and self._worker.running - - def setup_thread(self): - self._worker = ComputeWorker(self._calculate_histogram_thread) - self._worker.compute_end.connect(self._calculate_histogram_postthread) - self._worker.compute_error.connect(self._calculate_histogram_error) - self._worker.compute_start.connect(self.notify_start_computation) - self._worker.start() - - @defer_draw - def _calculate_histogram(self, reset=False): - if self.state.layer is not None and self.state.layer.size > 1e7: - self._worker.work_queue.put(reset) - else: - super(QThreadedHistogramLayerArtist, self)._calculate_histogram(reset=reset) - - def _calculate_histogram_postthread(self): - - # If the worker has started running again, we should stop at this point - # since this function will get called again. - if self._worker is not None and self._worker.running: - return - - super(QThreadedHistogramLayerArtist, self)._calculate_histogram_postthread() +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.viewers.histogram.qt.layer_artist is deprecated, use glue_qt.viewers.histogram.layer_artist instead', GlueDeprecationWarning) +from glue_qt.viewers.histogram.layer_artist import * # noqa diff --git a/glue/viewers/histogram/qt/layer_style_editor.py b/glue/viewers/histogram/qt/layer_style_editor.py index b4a7474d7..96f7f1f29 100644 --- a/glue/viewers/histogram/qt/layer_style_editor.py +++ b/glue/viewers/histogram/qt/layer_style_editor.py @@ -1,20 +1,4 @@ -import os - -from qtpy import QtWidgets - -from echo.qt import autoconnect_callbacks_to_qt -from glue.utils.qt import load_ui - - -class HistogramLayerStyleEditor(QtWidgets.QWidget): - - def __init__(self, layer, parent=None): - - super(HistogramLayerStyleEditor, self).__init__(parent=parent) - - self.ui = load_ui('layer_style_editor.ui', self, - directory=os.path.dirname(__file__)) - - connect_kwargs = {'alpha': dict(value_range=(0, 1))} - - self._connections = autoconnect_callbacks_to_qt(layer.state, self.ui, connect_kwargs) +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.viewers.histogram.qt.layer_style_editor is deprecated, use glue_qt.viewers.histogram.layer_style_editor instead', GlueDeprecationWarning) +from glue_qt.viewers.histogram.layer_style_editor import * # noqa diff --git a/glue/viewers/histogram/qt/layer_style_editor.ui b/glue/viewers/histogram/qt/layer_style_editor.ui deleted file mode 100644 index 3eb8b7b20..000000000 --- a/glue/viewers/histogram/qt/layer_style_editor.ui +++ /dev/null @@ -1,127 +0,0 @@ - - - Form - - - - 0 - 0 - 154 - 96 - - - - Form - - - - 5 - - - 5 - - - 5 - - - 5 - - - 10 - - - 5 - - - - - - 0 - 0 - - - - - - - - - - - - 75 - true - - - - color - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - 75 - true - - - - opacity - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - 100 - - - Qt::Horizontal - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - Qt::Horizontal - - - - 40 - 5 - - - - - - - - - QColorBox - QLabel -
glue.utils.qt.colors
-
-
- - -
diff --git a/glue/viewers/histogram/qt/options_widget.py b/glue/viewers/histogram/qt/options_widget.py index 4c2562105..3a7c880eb 100644 --- a/glue/viewers/histogram/qt/options_widget.py +++ b/glue/viewers/histogram/qt/options_widget.py @@ -1,48 +1,4 @@ -import os - -from qtpy import QtWidgets - -from echo.qt import autoconnect_callbacks_to_qt -from glue.utils.qt import load_ui, fix_tab_widget_fontsize -from glue.viewers.matplotlib.state import MatplotlibDataViewerState - -__all__ = ['HistogramOptionsWidget'] - - -class HistogramOptionsWidget(QtWidgets.QWidget): - - def __init__(self, viewer_state, session, parent=None): - - super(HistogramOptionsWidget, self).__init__(parent=parent) - - self.ui = load_ui('options_widget.ui', self, - directory=os.path.dirname(__file__)) - - fix_tab_widget_fontsize(self.ui.tab_widget) - - self._connections = autoconnect_callbacks_to_qt(viewer_state, self.ui) - self._connections_axes = autoconnect_callbacks_to_qt(viewer_state, self.ui.axes_editor.ui) - connect_kwargs = {'alpha': dict(value_range=(0, 1))} - self._connections_legend = autoconnect_callbacks_to_qt(viewer_state.legend, self.ui.legend_editor.ui, connect_kwargs) - - self.viewer_state = viewer_state - - viewer_state.add_callback('x_att', self._update_attribute) - - self.session = session - self.ui.axes_editor.button_apply_all.clicked.connect(self._apply_all_viewers) - - def _apply_all_viewers(self): - for tab in self.session.application.viewers: - for viewer in tab: - if isinstance(viewer.state, MatplotlibDataViewerState): - viewer.state.update_axes_settings_from(self.viewer_state) - - def _update_attribute(self, *args): - # If at least one of the components is categorical or a date, disable log button - log_enabled = 'categorical' not in self.viewer_state.x_kinds - self.ui.bool_x_log.setEnabled(log_enabled) - self.ui.bool_x_log_.setEnabled(log_enabled) - if not log_enabled: - self.ui.bool_x_log.setChecked(False) - self.ui.bool_x_log_.setChecked(False) +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.viewers.histogram.qt.options_widget is deprecated, use glue_qt.viewers.histogram.options_widget instead', GlueDeprecationWarning) +from glue_qt.viewers.histogram.options_widget import * # noqa diff --git a/glue/viewers/histogram/qt/options_widget.ui b/glue/viewers/histogram/qt/options_widget.ui deleted file mode 100644 index eda9080db..000000000 --- a/glue/viewers/histogram/qt/options_widget.ui +++ /dev/null @@ -1,462 +0,0 @@ - - - Widget - - - - 0 - 0 - 269 - 418 - - - - 1D Histogram - - - - 5 - - - 5 - - - 5 - - - 5 - - - 5 - - - - - 0 - - - - General - - - - 10 - - - 10 - - - 10 - - - 10 - - - 10 - - - 5 - - - - - - 0 - 0 - - - - - 5 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - normalized - - - true - - - - - - - cumulative - - - true - - - - - - - log - - - true - - - - - - - Qt::Horizontal - - - - 1 - 20 - - - - - - - - - - - - - - - 50 - false - - - - bins - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Update bins to view - - - - - - - Qt::Horizontal - - - - 40 - 5 - - - - - - - - Qt::Vertical - - - QSizePolicy::Fixed - - - - 20 - 5 - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - 75 - true - - - - y axis - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Fixed # of numerical bins - - - - - - - - 75 - true - - - - x axis - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - true - - - 1000 - - - - - - - QComboBox::AdjustToMinimumContentsLengthWithIcon - - - - - - - log - - - true - - - - - - - - Limits - - - - 10 - - - 10 - - - 10 - - - 10 - - - 10 - - - 5 - - - - - padding: 0px - - - ⇄ - - - - - - - - - - - - - - - - - 75 - true - - - - x axis - - - - - - - - 75 - true - - - - y axis - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - Qt::Horizontal - - - - 40 - 5 - - - - - - - - log - - - true - - - - - - - log - - - true - - - - - bool_x_log - valuetext_x_max - button_flip_x - valuetext_x_min - valuetext_y_min - valuetext_y_max - bool_y_log - label_2 - label_5 - verticalSpacer - horizontalSpacer_2 - - - - Axes - - - - 5 - - - 5 - - - 5 - - - 5 - - - - - - - - - Legend - - - - 5 - - - 5 - - - 5 - - - 5 - - - - - - - - - - - - - - AxesEditorWidget - QWidget -
glue.viewers.matplotlib.qt.axes_editor
-
- - LegendEditorWidget - QWidget -
glue.viewers.matplotlib.qt.legend_editor
-
-
- - -
diff --git a/glue/viewers/histogram/qt/tests/__init__.py b/glue/viewers/histogram/qt/tests/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/glue/viewers/histogram/qt/tests/data/histogram_v0.glu b/glue/viewers/histogram/qt/tests/data/histogram_v0.glu deleted file mode 100644 index a481fefc2..000000000 --- a/glue/viewers/histogram/qt/tests/data/histogram_v0.glu +++ /dev/null @@ -1,397 +0,0 @@ -{ - "Component": { - "_type": "glue.core.component.Component", - "data": { - "_type": "numpy.ndarray", - "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGk4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDEwLCksIH0gICAgICAgICAgIAoAAAAAAAAAAAEAAAAAAAAAAgAAAAAAAAADAAAAAAAAAAQAAAAAAAAABQAAAAAAAAAGAAAAAAAAAAcAAAAAAAAACAAAAAAAAAAJAAAAAAAAAA==" - }, - "units": "" - }, - "Component_0": { - "_type": "glue.core.component.Component", - "data": { - "_type": "numpy.ndarray", - "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGk4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDEwLCksIH0gICAgICAgICAgIAoAAAAAAAAAAAIAAAAAAAAABAAAAAAAAAAGAAAAAAAAAAgAAAAAAAAACgAAAAAAAAAMAAAAAAAAAA4AAAAAAAAAEAAAAAAAAAASAAAAAAAAAA==" - }, - "units": "" - }, - "CoordinateComponent": { - "_type": "glue.core.component.CoordinateComponent", - "axis": 0, - "world": false - }, - "CoordinateComponentLink": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "World 0" - ], - "index": 0, - "pix2world": false, - "to": [ - "Pixel Axis 0 [x]" - ] - }, - "CoordinateComponentLink_0": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "Pixel Axis 0 [x]" - ], - "index": 0, - "pix2world": true, - "to": [ - "World 0" - ] - }, - "CoordinateComponent_0": { - "_type": "glue.core.component.CoordinateComponent", - "axis": 0, - "world": true - }, - "Coordinates": { - "_type": "glue.core.coordinates.Coordinates" - }, - "DataCollection": { - "_protocol": 3, - "_type": "glue.core.data_collection.DataCollection", - "cids": [ - "a", - "Pixel Axis 0 [x]", - "World 0", - "b" - ], - "components": [ - "Component", - "CoordinateComponent", - "CoordinateComponent_0", - "Component_0" - ], - "data": [ - "data" - ], - "groups": [ - "Subset 1_0" - ], - "links": [ - "CoordinateComponentLink", - "CoordinateComponentLink_0" - ], - "subset_group_count": 1 - }, - "HistogramWidget": { - "_type": "glue.viewers.histogram.qt.viewer_widget.HistogramWidget", - "layers": [ - { - "_type": "glue.viewers.histogram.layer_artist.HistogramLayerArtist", - "hi": 9, - "layer": "data", - "lo": 0, - "nbins": 6.0, - "visible": true, - "xlog": false, - "zorder": 1 - }, - { - "_type": "glue.viewers.histogram.layer_artist.HistogramLayerArtist", - "hi": 9, - "layer": "Subset 1", - "lo": 0, - "nbins": 6.0, - "visible": false, - "xlog": false, - "zorder": 2 - } - ], - "pos": [ - 0, - 0 - ], - "properties": { - "autoscale": true, - "component": "a", - "cumulative": false, - "nbins": 6.0, - "normed": false, - "xlog": false, - "xmax": 9.0, - "xmin": 0.0, - "ylog": false - }, - "session": "Session", - "size": [ - 600, - 400 - ] - }, - "HistogramWidget_0": { - "_type": "glue.viewers.histogram.qt.viewer_widget.HistogramWidget", - "layers": [ - { - "_type": "glue.viewers.histogram.layer_artist.HistogramLayerArtist", - "hi": 16.0, - "layer": "data", - "lo": 2.0, - "nbins": 8.0, - "visible": true, - "xlog": false, - "zorder": 1 - }, - { - "_type": "glue.viewers.histogram.layer_artist.HistogramLayerArtist", - "hi": 16.0, - "layer": "Subset 1", - "lo": 2.0, - "nbins": 8.0, - "visible": true, - "xlog": false, - "zorder": 2 - } - ], - "pos": [ - 502, - 362 - ], - "properties": { - "autoscale": true, - "component": "b", - "cumulative": false, - "nbins": 8.0, - "normed": false, - "xlog": false, - "xmax": 16.0, - "xmin": 2.0, - "ylog": false - }, - "session": "Session", - "size": [ - 600, - 400 - ] - }, - "HistogramWidget_1": { - "_type": "glue.viewers.histogram.qt.viewer_widget.HistogramWidget", - "layers": [ - { - "_type": "glue.viewers.histogram.layer_artist.HistogramLayerArtist", - "hi": 9, - "layer": "data", - "lo": 0, - "nbins": 10.0, - "visible": true, - "xlog": false, - "zorder": 1 - }, - { - "_type": "glue.viewers.histogram.layer_artist.HistogramLayerArtist", - "hi": 9, - "layer": "Subset 1", - "lo": 0, - "nbins": 10.0, - "visible": true, - "xlog": false, - "zorder": 2 - } - ], - "pos": [ - 502, - 0 - ], - "properties": { - "autoscale": true, - "component": "a", - "cumulative": false, - "nbins": 10.0, - "normed": true, - "xlog": false, - "xmax": 9.0, - "xmin": 0.0, - "ylog": true - }, - "session": "Session", - "size": [ - 600, - 400 - ] - }, - "HistogramWidget_2": { - "_type": "glue.viewers.histogram.qt.viewer_widget.HistogramWidget", - "layers": [ - { - "_type": "glue.viewers.histogram.layer_artist.HistogramLayerArtist", - "hi": 10.0, - "layer": "data", - "lo": -1.0, - "nbins": 4.0, - "visible": true, - "xlog": false, - "zorder": 1 - }, - { - "_type": "glue.viewers.histogram.layer_artist.HistogramLayerArtist", - "hi": 10.0, - "layer": "Subset 1", - "lo": -1.0, - "nbins": 4.0, - "visible": true, - "xlog": false, - "zorder": 2 - } - ], - "pos": [ - 0, - 362 - ], - "properties": { - "autoscale": true, - "component": "a", - "cumulative": true, - "nbins": 4.0, - "normed": false, - "xlog": false, - "xmax": 10.0, - "xmin": -1.0, - "ylog": false - }, - "session": "Session", - "size": [ - 600, - 400 - ] - }, - "Pixel Axis 0 [x]": { - "_type": "glue.core.component_id.PixelComponentID", - "axis": 0, - "hidden": true, - "label": "Pixel Axis 0 [x]" - }, - "RangeSubsetState": { - "_type": "glue.core.subset.RangeSubsetState", - "att": "a", - "hi": 4.5, - "lo": 1.5 - }, - "Session": { - "_type": "glue.core.session.Session" - }, - "Subset 1": { - "_type": "glue.core.subset_group.GroupedSubset", - "group": "Subset 1_0", - "style": { - "_type": "glue.core.visual.VisualAttributes", - "alpha": 0.5, - "color": "#e31a1c", - "linestyle": "solid", - "linewidth": 1, - "marker": "o", - "markersize": 7 - } - }, - "Subset 1_0": { - "_type": "glue.core.subset_group.SubsetGroup", - "label": "Subset 1", - "state": "RangeSubsetState", - "style": { - "_type": "glue.core.visual.VisualAttributes", - "alpha": 0.5, - "color": "#e31a1c", - "linestyle": "solid", - "linewidth": 1, - "marker": "o", - "markersize": 7 - }, - "subsets": [ - "Subset 1" - ] - }, - "World 0": { - "_type": "glue.core.component_id.ComponentID", - "hidden": true, - "label": "World 0" - }, - "__main__": { - "_type": "glue.app.qt.application.GlueApplication", - "data": "DataCollection", - "plugins": [ - "glue_vispy_viewers.scatter", - "glue.plugins.tools.spectrum_tool", - "glue.viewers.table", - "glue.viewers.histogram", - "glue.viewers.scatter", - "glue.viewers.image", - "glue.plugins.tools.pv_slicer", - "glue.plugins.coordinate_helpers", - "glue.plugins.exporters.plotly", - "glue.plugins.export_d3po", - "glue.core.data_exporters", - "glue_h5part", - "glue_vispy_viewers.volume" - ], - "session": "Session", - "tab_names": [ - "Tab 1" - ], - "viewers": [ - [ - "HistogramWidget", - "HistogramWidget_0", - "HistogramWidget_1", - "HistogramWidget_2" - ] - ] - }, - "a": { - "_type": "glue.core.component_id.ComponentID", - "hidden": false, - "label": "a" - }, - "b": { - "_type": "glue.core.component_id.ComponentID", - "hidden": false, - "label": "b" - }, - "data": { - "_key_joins": [], - "_protocol": 5, - "_type": "glue.core.data.Data", - "components": [ - [ - "a", - "Component" - ], - [ - "Pixel Axis 0 [x]", - "CoordinateComponent" - ], - [ - "World 0", - "CoordinateComponent_0" - ], - [ - "b", - "Component_0" - ] - ], - "coords": "Coordinates", - "label": "data", - "primary_owner": [ - "a", - "Pixel Axis 0 [x]", - "World 0", - "b" - ], - "style": { - "_type": "glue.core.visual.VisualAttributes", - "alpha": 0.8, - "color": "0.35", - "linestyle": "solid", - "linewidth": 1, - "marker": "o", - "markersize": 3 - }, - "subsets": [ - "Subset 1" - ], - "uuid": "103f82ee-7731-4816-b207-164b683762d2" - } -} \ No newline at end of file diff --git a/glue/viewers/histogram/qt/tests/data/histogram_v1.glu b/glue/viewers/histogram/qt/tests/data/histogram_v1.glu deleted file mode 100644 index 973a8ae58..000000000 --- a/glue/viewers/histogram/qt/tests/data/histogram_v1.glu +++ /dev/null @@ -1,516 +0,0 @@ -{ - "CallbackList": { - "_type": "glue.external.echo.list.CallbackList", - "values": [ - "HistogramLayerState", - "HistogramLayerState_0" - ] - }, - "CallbackList_0": { - "_type": "glue.external.echo.list.CallbackList", - "values": [ - "HistogramLayerState_1", - "HistogramLayerState_2" - ] - }, - "CallbackList_1": { - "_type": "glue.external.echo.list.CallbackList", - "values": [ - "HistogramLayerState_3", - "HistogramLayerState_4" - ] - }, - "CallbackList_2": { - "_type": "glue.external.echo.list.CallbackList", - "values": [ - "HistogramLayerState_5", - "HistogramLayerState_6" - ] - }, - "Component": { - "_type": "glue.core.component.Component", - "data": { - "_type": "numpy.ndarray", - "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGk4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDEwLCksIH0gICAgICAgICAgIAoAAAAAAAAAAAEAAAAAAAAAAgAAAAAAAAADAAAAAAAAAAQAAAAAAAAABQAAAAAAAAAGAAAAAAAAAAcAAAAAAAAACAAAAAAAAAAJAAAAAAAAAA==" - }, - "units": "" - }, - "Component_0": { - "_type": "glue.core.component.Component", - "data": { - "_type": "numpy.ndarray", - "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGk4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDEwLCksIH0gICAgICAgICAgIAoAAAAAAAAAAAIAAAAAAAAABAAAAAAAAAAGAAAAAAAAAAgAAAAAAAAACgAAAAAAAAAMAAAAAAAAAA4AAAAAAAAAEAAAAAAAAAASAAAAAAAAAA==" - }, - "units": "" - }, - "CoordinateComponent": { - "_type": "glue.core.component.CoordinateComponent", - "axis": 0, - "world": false - }, - "CoordinateComponentLink": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "Pixel Axis 0 [x]" - ], - "index": 0, - "pix2world": true, - "to": [ - "World 0" - ] - }, - "CoordinateComponentLink_0": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "World 0" - ], - "index": 0, - "pix2world": false, - "to": [ - "Pixel Axis 0 [x]" - ] - }, - "CoordinateComponentLink_1": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "World 0" - ], - "index": 0, - "pix2world": false, - "to": [ - "Pixel Axis 0 [x]" - ] - }, - "CoordinateComponentLink_2": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "Pixel Axis 0 [x]" - ], - "index": 0, - "pix2world": true, - "to": [ - "World 0" - ] - }, - "CoordinateComponent_0": { - "_type": "glue.core.component.CoordinateComponent", - "axis": 0, - "world": true - }, - "Coordinates": { - "_type": "glue.core.coordinates.Coordinates" - }, - "DataCollection": { - "_protocol": 3, - "_type": "glue.core.data_collection.DataCollection", - "cids": [ - "a", - "Pixel Axis 0 [x]", - "World 0", - "b" - ], - "components": [ - "Component", - "CoordinateComponent", - "CoordinateComponent_0", - "Component_0" - ], - "data": [ - "data" - ], - "groups": [ - "Subset 1" - ], - "links": [ - "CoordinateComponentLink", - "CoordinateComponentLink_0", - "CoordinateComponentLink_1", - "CoordinateComponentLink_2" - ], - "subset_group_count": 1 - }, - "HistogramLayerState": { - "_type": "glue.viewers.histogram.state.HistogramLayerState", - "values": { - "alpha": 0.8, - "color": "st__0.35", - "layer": "data", - "visible": true, - "zorder": 2 - } - }, - "HistogramLayerState_0": { - "_type": "glue.viewers.histogram.state.HistogramLayerState", - "values": { - "alpha": 0.5, - "color": "st__#e31a1c", - "layer": "Subset 1_0", - "visible": false, - "zorder": 3 - } - }, - "HistogramLayerState_1": { - "_type": "glue.viewers.histogram.state.HistogramLayerState", - "values": { - "alpha": 0.8, - "color": "st__0.35", - "layer": "data", - "visible": true, - "zorder": 2 - } - }, - "HistogramLayerState_2": { - "_type": "glue.viewers.histogram.state.HistogramLayerState", - "values": { - "alpha": 0.5, - "color": "st__#e31a1c", - "layer": "Subset 1_0", - "visible": true, - "zorder": 3 - } - }, - "HistogramLayerState_3": { - "_type": "glue.viewers.histogram.state.HistogramLayerState", - "values": { - "alpha": 0.8, - "color": "st__0.35", - "layer": "data", - "visible": true, - "zorder": 2 - } - }, - "HistogramLayerState_4": { - "_type": "glue.viewers.histogram.state.HistogramLayerState", - "values": { - "alpha": 0.5, - "color": "st__#e31a1c", - "layer": "Subset 1_0", - "visible": true, - "zorder": 3 - } - }, - "HistogramLayerState_5": { - "_type": "glue.viewers.histogram.state.HistogramLayerState", - "values": { - "alpha": 0.8, - "color": "st__0.35", - "layer": "data", - "visible": true, - "zorder": 2 - } - }, - "HistogramLayerState_6": { - "_type": "glue.viewers.histogram.state.HistogramLayerState", - "values": { - "alpha": 0.5, - "color": "st__#e31a1c", - "layer": "Subset 1_0", - "visible": true, - "zorder": 3 - } - }, - "HistogramViewer": { - "_protocol": 1, - "_type": "glue.viewers.histogram.qt.data_viewer.HistogramViewer", - "layers": [ - { - "_type": "glue.viewers.histogram.layer_artist.HistogramLayerArtist", - "state": "HistogramLayerState" - }, - { - "_type": "glue.viewers.histogram.layer_artist.HistogramLayerArtist", - "state": "HistogramLayerState_0" - } - ], - "pos": [ - 664, - 475 - ], - "session": "Session", - "size": [ - 663, - 473 - ], - "state": { - "values": { - "common_n_bin": true, - "cumulative": false, - "hist_n_bin": 6, - "hist_x_max": 9.0, - "hist_x_min": 0.0, - "layers": "CallbackList", - "normalize": false, - "x_att": "a", - "x_log": false, - "x_max": 9.0, - "x_min": 0.0, - "y_log": false, - "y_max": 2.4, - "y_min": 0 - } - } - }, - "HistogramViewer_0": { - "_protocol": 1, - "_type": "glue.viewers.histogram.qt.data_viewer.HistogramViewer", - "layers": [ - { - "_type": "glue.viewers.histogram.layer_artist.HistogramLayerArtist", - "state": "HistogramLayerState_1" - }, - { - "_type": "glue.viewers.histogram.layer_artist.HistogramLayerArtist", - "state": "HistogramLayerState_2" - } - ], - "pos": [ - 0, - 475 - ], - "session": "Session", - "size": [ - 664, - 473 - ], - "state": { - "values": { - "common_n_bin": true, - "cumulative": false, - "hist_n_bin": 8, - "hist_x_max": 16.0, - "hist_x_min": 2.0, - "layers": "CallbackList_0", - "normalize": false, - "x_att": "b", - "x_log": false, - "x_max": 16.0, - "x_min": 2.0, - "y_log": false, - "y_max": 1.2, - "y_min": 0 - } - } - }, - "HistogramViewer_1": { - "_protocol": 1, - "_type": "glue.viewers.histogram.qt.data_viewer.HistogramViewer", - "layers": [ - { - "_type": "glue.viewers.histogram.layer_artist.HistogramLayerArtist", - "state": "HistogramLayerState_3" - }, - { - "_type": "glue.viewers.histogram.layer_artist.HistogramLayerArtist", - "state": "HistogramLayerState_4" - } - ], - "pos": [ - 664, - 0 - ], - "session": "Session", - "size": [ - 663, - 475 - ], - "state": { - "values": { - "common_n_bin": true, - "cumulative": false, - "hist_n_bin": 10, - "hist_x_max": 9.0, - "hist_x_min": 0.0, - "layers": "CallbackList_1", - "normalize": true, - "x_att": "a", - "x_log": false, - "x_max": 9.0, - "x_min": 0.0, - "y_log": true, - "y_max": 0.7407407407407407, - "y_min": 0.01111111111111111 - } - } - }, - "HistogramViewer_2": { - "_protocol": 1, - "_type": "glue.viewers.histogram.qt.data_viewer.HistogramViewer", - "layers": [ - { - "_type": "glue.viewers.histogram.layer_artist.HistogramLayerArtist", - "state": "HistogramLayerState_5" - }, - { - "_type": "glue.viewers.histogram.layer_artist.HistogramLayerArtist", - "state": "HistogramLayerState_6" - } - ], - "pos": [ - 0, - 0 - ], - "session": "Session", - "size": [ - 664, - 475 - ], - "state": { - "values": { - "common_n_bin": true, - "cumulative": true, - "hist_n_bin": 4, - "hist_x_max": 10.0, - "hist_x_min": -1.0, - "layers": "CallbackList_2", - "normalize": false, - "x_att": "a", - "x_log": false, - "x_max": 10.0, - "x_min": -1.0, - "y_log": false, - "y_max": 12.0, - "y_min": 0 - } - } - }, - "Pixel Axis 0 [x]": { - "_type": "glue.core.component_id.PixelComponentID", - "axis": 0, - "hidden": true, - "label": "Pixel Axis 0 [x]" - }, - "RangeSubsetState": { - "_type": "glue.core.subset.RangeSubsetState", - "att": "a", - "hi": 4.5, - "lo": 1.5 - }, - "Session": { - "_type": "glue.core.session.Session" - }, - "Subset 1": { - "_type": "glue.core.subset_group.SubsetGroup", - "label": "Subset 1", - "state": "RangeSubsetState", - "style": { - "_type": "glue.core.visual.VisualAttributes", - "alpha": 0.5, - "color": "#e31a1c", - "linestyle": "solid", - "linewidth": 1, - "marker": "o", - "markersize": 7 - }, - "subsets": [ - "Subset 1_0" - ] - }, - "Subset 1_0": { - "_type": "glue.core.subset_group.GroupedSubset", - "group": "Subset 1", - "style": { - "_type": "glue.core.visual.VisualAttributes", - "alpha": 0.5, - "color": "#e31a1c", - "linestyle": "solid", - "linewidth": 1, - "marker": "o", - "markersize": 7 - } - }, - "World 0": { - "_type": "glue.core.component_id.ComponentID", - "hidden": true, - "label": "World 0" - }, - "__main__": { - "_type": "glue.app.qt.application.GlueApplication", - "data": "DataCollection", - "plugins": [ - "glue_vispy_viewers.scatter", - "glue.plugins.coordinate_helpers", - "glue.plugins.exporters.plotly", - "glue.viewers.table", - "glue.core.data_exporters", - "glue_medical", - "glue.plugins.tools.pv_slicer", - "specviz.app", - "glue.viewers.scatter", - "glue.viewers.histogram", - "glue_vispy_viewers.volume", - "glue.plugins.tools.spectrum_tool", - "glue.plugins.export_d3po", - "glue.viewers.image" - ], - "session": "Session", - "tab_names": [ - "Tab 1" - ], - "viewers": [ - [ - "HistogramViewer", - "HistogramViewer_0", - "HistogramViewer_1", - "HistogramViewer_2" - ] - ] - }, - "a": { - "_type": "glue.core.component_id.ComponentID", - "hidden": false, - "label": "a" - }, - "b": { - "_type": "glue.core.component_id.ComponentID", - "hidden": false, - "label": "b" - }, - "data": { - "_key_joins": [], - "_protocol": 5, - "_type": "glue.core.data.Data", - "components": [ - [ - "a", - "Component" - ], - [ - "Pixel Axis 0 [x]", - "CoordinateComponent" - ], - [ - "World 0", - "CoordinateComponent_0" - ], - [ - "b", - "Component_0" - ] - ], - "coords": "Coordinates", - "label": "data", - "primary_owner": [ - "a", - "Pixel Axis 0 [x]", - "World 0", - "b" - ], - "style": { - "_type": "glue.core.visual.VisualAttributes", - "alpha": 0.8, - "color": "0.35", - "linestyle": "solid", - "linewidth": 1, - "marker": "o", - "markersize": 3 - }, - "subsets": [ - "Subset 1_0" - ], - "uuid": "103f82ee-7731-4816-b207-164b683762d2" - } -} diff --git a/glue/viewers/histogram/qt/tests/test_data_viewer.py b/glue/viewers/histogram/qt/tests/test_data_viewer.py deleted file mode 100644 index 14482dc1f..000000000 --- a/glue/viewers/histogram/qt/tests/test_data_viewer.py +++ /dev/null @@ -1,743 +0,0 @@ -# pylint: disable=I0011,W0613,W0201,W0212,E1101,E1103 - -import os -from collections import Counter - -import pytest -import numpy as np - -from numpy.testing import assert_equal, assert_allclose - -from glue.core.message import SubsetUpdateMessage -from glue.core import HubListener, Data -from glue.core.roi import XRangeROI, RectangularROI -from glue.core.subset import SubsetState, RangeSubsetState, CategoricalROISubsetState, RoiSubsetState -from glue import core -from glue.app.qt import GlueApplication -from glue.core.component_id import ComponentID -from glue.utils.qt import combo_as_string, process_events -from glue.viewers.matplotlib.qt.tests.test_data_viewer import BaseTestMatplotlibDataViewer -from glue.core.state import GlueUnSerializer -from glue.app.qt.layer_tree_widget import LayerTreeWidget -from glue.tests.helpers import requires_matplotlib_ge_22 - -from ..data_viewer import HistogramViewer - -DATA = os.path.join(os.path.dirname(__file__), 'data') - - -class TestHistogramCommon(BaseTestMatplotlibDataViewer): - def init_data(self): - return Data(label='d1', x=[3.4, 2.3, -1.1, 0.3], y=['a', 'b', 'c', 'a']) - viewer_cls = HistogramViewer - - -class TestHistogramViewer(object): - - def setup_method(self, method): - - self.data = Data(label='d1', x=[3.4, 2.3, -1.1, 0.3], y=['a', 'b', 'c', 'a']) - - self.app = GlueApplication() - self.session = self.app.session - self.hub = self.session.hub - - self.data_collection = self.session.data_collection - self.data_collection.append(self.data) - - self.viewer = self.app.new_data_viewer(HistogramViewer) - - def teardown_method(self, method): - self.viewer.close() - self.viewer = None - self.app.close() - self.app = None - - def test_basic(self): - - viewer_state = self.viewer.state - - # Check defaults when we add data - self.viewer.add_data(self.data) - - assert combo_as_string(self.viewer.options_widget().ui.combosel_x_att) == 'Main components:x:y:Coordinate components:Pixel Axis 0 [x]' - - assert viewer_state.x_att is self.data.id['x'] - assert viewer_state.x_min == -1.1 - assert viewer_state.x_max == 3.4 - assert viewer_state.y_min == 0.0 - assert viewer_state.y_max == 1.2 - - assert viewer_state.hist_x_min == -1.1 - assert viewer_state.hist_x_max == 3.4 - assert viewer_state.hist_n_bin == 15 - - assert not viewer_state.cumulative - assert not viewer_state.normalize - - assert not viewer_state.x_log - assert not viewer_state.y_log - - assert len(viewer_state.layers) == 1 - - # Change to categorical component and check new values - - viewer_state.x_att = self.data.id['y'] - - assert viewer_state.x_min == -0.5 - assert viewer_state.x_max == 2.5 - assert viewer_state.y_min == 0.0 - assert viewer_state.y_max == 2.4 - - assert viewer_state.hist_x_min == -0.5 - assert viewer_state.hist_x_max == 2.5 - assert viewer_state.hist_n_bin == 3 - - assert not viewer_state.cumulative - assert not viewer_state.normalize - - assert not viewer_state.x_log - assert not viewer_state.y_log - - def test_log_labels(self): - - # Regression test to make sure the labels are correctly changed to log - # when the x-axis is in log space. - - viewer_state = self.viewer.state - data = Data(x=np.logspace(-5, 5, 10000)) - self.data_collection.append(data) - - self.viewer.add_data(data) - viewer_state.x_log = True - - process_events() - - labels = [x.get_text() for x in self.viewer.axes.xaxis.get_ticklabels()] - - # Different Matplotlib versions return slightly different - # labels, but the ones below should be present regardless - # of Matplotlib version. - expected_present = ['$\\mathdefault{10^{-5}}$', - '$\\mathdefault{10^{-3}}$', - '$\\mathdefault{10^{-1}}$', - '$\\mathdefault{10^{1}}$', - '$\\mathdefault{10^{3}}$', - '$\\mathdefault{10^{5}}$'] - - for label in expected_present: - assert label in labels - - def test_flip(self): - - viewer_state = self.viewer.state - - self.viewer.add_data(self.data) - - assert viewer_state.x_min == -1.1 - assert viewer_state.x_max == 3.4 - - self.viewer.options_widget().button_flip_x.click() - - assert viewer_state.x_min == 3.4 - assert viewer_state.x_max == -1.1 - - def test_remove_data(self): - self.viewer.add_data(self.data) - assert combo_as_string(self.viewer.options_widget().ui.combosel_x_att) == 'Main components:x:y:Coordinate components:Pixel Axis 0 [x]' - self.data_collection.remove(self.data) - assert combo_as_string(self.viewer.options_widget().ui.combosel_x_att) == '' - - def test_update_component_updates_title(self): - self.viewer.add_data(self.data) - assert self.viewer.windowTitle() == '1D Histogram' - self.viewer.state.x_att = self.data.id['y'] - assert self.viewer.windowTitle() == '1D Histogram' - - def test_combo_updates_with_component_add(self): - self.viewer.add_data(self.data) - self.data.add_component([3, 4, 1, 2], 'z') - assert self.viewer.state.x_att is self.data.id['x'] - assert combo_as_string(self.viewer.options_widget().ui.combosel_x_att) == 'Main components:x:y:z:Coordinate components:Pixel Axis 0 [x]' - - def test_nonnumeric_first_component(self): - # regression test for #208. Shouldn't complain if - # first component is non-numerical - data = core.Data() - data.add_component(['a', 'b', 'c'], label='c1') - data.add_component([1, 2, 3], label='c2') - self.data_collection.append(data) - self.viewer.add_data(data) - - def test_nan_component(self): - # regression test for case when all values are NaN in a component - data = core.Data() - data.add_component([np.nan, np.nan, np.nan], label='c1') - self.data_collection.append(data) - self.viewer.add_data(data) - - def test_histogram_values(self): - - # Check the actual values of the histograms - - viewer_state = self.viewer.state - - self.viewer.add_data(self.data) - - # Numerical attribute - - viewer_state.hist_x_min = -5 - viewer_state.hist_x_max = 5 - viewer_state.hist_n_bin = 4 - - assert_allclose(self.viewer.state.y_max, 2.4) - - assert_allclose(self.viewer.layers[0].state.histogram[1], [0, 1, 2, 1]) - assert_allclose(self.viewer.layers[0].state.histogram[0], [-5, -2.5, 0, 2.5, 5]) - - cid = self.data.main_components[0] - self.data_collection.new_subset_group('subset 1', cid < 2) - - assert_allclose(self.viewer.layers[1].state.histogram[1], [0, 1, 1, 0]) - assert_allclose(self.viewer.layers[1].state.histogram[0], [-5, -2.5, 0, 2.5, 5]) - - viewer_state.normalize = True - - assert_allclose(self.viewer.state.y_max, 0.24) - assert_allclose(self.viewer.layers[0].state.histogram[1], [0, 0.1, 0.2, 0.1]) - assert_allclose(self.viewer.layers[0].state.histogram[0], [-5, -2.5, 0, 2.5, 5]) - assert_allclose(self.viewer.layers[1].state.histogram[1], [0, 0.2, 0.2, 0]) - assert_allclose(self.viewer.layers[1].state.histogram[0], [-5, -2.5, 0, 2.5, 5]) - - viewer_state.cumulative = True - - assert_allclose(self.viewer.state.y_max, 1.2) - assert_allclose(self.viewer.layers[0].state.histogram[1], [0, 0.25, 0.75, 1.0]) - assert_allclose(self.viewer.layers[0].state.histogram[0], [-5, -2.5, 0, 2.5, 5]) - assert_allclose(self.viewer.layers[1].state.histogram[1], [0, 0.5, 1.0, 1.0]) - assert_allclose(self.viewer.layers[1].state.histogram[0], [-5, -2.5, 0, 2.5, 5]) - - viewer_state.normalize = False - - assert_allclose(self.viewer.state.y_max, 4.8) - assert_allclose(self.viewer.layers[0].state.histogram[1], [0, 1, 3, 4]) - assert_allclose(self.viewer.layers[0].state.histogram[0], [-5, -2.5, 0, 2.5, 5]) - assert_allclose(self.viewer.layers[1].state.histogram[1], [0, 1, 2, 2]) - assert_allclose(self.viewer.layers[1].state.histogram[0], [-5, -2.5, 0, 2.5, 5]) - - viewer_state.cumulative = False - - # Categorical attribute - - viewer_state.x_att = self.data.id['y'] - - formatter = self.viewer.axes.xaxis.get_major_formatter() - xlabels = [formatter.format_data(pos) for pos in range(3)] - assert xlabels == ['a', 'b', 'c'] - - assert_allclose(self.viewer.state.y_max, 2.4) - assert_allclose(self.viewer.layers[0].state.histogram[1], [2, 1, 1]) - assert_allclose(self.viewer.layers[0].state.histogram[0], [-0.5, 0.5, 1.5, 2.5]) - assert_allclose(self.viewer.layers[1].state.histogram[1], [1, 0, 1]) - assert_allclose(self.viewer.layers[1].state.histogram[0], [-0.5, 0.5, 1.5, 2.5]) - - viewer_state.normalize = True - - assert_allclose(self.viewer.state.y_max, 0.6) - assert_allclose(self.viewer.layers[0].state.histogram[1], [0.5, 0.25, 0.25]) - assert_allclose(self.viewer.layers[0].state.histogram[0], [-0.5, 0.5, 1.5, 2.5]) - assert_allclose(self.viewer.layers[1].state.histogram[1], [0.5, 0, 0.5]) - assert_allclose(self.viewer.layers[1].state.histogram[0], [-0.5, 0.5, 1.5, 2.5]) - - viewer_state.cumulative = True - - assert_allclose(self.viewer.state.y_max, 1.2) - assert_allclose(self.viewer.layers[0].state.histogram[1], [0.5, 0.75, 1]) - assert_allclose(self.viewer.layers[0].state.histogram[0], [-0.5, 0.5, 1.5, 2.5]) - assert_allclose(self.viewer.layers[1].state.histogram[1], [0.5, 0.5, 1]) - assert_allclose(self.viewer.layers[1].state.histogram[0], [-0.5, 0.5, 1.5, 2.5]) - - viewer_state.normalize = False - - assert_allclose(self.viewer.state.y_max, 4.8) - assert_allclose(self.viewer.layers[0].state.histogram[1], [2, 3, 4]) - assert_allclose(self.viewer.layers[0].state.histogram[0], [-0.5, 0.5, 1.5, 2.5]) - assert_allclose(self.viewer.layers[1].state.histogram[1], [1, 1, 2]) - assert_allclose(self.viewer.layers[1].state.histogram[0], [-0.5, 0.5, 1.5, 2.5]) - - # TODO: add tests for log - - def test_apply_roi(self): - - # Check that when doing an ROI selection, the ROI clips to the bin edges - # outside the selection - - viewer_state = self.viewer.state - - self.viewer.add_data(self.data) - - viewer_state.hist_x_min = -5 - viewer_state.hist_x_max = 5 - viewer_state.hist_n_bin = 4 - - roi = XRangeROI(-0.2, 0.1) - - assert len(self.viewer.layers) == 1 - - self.viewer.apply_roi(roi) - - assert len(self.viewer.layers) == 2 - - assert_allclose(self.viewer.layers[0].state.histogram[1], [0, 1, 2, 1]) - assert_allclose(self.viewer.layers[1].state.histogram[1], [0, 1, 2, 0]) - - assert_allclose(self.data.subsets[0].to_mask(), [0, 1, 1, 1]) - - state = self.data.subsets[0].subset_state - assert isinstance(state, RangeSubsetState) - - assert state.lo == -2.5 - assert state.hi == 2.5 - - # TODO: add a similar test in log space - - def test_apply_roi_categorical(self): - - # Check that when doing an ROI selection, the ROI clips to the bin edges - # outside the selection - - viewer_state = self.viewer.state - - self.viewer.add_data(self.data) - - viewer_state.x_att = self.data.id['y'] - - roi = XRangeROI(0.3, 0.9) - - assert len(self.viewer.layers) == 1 - - self.viewer.apply_roi(roi) - - assert len(self.viewer.layers) == 2 - - assert_allclose(self.viewer.layers[0].state.histogram[1], [2, 1, 1]) - assert_allclose(self.viewer.layers[1].state.histogram[1], [2, 1, 0]) - - assert_allclose(self.data.subsets[0].to_mask(), [1, 1, 0, 1]) - - state = self.data.subsets[0].subset_state - assert isinstance(state, CategoricalROISubsetState) - - assert_equal(state.roi.categories, ['a', 'b']) - - def test_apply_roi_empty(self): - # Make sure that doing an ROI selection on an empty viewer doesn't - # produce error messsages - roi = XRangeROI(-0.2, 0.1) - self.viewer.apply_roi(roi) - - def test_axes_labels(self): - - viewer_state = self.viewer.state - - self.viewer.add_data(self.data) - - assert self.viewer.axes.get_xlabel() == 'x' - assert self.viewer.axes.get_ylabel() == 'Number' - - viewer_state.x_log = True - - assert self.viewer.axes.get_xlabel() == 'x' - assert self.viewer.axes.get_ylabel() == 'Number' - - viewer_state.x_att = self.data.id['y'] - - assert self.viewer.axes.get_xlabel() == 'y' - assert self.viewer.axes.get_ylabel() == 'Number' - - viewer_state.normalize = True - - assert self.viewer.axes.get_xlabel() == 'y' - assert self.viewer.axes.get_ylabel() == 'Normalized number' - - viewer_state.normalize = False - viewer_state.cumulative = True - - assert self.viewer.axes.get_xlabel() == 'y' - assert self.viewer.axes.get_ylabel() == 'Number' - - def test_y_min_y_max(self): - - # Regression test for a bug that caused y_max to not be set correctly - # when multiple subsets were present and after turning on normalization - # after switching to a different attribute from that used to make the - # selection. - - viewer_state = self.viewer.state - self.viewer.add_data(self.data) - - self.data.add_component([3.4, 3.5, 10.2, 20.3], 'z') - - viewer_state.x_att = self.data.id['x'] - - cid = self.data.main_components[0] - self.data_collection.new_subset_group('subset 1', cid < 1) - - cid = self.data.main_components[0] - self.data_collection.new_subset_group('subset 2', cid < 2) - - cid = self.data.main_components[0] - self.data_collection.new_subset_group('subset 3', cid < 3) - - assert_allclose(self.viewer.state.y_min, 0) - assert_allclose(self.viewer.state.y_max, 1.2) - - viewer_state.x_att = self.data.id['z'] - - assert_allclose(self.viewer.state.y_min, 0) - assert_allclose(self.viewer.state.y_max, 2.4) - - viewer_state.normalize = True - - assert_allclose(self.viewer.state.y_min, 0) - assert_allclose(self.viewer.state.y_max, 0.5325443786982249) - - def test_update_when_limits_unchanged(self): - - # Regression test for glue-viz/glue#1010 - this bug caused histograms - # to not be recomputed if the attribute changed but the limits and - # number of bins did not. - - viewer_state = self.viewer.state - - self.viewer.add_data(self.data) - - viewer_state.x_att = self.data.id['y'] - viewer_state.hist_x_min = -10.1 - viewer_state.hist_x_max = +10 - viewer_state.hist_n_bin = 5 - - assert_allclose(self.viewer.layers[0].state.histogram[1], [0, 0, 3, 1, 0]) - - viewer_state.x_att = self.data.id['x'] - viewer_state.hist_x_min = -10.1 - viewer_state.hist_x_max = +10 - viewer_state.hist_n_bin = 5 - - assert_allclose(self.viewer.layers[0].state.histogram[1], [0, 0, 2, 2, 0]) - - viewer_state.x_att = self.data.id['y'] - - assert_allclose(self.viewer.layers[0].state.histogram[1], [0, 0, 3, 1, 0]) - - viewer_state.x_att = self.data.id['x'] - - assert_allclose(self.viewer.layers[0].state.histogram[1], [0, 0, 2, 2, 0]) - - def test_component_replaced(self): - - # regression test for 508 - if a component ID is replaced, we should - # make sure that the component ID is selected if the old component ID - # was selected - - self.viewer.add_data(self.data) - self.viewer.state.x_att = self.data.id['x'] - test = ComponentID('test') - self.data.update_id(self.viewer.state.x_att, test) - assert self.viewer.state.x_att is test - assert combo_as_string(self.viewer.options_widget().ui.combosel_x_att) == 'Main components:test:y:Coordinate components:Pixel Axis 0 [x]' - - def test_nbin_override_persists_over_numerical_attribute_change(self): - - # regression test for #398 - - self.data.add_component([3, 4, 1, 2], 'z') - - self.viewer.add_data(self.data) - self.viewer.state.x_att = self.data.id['x'] - self.viewer.state.hist_n_bin = 7 - self.viewer.state.x_att = self.data.id['z'] - assert self.viewer.state.hist_n_bin == 7 - - @pytest.mark.parametrize('protocol', [0, 1]) - def test_session_back_compat(self, protocol): - - filename = os.path.join(DATA, 'histogram_v{0}.glu'.format(protocol)) - - with open(filename, 'r') as f: - session = f.read() - - state = GlueUnSerializer.loads(session) - - ga = state.object('__main__') - - dc = ga.session.data_collection - - assert len(dc) == 1 - - assert dc[0].label == 'data' - - viewer1 = ga.viewers[0][0] - assert len(viewer1.state.layers) == 2 - assert viewer1.state.x_att is dc[0].id['a'] - assert_allclose(viewer1.state.x_min, 0) - assert_allclose(viewer1.state.x_max, 9) - assert_allclose(viewer1.state.y_min, 0) - assert_allclose(viewer1.state.y_max, 2.4) - assert_allclose(viewer1.state.hist_x_min, 0) - assert_allclose(viewer1.state.hist_x_max, 9) - assert_allclose(viewer1.state.hist_n_bin, 6) - assert not viewer1.state.x_log - assert not viewer1.state.y_log - assert viewer1.state.layers[0].visible - assert not viewer1.state.layers[1].visible - assert not viewer1.state.cumulative - assert not viewer1.state.normalize - - viewer2 = ga.viewers[0][1] - assert viewer2.state.x_att is dc[0].id['b'] - assert_allclose(viewer2.state.x_min, 2) - assert_allclose(viewer2.state.x_max, 16) - assert_allclose(viewer2.state.y_min, 0) - assert_allclose(viewer2.state.y_max, 1.2) - assert_allclose(viewer2.state.hist_x_min, 2) - assert_allclose(viewer2.state.hist_x_max, 16) - assert_allclose(viewer2.state.hist_n_bin, 8) - assert not viewer2.state.x_log - assert not viewer2.state.y_log - assert viewer2.state.layers[0].visible - assert viewer2.state.layers[1].visible - assert not viewer2.state.cumulative - assert not viewer2.state.normalize - - viewer3 = ga.viewers[0][2] - assert viewer3.state.x_att is dc[0].id['a'] - assert_allclose(viewer3.state.x_min, 0) - assert_allclose(viewer3.state.x_max, 9) - assert_allclose(viewer3.state.y_min, 0.01111111111111111) - assert_allclose(viewer3.state.y_max, 0.7407407407407407) - assert_allclose(viewer3.state.hist_x_min, 0) - assert_allclose(viewer3.state.hist_x_max, 9) - assert_allclose(viewer3.state.hist_n_bin, 10) - assert not viewer3.state.x_log - assert viewer3.state.y_log - assert viewer3.state.layers[0].visible - assert viewer3.state.layers[1].visible - assert not viewer3.state.cumulative - assert viewer3.state.normalize - - viewer4 = ga.viewers[0][3] - assert viewer4.state.x_att is dc[0].id['a'] - assert_allclose(viewer4.state.x_min, -1) - assert_allclose(viewer4.state.x_max, 10) - assert_allclose(viewer4.state.y_min, 0) - assert_allclose(viewer4.state.y_max, 12) - assert_allclose(viewer4.state.hist_x_min, -1) - assert_allclose(viewer4.state.hist_x_max, 10) - assert_allclose(viewer4.state.hist_n_bin, 4) - assert not viewer4.state.x_log - assert not viewer4.state.y_log - assert viewer4.state.layers[0].visible - assert viewer4.state.layers[1].visible - assert viewer4.state.cumulative - assert not viewer4.state.normalize - - ga.close() - - def test_apply_roi_single(self): - - # Regression test for a bug that caused mode.update to be called - # multiple times and resulted in all other viewers receiving many - # messages regarding subset updates (this occurred when multiple) - # datasets were present. - - layer_tree = LayerTreeWidget(session=self.session) - layer_tree.set_checkable(False) - layer_tree.setup(self.data_collection) - layer_tree.bind_selection_to_edit_subset() - - class Client(HubListener): - - def __init__(self, *args, **kwargs): - super(Client, self).__init__(*args, **kwargs) - self.count = Counter() - - def ping(self, message): - self.count[message.sender] += 1 - - def register_to_hub(self, hub): - hub.subscribe(self, SubsetUpdateMessage, handler=self.ping) - - d1 = Data(a=[1, 2, 3], label='d1') - d2 = Data(b=[1, 2, 3], label='d2') - d3 = Data(c=[1, 2, 3], label='d3') - d4 = Data(d=[1, 2, 3], label='d4') - - self.data_collection.append(d1) - self.data_collection.append(d2) - self.data_collection.append(d3) - self.data_collection.append(d4) - - client = Client() - client.register_to_hub(self.hub) - - self.viewer.add_data(d1) - self.viewer.add_data(d3) - - roi = XRangeROI(2.5, 3.5) - self.viewer.apply_roi(roi) - - for subset in client.count: - assert client.count[subset] == 1 - - @pytest.mark.filterwarnings('ignore:elementwise') - def test_datetime64_support(self, tmpdir): - - self.data.add_component(np.array([100, 200, 300, 400], dtype='M8[D]'), 't1') - self.viewer.add_data(self.data) - self.viewer.state.x_att = self.data.id['t1'] - - # Matplotlib deals with dates by converting them to the number of days - # since 01-01-0001, so we can check that the limits are correctly - # converted (and not 100 to 400) - assert self.viewer.axes.get_xlim() == (719263.0, 719563.0) - - # Apply an ROI selection in plotting coordinates - roi = XRangeROI(719313, 719513) - self.viewer.apply_roi(roi) - - # Check that the two middle elements are selected - assert_equal(self.data.subsets[0].to_mask(), [0, 1, 1, 0]) - - # Make sure that the Qt labels look ok - options = self.viewer.options_widget().ui - assert options.valuetext_x_min.text() == '1970-04-11' - assert options.valuetext_x_max.text() == '1971-02-05' - - # Make sure that we can set the xmin/xmax to a string date - assert_equal(self.viewer.state.x_min, np.datetime64('1970-04-11', 'D')) - options.valuetext_x_min.setText('1970-04-14') - options.valuetext_x_min.editingFinished.emit() - assert self.viewer.axes.get_xlim() == (719266.0, 719563.0) - assert_equal(self.viewer.state.x_min, np.datetime64('1970-04-14', 'D')) - - # Make sure that everything works fine after saving/reloading - filename = tmpdir.join('test_datetime64.glu').strpath - self.session.application.save_session(filename) - with open(filename, 'r') as f: - session = f.read() - state = GlueUnSerializer.loads(session) - ga = state.object('__main__') - viewer = ga.viewers[0][0] - options = viewer.options_widget().ui - - assert_equal(self.viewer.state.x_min, np.datetime64('1970-04-14', 'D')) - - assert options.valuetext_x_min.text() == '1970-04-14' - assert options.valuetext_x_max.text() == '1971-02-05' - - ga.close() - - @requires_matplotlib_ge_22 - def test_categorical_labels(self, tmpdir): - - # Fix a bug that caused labels on histograms of categorical variables - # to not be restored correctly after saving and reloading session - - self.viewer.add_data(self.data) - self.viewer.state.x_att = self.data.id['y'] - - self.viewer.figure.canvas.draw() - - assert [x.get_text() for x in self.viewer.axes.xaxis.get_ticklabels()] == ['', 'a', 'b', 'c', ''] - - # Make sure that everything works fine after saving/reloading - filename = tmpdir.join('test_categorical_labels.glu').strpath - self.session.application.save_session(filename) - with open(filename, 'r') as f: - session = f.read() - state = GlueUnSerializer.loads(session) - ga = state.object('__main__') - viewer = ga.viewers[0][0] - - viewer.figure.canvas.draw() - - assert [x.get_text() for x in viewer.axes.xaxis.get_ticklabels()] == ['', 'a', 'b', 'c', ''] - - ga.close() - - def test_legend(self): - from matplotlib.colors import to_hex - - viewer_state = self.viewer.state - - self.viewer.add_data(self.data) - self.viewer.state.legend.visible = True - - handles, labels, handler_dict = self.viewer.get_handles_legend() - assert len(handles) == 1 - assert labels[0] == 'd1' - - self.data_collection.new_subset_group('test', self.data.id['x'] > 1) - assert len(viewer_state.layers) == 2 - handles, labels, handler_dict = self.viewer.get_handles_legend() - assert len(handles) == 2 - assert labels[1] == 'test' - - assert to_hex(handles[1].get_facecolor()) == viewer_state.layers[1].color - - def test_redraw_empty_subset(self): - self.viewer.add_data(self.data) - self.data_collection.new_subset_group('redraw_empty', self.data.id['x'] > 1) - layer_artist = self.viewer.layers[-1] - subset = layer_artist.layer - subset.subset_state = SubsetState() - for artist in layer_artist.mpl_artists: - assert artist.get_height() == 0 - - -def test_with_dask_array(): - - # Regression test for a bug that caused the histogram to now work when - # making spatial selections on a cube represented as a dask array - - da = pytest.importorskip('dask.array') - - data = Data(x=da.arange(1000).reshape((10, 10, 10)), label='d1') - - app = GlueApplication() - session = app.session - hub = session.hub - - data_collection = session.data_collection - data_collection.append(data) - - viewer = app.new_data_viewer(HistogramViewer) - - viewer.add_data(data) - - viewer.state.hist_n_bin = 1 - - process_events(0.5) - - assert len(viewer.layers) == 1 - assert viewer.layers[0].enabled - - zid, yid, xid = data.pixel_component_ids - - subset_state = RoiSubsetState(xatt=xid, yatt=yid, - roi=RectangularROI(xmin=3.5, xmax=5.5, ymin=3.7, ymax=7.5)) - - data_collection.new_subset_group(subset_state=subset_state, label='subset') - - assert len(viewer.layers) == 2 - assert viewer.layers[0].enabled - assert viewer.layers[1].enabled - - assert viewer.state.layers[0].histogram[1][0] == 1000. - assert viewer.state.layers[1].histogram[1][0] == 80. - - viewer.close() - viewer = None - app.close() - app = None diff --git a/glue/viewers/histogram/qt/tests/test_python_export.py b/glue/viewers/histogram/qt/tests/test_python_export.py deleted file mode 100644 index 987fd0050..000000000 --- a/glue/viewers/histogram/qt/tests/test_python_export.py +++ /dev/null @@ -1,63 +0,0 @@ -from astropy.utils import NumpyRNGContext - -from glue.core import Data, DataCollection -from glue.app.qt.application import GlueApplication -from glue.viewers.histogram.qt import HistogramViewer -from glue.viewers.matplotlib.qt.tests.test_python_export import BaseTestExportPython, random_with_nan - - -class TestExportPython(BaseTestExportPython): - - def setup_method(self, method): - - with NumpyRNGContext(12345): - self.data = Data(**dict((name, random_with_nan(100, nan_index=idx + 1)) for idx, name in enumerate('abcdefgh'))) - self.data_collection = DataCollection([self.data]) - self.app = GlueApplication(self.data_collection) - self.viewer = self.app.new_data_viewer(HistogramViewer) - self.viewer.add_data(self.data) - self.viewer.state.x_att = self.data.id['a'] - - def teardown_method(self, method): - self.viewer.close() - self.viewer = None - self.app.close() - self.app = None - - def test_simple(self, tmpdir): - self.assert_same(tmpdir) - - def test_simple_visual(self, tmpdir): - self.viewer.state.layers[0].color = 'blue' - self.viewer.state.layers[0].alpha = 0.5 - self.assert_same(tmpdir) - - def test_simple_visual_legend(self, tmpdir): - self.viewer.state.legend.visible = True - self.viewer.state.layers[0].color = 'blue' - self.viewer.state.layers[0].alpha = 0.5 - self.assert_same(tmpdir) - - def test_cumulative(self, tmpdir): - self.viewer.state.cumulative = True - self.assert_same(tmpdir) - - def test_normalize(self, tmpdir): - self.viewer.state.normalize = True - self.assert_same(tmpdir) - - def test_subset(self, tmpdir): - self.data_collection.new_subset_group('mysubset', self.data.id['a'] > 0.5) - self.assert_same(tmpdir) - - def test_subset_legend(self, tmpdir): - self.viewer.state.legend.visible = True - self.data_collection.new_subset_group('mysubset', self.data.id['a'] > 0.5) - self.assert_same(tmpdir) - - def test_empty(self, tmpdir): - self.viewer.state.x_min = 10 - self.viewer.state.x_max = 11 - self.viewer.state.hist_x_min = 10 - self.viewer.state.hist_x_max = 11 - self.assert_same(tmpdir) diff --git a/glue/viewers/image/composite_array.py b/glue/viewers/image/composite_array.py index 1742c60fa..323bc3348 100644 --- a/glue/viewers/image/composite_array.py +++ b/glue/viewers/image/composite_array.py @@ -5,11 +5,10 @@ import numpy as np -from glue.config import colormaps +from glue.config import colormaps, stretches from matplotlib.colors import ColorConverter, Colormap -from astropy.visualization import (LinearStretch, SqrtStretch, AsinhStretch, - LogStretch, ManualInterval, ContrastBiasStretch) +from astropy.visualization import ManualInterval, ContrastBiasStretch __all__ = ['CompositeArray'] @@ -18,13 +17,6 @@ CMAP_SAMPLING = np.linspace(0, 1, 256) -STRETCHES = { - 'linear': LinearStretch, - 'sqrt': SqrtStretch, - 'arcsinh': AsinhStretch, - 'log': LogStretch -} - class CompositeArray(object): @@ -126,7 +118,7 @@ def __call__(self, bounds=None): interval = ManualInterval(*layer['clim']) contrast_bias = ContrastBiasStretch(layer['contrast'], layer['bias']) - stretch = STRETCHES[layer['stretch']]() + stretch = stretches.members[layer['stretch']] if callable(layer['array']): array = layer['array'](bounds=bounds) @@ -145,21 +137,29 @@ def __call__(self, bounds=None): data = interval(array) data = contrast_bias(data, out=data) data = stretch(data, out=data) - data[np.isnan(data)] = 0 if self.mode == 'colormap': + # ensure "bad" values have the same alpha as the + # rest of the layer: + if hasattr(layer['cmap'], 'get_bad'): + bad_color = layer['cmap'].get_bad().tolist()[:3] + layer_cmap = layer['cmap'].with_extremes( + bad=bad_color + [layer['alpha']] + ) + else: + layer_cmap = layer['cmap'] + if img is None: img = np.ones(data.shape + (4,)) # Compute colormapped image - plane = layer['cmap'](data) + plane = layer_cmap(data) # Check what the smallest colormap alpha value for this layer is # - if it is 1 then this colormap does not change transparency, # and this allows us to speed things up a little. - - if layer['cmap'](CMAP_SAMPLING)[:, 3].min() == 1: + if layer_cmap(CMAP_SAMPLING)[:, 3].min() == 1: if layer['alpha'] == 1: img[...] = 0 @@ -170,7 +170,6 @@ def __call__(self, bounds=None): else: # Use traditional alpha compositing - alpha_plane = layer['alpha'] * plane[:, :, 3] plane[:, :, 0] = plane[:, :, 0] * alpha_plane @@ -184,7 +183,6 @@ def __call__(self, bounds=None): img[:, :, 3] = 1 else: - if img is None: img = np.zeros(data.shape + (4,)) diff --git a/glue/viewers/image/qt/__init__.py b/glue/viewers/image/qt/__init__.py index 63a0b2170..5444f7485 100644 --- a/glue/viewers/image/qt/__init__.py +++ b/glue/viewers/image/qt/__init__.py @@ -1,7 +1,4 @@ -from .data_viewer import ImageViewer # noqa -from .standalone_image_viewer import StandaloneImageViewer # noqa - - -def setup(): - from glue.config import qt_client - qt_client.add(ImageViewer) +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.viewers.image.qt is deprecated, use glue_qt.viewers.image instead', GlueDeprecationWarning) +from glue_qt.viewers.image import * # noqa diff --git a/glue/viewers/image/qt/contrast_mouse_mode.py b/glue/viewers/image/qt/contrast_mouse_mode.py index 52fa167ce..37d452705 100644 --- a/glue/viewers/image/qt/contrast_mouse_mode.py +++ b/glue/viewers/image/qt/contrast_mouse_mode.py @@ -1,38 +1,4 @@ -# New contrast/bias mode that operates on viewers with state objects - -from echo import delay_callback -from glue.config import viewer_tool -from glue.viewers.matplotlib.toolbar_mode import ToolbarModeBase - - -@viewer_tool -class ContrastBiasMode(ToolbarModeBase): - """ - Uses right mouse button drags to set bias and contrast, DS9-style. The - horizontal position of the mouse sets the bias, the vertical position sets - the contrast. - """ - - icon = 'glue_contrast' - tool_id = 'image:contrast_bias' - action_text = 'Contrast/Bias' - tool_tip = 'Adjust the bias/contrast' - status_tip = ('CLICK and DRAG on image from left to right to adjust ' - 'bias and up and down to adjust contrast') - - def move(self, event): - """ - Update bias and contrast on Right Mouse button drag. - """ - - if event.button not in (1, 3): - return - - x, y = self.viewer.axes.transAxes.inverted().transform((event.x, event.y)) - state = self.viewer.selected_layer.state - - with delay_callback(state, 'bias', 'contrast'): - state.bias = -(x * 2 - 1.5) - state.contrast = 10. ** (y * 2 - 1) - - super(ContrastBiasMode, self).move(event) +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.viewers.image.qt.contrast_mouse_mode is deprecated, use glue_qt.viewers.image.contrast_mouse_mode instead', GlueDeprecationWarning) +from glue_qt.viewers.image.contrast_mouse_mode import * # noqa diff --git a/glue/viewers/image/qt/data_viewer.py b/glue/viewers/image/qt/data_viewer.py index c78a443f0..7499d72a3 100644 --- a/glue/viewers/image/qt/data_viewer.py +++ b/glue/viewers/image/qt/data_viewer.py @@ -1,52 +1,4 @@ -from glue.viewers.matplotlib.qt.data_viewer import MatplotlibDataViewer -from glue.viewers.scatter.qt.layer_style_editor import ScatterLayerStyleEditor -from glue.viewers.scatter.layer_artist import ScatterLayerArtist -from glue.viewers.image.qt.layer_style_editor import ImageLayerStyleEditor -from glue.viewers.image.qt.layer_style_editor_subset import ImageLayerSubsetStyleEditor -from glue.viewers.image.layer_artist import ImageLayerArtist, ImageSubsetLayerArtist -from glue.viewers.image.qt.options_widget import ImageOptionsWidget -from glue.viewers.image.qt.mouse_mode import RoiClickAndDragMode -from glue.viewers.image.state import ImageViewerState -from glue.utils import defer_draw, decorate_all_methods - -# Import the mouse mode to make sure it gets registered -from glue.viewers.image.qt.contrast_mouse_mode import ContrastBiasMode # noqa -from glue.viewers.image.qt.profile_viewer_tool import ProfileViewerTool # noqa -from glue.viewers.image.qt.pixel_selection_mode import PixelSelectionTool # noqa - -from glue.viewers.image.viewer import MatplotlibImageMixin - -__all__ = ['ImageViewer'] - - -@decorate_all_methods(defer_draw) -class ImageViewer(MatplotlibImageMixin, MatplotlibDataViewer): - - LABEL = '2D Image' - _default_mouse_mode_cls = RoiClickAndDragMode - _layer_style_widget_cls = {ImageLayerArtist: ImageLayerStyleEditor, - ImageSubsetLayerArtist: ImageLayerSubsetStyleEditor, - ScatterLayerArtist: ScatterLayerStyleEditor} - _state_cls = ImageViewerState - _options_cls = ImageOptionsWidget - - allow_duplicate_data = True - - # NOTE: _data_artist_cls and _subset_artist_cls are not defined - instead - # we override get_data_layer_artist and get_subset_layer_artist for - # more advanced logic. - - tools = ['select:rectangle', 'select:xrange', - 'select:yrange', 'select:circle', - 'select:polygon', 'image:point_selection', 'image:contrast_bias', - 'profile-viewer'] - - def __init__(self, session, parent=None, state=None): - MatplotlibDataViewer.__init__(self, session, wcs=True, parent=parent, state=state) - MatplotlibImageMixin.setup_callbacks(self) - - def closeEvent(self, *args): - super(ImageViewer, self).closeEvent(*args) - if self.axes._composite_image is not None: - self.axes._composite_image.remove() - self.axes._composite_image = None +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.viewers.image.qt.data_viewer is deprecated, use glue_qt.viewers.image.data_viewer instead', GlueDeprecationWarning) +from glue_qt.viewers.image.data_viewer import * # noqa diff --git a/glue/viewers/image/qt/layer_style_editor.py b/glue/viewers/image/qt/layer_style_editor.py index c76b10326..253591c36 100644 --- a/glue/viewers/image/qt/layer_style_editor.py +++ b/glue/viewers/image/qt/layer_style_editor.py @@ -1,36 +1,4 @@ -import os - -from qtpy import QtWidgets - -from echo.qt import autoconnect_callbacks_to_qt -from glue.utils.qt import load_ui - - -class ImageLayerStyleEditor(QtWidgets.QWidget): - - def __init__(self, layer, parent=None): - - super(ImageLayerStyleEditor, self).__init__(parent=parent) - - self.ui = load_ui('layer_style_editor.ui', self, - directory=os.path.dirname(__file__)) - - connect_kwargs = {'alpha': dict(value_range=(0, 1)), - 'contrast': dict(value_range=(0.1, 10), log=True), - 'bias': dict(value_range=(1.5, -0.5))} - - self._connections = autoconnect_callbacks_to_qt(layer.state, self.ui, connect_kwargs) - - layer._viewer_state.add_callback('color_mode', self._update_color_mode) - - self._update_color_mode(layer._viewer_state.color_mode) - - self.ui.bool_global_sync.setToolTip('Whether to sync the color and transparency with other viewers') - - def _update_color_mode(self, color_mode): - if color_mode == 'Colormaps': - self.ui.color_color.hide() - self.ui.combodata_cmap.show() - else: - self.ui.color_color.show() - self.ui.combodata_cmap.hide() +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.viewers.image.qt.layer_style_editor is deprecated, use glue_qt.viewers.image.layer_style_editor instead', GlueDeprecationWarning) +from glue_qt.viewers.image.layer_style_editor import * # noqa diff --git a/glue/viewers/image/qt/layer_style_editor.ui b/glue/viewers/image/qt/layer_style_editor.ui deleted file mode 100644 index a655323b1..000000000 --- a/glue/viewers/image/qt/layer_style_editor.ui +++ /dev/null @@ -1,309 +0,0 @@ - - - Form - - - - 0 - 0 - 292 - 197 - - - - Form - - - - 5 - - - 5 - - - 5 - - - 5 - - - 10 - - - 5 - - - - - QComboBox::AdjustToMinimumContentsLengthWithIcon - - - - - - - - 75 - true - - - - attribute - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - 75 - true - - - - limits - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - 10 - - - - - - - - 80 - 0 - - - - QComboBox::AdjustToMinimumContentsLengthWithIcon - - - false - - - - - - - - 0 - 0 - - - - - - - - - - - - - - 50 - 0 - - - - 100 - - - Qt::Horizontal - - - - - - - Sync - - - true - - - - - - - - - 2 - - - - - - - - padding: 0px - - - ⇄ - - - - - - - - - - - - - 50 - 0 - - - - QComboBox::AdjustToMinimumContentsLengthWithIcon - - - - - - - - 50 - 0 - - - - QComboBox::AdjustToMinimumContentsLengthWithIcon - - - - - - - 10 - - - - - - 50 - 0 - - - - 1000 - - - Qt::Horizontal - - - - - - - - 50 - 0 - - - - 1000 - - - Qt::Horizontal - - - - - - - Reset - - - - - - - - - - 75 - true - - - - contrast/bias - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - 75 - true - - - - color/opacity - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Qt::Horizontal - - - - 40 - 5 - - - - - - - - - QColorBox - QLabel -
glue.utils.qt.colors
-
- - QColormapCombo - QComboBox -
glue.utils.qt.colors
-
-
- - -
diff --git a/glue/viewers/image/qt/layer_style_editor_subset.py b/glue/viewers/image/qt/layer_style_editor_subset.py index 6173074fb..8713eb546 100644 --- a/glue/viewers/image/qt/layer_style_editor_subset.py +++ b/glue/viewers/image/qt/layer_style_editor_subset.py @@ -1,20 +1,4 @@ -import os - -from qtpy import QtWidgets - -from echo.qt import autoconnect_callbacks_to_qt -from glue.utils.qt import load_ui - - -class ImageLayerSubsetStyleEditor(QtWidgets.QWidget): - - def __init__(self, layer, parent=None): - - super(ImageLayerSubsetStyleEditor, self).__init__(parent=parent) - - self.ui = load_ui('layer_style_editor_subset.ui', self, - directory=os.path.dirname(__file__)) - - connect_kwargs = {'alpha': dict(value_range=(0, 1))} - - self._connections = autoconnect_callbacks_to_qt(layer.state, self.ui, connect_kwargs) +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.viewers.image.qt.layer_style_editor_subset is deprecated, use glue_qt.viewers.image.layer_style_editor_subset instead', GlueDeprecationWarning) +from glue_qt.viewers.image.layer_style_editor_subset import * # noqa diff --git a/glue/viewers/image/qt/layer_style_editor_subset.ui b/glue/viewers/image/qt/layer_style_editor_subset.ui deleted file mode 100644 index 3d3356f89..000000000 --- a/glue/viewers/image/qt/layer_style_editor_subset.ui +++ /dev/null @@ -1,102 +0,0 @@ - - - Form - - - - 0 - 0 - 234 - 55 - - - - Form - - - - 5 - - - 5 - - - - - - 0 - 0 - - - - - - - - - - - 100 - - - Qt::Horizontal - - - - - - - - 75 - true - - - - transparency - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - 75 - true - - - - color - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - QColorBox - QLabel -
glue.utils.qt.colors
-
-
- - -
diff --git a/glue/viewers/image/qt/mouse_mode.py b/glue/viewers/image/qt/mouse_mode.py index effa4b468..bf4789933 100644 --- a/glue/viewers/image/qt/mouse_mode.py +++ b/glue/viewers/image/qt/mouse_mode.py @@ -1,96 +1,4 @@ -from qtpy.QtGui import QCursor -from qtpy.QtWidgets import QMenu, QAction - -from glue.core.subset import RoiSubsetState -from glue.core.roi import MplPolygonalROI -from glue.core.edit_subset_mode import ReplaceMode -from glue.viewers.matplotlib.mouse_mode import MouseMode -from glue.viewers.image.layer_artist import ImageSubsetLayerArtist - - -__all__ = ['RoiClickAndDragMode'] - - -_MPL_LEFT_CLICK = 1 -_MPL_RIGHT_CLICK = 3 - - -class RoiClickAndDragMode(MouseMode): - """ - A MouseMode that enables clicking and dragging of existing ROIs. - """ - - def __init__(self, viewer, **kwargs): - super(RoiClickAndDragMode, self).__init__(viewer, **kwargs) - - self._viewer = viewer - self._dc = viewer.state.data_collection - self._edit_subset_mode = viewer.session.edit_subset_mode - - self._roi = None - self._subset = None - self._selected = False - - def _select_roi(self, roi, index, event): - self._roi = MplPolygonalROI(self._axes, roi=roi) - self._roi.start_selection(event, scrubbing=True) - self._edit_subset_mode.edit_subset = [self._dc.subset_groups[index]] - - def _deselect_roi(self, event): - - self._edit_subset_mode.edit_subset = [] - - if self._roi: - self._roi = None - self._subset = None - - def _display_roi_context_menu(self, roi_index): - - def delete_roi(event): - self._dc.remove_subset_group(self._dc.subset_groups[roi_index]) - - context_menu = QMenu() - action = QAction("Delete ROI", context_menu) - action.triggered.connect(delete_roi) - context_menu.addAction(action) - pos = self._viewer.mapToParent(QCursor().pos()) - context_menu.exec_(pos) - - def press(self, event): - # Ignore button presses outside the data viewer canvas - if event.xdata is None or event.ydata is None: - return - - x, y = (int(event.xdata + 0.5), int(event.ydata + 0.5)) - - roi_index = 0 - for layer in self._viewer.layers: - if not isinstance(layer, ImageSubsetLayerArtist): - continue - - subset_state = layer.state.layer.subset_state - if layer.visible and isinstance(subset_state, RoiSubsetState): - if subset_state.roi.contains(x, y): - if event.button == _MPL_LEFT_CLICK: - self._select_roi(subset_state.roi, roi_index, event) - self._subset = layer.state.layer - elif event.button == _MPL_RIGHT_CLICK: - self._display_roi_context_menu(roi_index) - self._selected = True - break - roi_index += 1 - else: - self._selected = False - self._deselect_roi(event) - - def move(self, event): - if self._roi is None or not self._selected: - return - - self._roi.update_selection(event) - - def release(self, event): - if self._roi: - self._viewer.apply_roi(self._roi.roi(), override_mode=ReplaceMode) - self._roi.finalize_selection(event) - self._selected = False +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.viewers.image.qt.mouse_mode is deprecated, use glue_qt.viewers.image.mouse_mode instead', GlueDeprecationWarning) +from glue_qt.viewers.image.mouse_mode import * # noqa diff --git a/glue/viewers/image/qt/options_widget.py b/glue/viewers/image/qt/options_widget.py index 3a005f3bc..b157c97de 100644 --- a/glue/viewers/image/qt/options_widget.py +++ b/glue/viewers/image/qt/options_widget.py @@ -1,41 +1,4 @@ -import os - -from qtpy import QtWidgets - -from echo.qt import autoconnect_callbacks_to_qt -from glue.utils.qt import load_ui, fix_tab_widget_fontsize -from glue.viewers.image.qt.slice_widget import MultiSliceWidgetHelper -from glue.viewers.matplotlib.state import MatplotlibDataViewerState - -__all__ = ['ImageOptionsWidget'] - - -class ImageOptionsWidget(QtWidgets.QWidget): - - def __init__(self, viewer_state, session, parent=None): - - super(ImageOptionsWidget, self).__init__(parent=parent) - - self.ui = load_ui('options_widget.ui', self, - directory=os.path.dirname(__file__)) - - fix_tab_widget_fontsize(self.ui.tab_widget) - - self._connections = autoconnect_callbacks_to_qt(viewer_state, self.ui) - self._connections_axes = autoconnect_callbacks_to_qt(viewer_state, self.ui.axes_editor.ui) - connect_kwargs = {'alpha': dict(value_range=(0, 1))} - self._connections_legend = autoconnect_callbacks_to_qt(viewer_state.legend, self.ui.legend_editor.ui, connect_kwargs) - - self.viewer_state = viewer_state - - self.slice_helper = MultiSliceWidgetHelper(viewer_state=self.viewer_state, - layout=self.ui.layout_slices) - - self.session = session - self.ui.axes_editor.button_apply_all.clicked.connect(self._apply_all_viewers) - - def _apply_all_viewers(self): - for tab in self.session.application.viewers: - for viewer in tab: - if isinstance(viewer.state, MatplotlibDataViewerState): - viewer.state.update_axes_settings_from(self.viewer_state) +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.viewers.image.qt.options_widget is deprecated, use glue_qt.viewers.image.options_widget instead', GlueDeprecationWarning) +from glue_qt.viewers.image.options_widget import * # noqa diff --git a/glue/viewers/image/qt/options_widget.ui b/glue/viewers/image/qt/options_widget.ui deleted file mode 100644 index dec488359..000000000 --- a/glue/viewers/image/qt/options_widget.ui +++ /dev/null @@ -1,401 +0,0 @@ - - - Widget - - - - 0 - 0 - 288 - 238 - - - - 2D Image - - - - 5 - - - 5 - - - 5 - - - 5 - - - 5 - - - - - 0 - - - - General - - - - 10 - - - 10 - - - 10 - - - 10 - - - 10 - - - 5 - - - - - - 75 - true - - - - mode - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - QComboBox::AdjustToMinimumContentsLengthWithIcon - - - - - - - QComboBox::AdjustToMinimumContentsLengthWithIcon - - - - - - - Qt::Horizontal - - - - - - - Qt::Horizontal - - - - 40 - 5 - - - - - - - - - 75 - true - - - - reference - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - QComboBox::AdjustToMinimumContentsLengthWithIcon - - - - - - - - 75 - true - - - - aspect - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - 75 - true - - - - x axis - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - 75 - true - - - - y axis - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - QComboBox::AdjustToMinimumContentsLengthWithIcon - - - - - - - QComboBox::AdjustToMinimumContentsLengthWithIcon - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - Limits - - - - 10 - - - 10 - - - 10 - - - 10 - - - 10 - - - 5 - - - - - padding: 0px - - - ⇄ - - - - - - - - 75 - true - - - - y axis - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Qt::Horizontal - - - - 40 - 5 - - - - - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - - - - 75 - true - - - - x axis - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - padding: 0px - - - ⇄ - - - - - label_2 - valuetext_x_min - button_flip_x - valuetext_x_max - label_2 - valuetext_y_min - button_flip_y - valuetext_y_max - verticalSpacer_3 - horizontalSpacer - - - - Axes - - - - 5 - - - 5 - - - 5 - - - 5 - - - - - - - - - Legend - - - - 5 - - - 5 - - - 5 - - - 5 - - - - - - - - - - - - - - AxesEditorWidget - QWidget -
glue.viewers.matplotlib.qt.axes_editor
-
- - LegendEditorWidget - QWidget -
glue.viewers.matplotlib.qt.legend_editor
-
-
- - -
diff --git a/glue/viewers/image/qt/pixel_selection_mode.py b/glue/viewers/image/qt/pixel_selection_mode.py index 004d6ecbc..4bad22fc4 100644 --- a/glue/viewers/image/qt/pixel_selection_mode.py +++ b/glue/viewers/image/qt/pixel_selection_mode.py @@ -1,65 +1,4 @@ -from glue.config import viewer_tool - -from glue.core.command import ApplySubsetState -from glue.core.edit_subset_mode import ReplaceMode - -from glue.viewers.matplotlib.toolbar_mode import ToolbarModeBase -from glue.viewers.image.pixel_selection_subset_state import PixelSubsetState - -__all__ = ['PixelSelectionTool'] - - -@viewer_tool -class PixelSelectionTool(ToolbarModeBase): - """ - Selects pixel under mouse cursor. - """ - - icon = "glue_crosshair" - tool_id = 'image:point_selection' - action_text = 'Pixel' - tool_tip = 'Select a single pixel based on mouse location' - status_tip = 'CLICK to select a point, CLICK and DRAG to update the selection in real time' - - _pressed = False - - def __init__(self, *args, **kwargs): - super(PixelSelectionTool, self).__init__(*args, **kwargs) - self._move_callback = self._select_pixel - self._press_callback = self._on_press - self._release_callback = self._on_release - - def _on_press(self, mode): - self._pressed = True - self.viewer.session.edit_subset_mode.mode = ReplaceMode - self._select_pixel(mode) - - def _on_release(self, mode): - self._pressed = False - - def _select_pixel(self, mode): - """ - Select a pixel - """ - - if not self._pressed: - return - - x, y = self._event_xdata, self._event_ydata - - if x is None or y is None: - return None - - x = int(round(x)) - y = int(round(y)) - - slices = [slice(None)] * self.viewer.state.reference_data.ndim - slices[self.viewer.state.x_att.axis] = slice(x, x + 1) - slices[self.viewer.state.y_att.axis] = slice(y, y + 1) - - subset_state = PixelSubsetState(self.viewer.state.reference_data, slices) - - cmd = ApplySubsetState(data_collection=self.viewer._data, - subset_state=subset_state, - override_mode=None) - self.viewer._session.command_stack.do(cmd) +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.viewers.image.qt.pixel_selection_mode is deprecated, use glue_qt.viewers.image.pixel_selection_mode instead', GlueDeprecationWarning) +from glue_qt.viewers.image.pixel_selection_mode import * # noqa diff --git a/glue/viewers/image/qt/profile_viewer_tool.py b/glue/viewers/image/qt/profile_viewer_tool.py index 48b110aab..c89021d39 100644 --- a/glue/viewers/image/qt/profile_viewer_tool.py +++ b/glue/viewers/image/qt/profile_viewer_tool.py @@ -1,80 +1,4 @@ -from glue.config import viewer_tool -from glue.viewers.common.tool import Tool -from glue.core.qt.dialogs import info, warn -from glue.core.component_id import PixelComponentID - - -@viewer_tool -class ProfileViewerTool(Tool): - - icon = 'glue_spectrum' - tool_id = 'profile-viewer' - - @property - def profile_viewers_exist(self): - from glue.viewers.profile.qt import ProfileViewer - for tab in self.viewer.session.application.viewers: - for viewer in tab: - if isinstance(viewer, ProfileViewer): - return True - return False - - def activate(self): - - if self.profile_viewers_exist: - - proceed = warn('A profile viewer was already created', - 'Do you really want to create a new one?', - default='Cancel', setting='show_warn_profile_duplicate') - - if not proceed: - return - - else: - - proceed = info('Creating a profile viewer', - 'Note: profiles are ' - 'computed from datasets and subsets collapsed along all but one ' - 'dimension. To view the profile of part of the data, once you ' - 'click OK you can draw and update a subset in the current ' - 'image viewer and the profile will update accordingly.', setting='show_info_profile_open') - - if not proceed: - return - - from glue.viewers.profile.qt import ProfileViewer - profile_viewer = self.viewer.session.application.new_data_viewer(ProfileViewer) - any_added = False - for data in self.viewer.session.data_collection: - if data in self.viewer._layer_artist_container: - result = profile_viewer.add_data(data) - any_added = any_added or result - - if not any_added: - profile_viewer.close() - return - - # If the reference data for the current image viewer is in the profile - # viewer, we make sure that it is used as the reference data there too - if self.viewer.state.reference_data in profile_viewer._layer_artist_container: - - profile_viewer.state.reference_data = self.viewer.state.reference_data - - # We now pick an attribute in the profile viewer that is one of the ones - # with a slider in the image viewer. Note that the attribute viewer may - # be a pixel attribute or world attribute depending on what information - # is available in the coordinates, so we need to be careful about that. - - x_att = profile_viewer.state.x_att - reference_data = self.viewer.state.reference_data - - if isinstance(profile_viewer.state.x_att, PixelComponentID): - for att in reference_data.pixel_component_ids: - if att is not self.viewer.state.x_att and att is not self.viewer.state.y_att: - if att is not profile_viewer.state.x_att: - profile_viewer.state.x_att = att - else: - for att in reference_data.world_component_ids: - if att is not self.viewer.state.x_att_world and att is not self.viewer.state.y_att_world: - if att is not profile_viewer.state.x_att: - profile_viewer.state.x_att = att +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.viewers.image.qt.profile_viewer_tool is deprecated, use glue_qt.viewers.image.profile_viewer_tool instead', GlueDeprecationWarning) +from glue_qt.viewers.image.profile_viewer_tool import * # noqa diff --git a/glue/viewers/image/qt/slice_widget.py b/glue/viewers/image/qt/slice_widget.py index 3fc80910b..692f4ea6c 100644 --- a/glue/viewers/image/qt/slice_widget.py +++ b/glue/viewers/image/qt/slice_widget.py @@ -1,149 +1,4 @@ -import numpy as np - -from glue.core.coordinates import LegacyCoordinates -from glue.core.coordinate_helpers import dependent_axes, world_axis -from glue.viewers.common.qt.data_slice_widget import SliceWidget -from glue.viewers.image.state import AggregateSlice -from glue.utils.decorators import avoid_circular - -__all__ = ['MultiSliceWidgetHelper'] - - -class MultiSliceWidgetHelper(object): - - def __init__(self, viewer_state=None, layout=None): - - self.viewer_state = viewer_state - - self.layout = layout - self.layout.setSpacing(4) - self.layout.setContentsMargins(0, 3, 0, 3) - - self.viewer_state.add_callback('x_att', self.sync_sliders_from_state) - self.viewer_state.add_callback('y_att', self.sync_sliders_from_state) - self.viewer_state.add_callback('slices', self.sync_sliders_from_state) - self.viewer_state.add_callback('reference_data', self.sync_sliders_from_state) - - self._sliders = [] - - self._reference_data = None - self._x_att = None - self._y_att = None - - self.sync_sliders_from_state() - - @property - def data(self): - return self.viewer_state.reference_data - - def _clear(self): - - for _ in range(self.layout.count()): - self.layout.takeAt(0) - - for s in self._sliders: - if s is not None: - s.close() - - self._sliders = [] - - @avoid_circular - def sync_state_from_sliders(self, *args): - slices = [] - for i, slider in enumerate(self._sliders): - if slider is not None: - slices.append(slider.state.slice_center) - else: - slices.append(self.viewer_state.slices[i]) - self.viewer_state.slices = tuple(slices) - - @avoid_circular - def sync_sliders_from_state(self, *args): - - if self.data is None or self.viewer_state.x_att is None or self.viewer_state.y_att is None: - return - - if self.viewer_state.x_att is self.viewer_state.y_att: - return - - # Update sliders if needed - - if (self.viewer_state.reference_data is not self._reference_data or - self.viewer_state.x_att is not self._x_att or - self.viewer_state.y_att is not self._y_att): - - self._reference_data = self.viewer_state.reference_data - self._x_att = self.viewer_state.x_att - self._y_att = self.viewer_state.y_att - - self._clear() - - for i in range(self.data.ndim): - - if i == self.viewer_state.x_att.axis or i == self.viewer_state.y_att.axis: - self._sliders.append(None) - continue - - # TODO: For now we simply pass a single set of world coordinates, - # but we will need to generalize this in future. We deliberately - # check the type of data.coords here since we want to treat - # subclasses differently. - if getattr(self.data, 'coords') is not None and type(self.data.coords) != LegacyCoordinates: - world_axis_index = self.data.ndim - 1 - i - world = world_axis(self.data.coords, self.data, - pixel_axis=world_axis_index, - world_axis=world_axis_index) - world_unit = self.data.coords.world_axis_units[world_axis_index] - world_warning = len(dependent_axes(self.data.coords, i)) > 1 - world_label = self.data.world_component_ids[i].label - else: - world = None - world_unit = None - world_warning = False - world_label = self.data.pixel_component_ids[i].label - - slider = SliceWidget(world_label, - hi=self.data.shape[i] - 1, world=world, - world_unit=world_unit, world_warning=world_warning) - - self.slider_state = slider.state - self.slider_state.add_callback('slice_center', self.sync_state_from_sliders) - self._sliders.append(slider) - self.layout.addWidget(slider) - - for i in range(self.data.ndim): - if self._sliders[i] is not None: - if isinstance(self.viewer_state.slices[i], AggregateSlice): - self._sliders[i].state.slice_center = self.viewer_state.slices[i].center - else: - self._sliders[i].state.slice_center = self.viewer_state.slices[i] - - -if __name__ == "__main__": - - from glue.core import Data - from glue.utils.qt import get_qapp - from echo import CallbackProperty - from glue.core.state_objects import State - - app = get_qapp() - - class FakeViewerState(State): - x_att = CallbackProperty() - y_att = CallbackProperty() - reference_data = CallbackProperty() - slices = CallbackProperty() - - viewer_state = FakeViewerState() - - data = Data(x=np.random.random((3, 50, 20, 5, 3))) - - viewer_state.reference_data = data - viewer_state.x_att = data.pixel_component_ids[0] - viewer_state.y_att = data.pixel_component_ids[3] - viewer_state.slices = [0] * 5 - - widget = MultiSliceWidgetHelper(viewer_state) - widget.show() - - app.exec_() +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.viewers.image.qt.slice_widget is deprecated, use glue_qt.viewers.image.slice_widget instead', GlueDeprecationWarning) +from glue_qt.viewers.image.slice_widget import * # noqa diff --git a/glue/viewers/image/qt/standalone_image_viewer.py b/glue/viewers/image/qt/standalone_image_viewer.py index 740ad1446..437466bba 100644 --- a/glue/viewers/image/qt/standalone_image_viewer.py +++ b/glue/viewers/image/qt/standalone_image_viewer.py @@ -1,168 +1,4 @@ -import numpy as np - -from qtpy import QtCore, QtWidgets - -from glue.config import colormaps -from glue.viewers.common.qt.toolbar import BasicToolbar -from glue.viewers.matplotlib.qt.widget import MplWidget -from glue.viewers.image.composite_array import CompositeArray -from glue.external.modest_image import imshow -from glue.utils import defer_draw - -from glue.viewers.matplotlib.mpl_axes import init_mpl - -# Import the mouse mode to make sure it gets registered -from glue.viewers.image.qt.contrast_mouse_mode import ContrastBiasMode # noqa - -__all__ = ['StandaloneImageViewer'] - - -class StandaloneImageViewer(QtWidgets.QMainWindow): - """ - A simplified image viewer, without any brushing or linking, - but with the ability to adjust contrast and resample. - """ - window_closed = QtCore.Signal() - _toolbar_cls = BasicToolbar - tools = ['image:contrast', 'image:colormap'] - - def __init__(self, image=None, wcs=None, parent=None, **kwargs): - """ - :param image: Image to display (2D numpy array) - :param parent: Parent widget (optional) - - :param kwargs: Extra keywords to pass to imshow - """ - super(StandaloneImageViewer, self).__init__(parent) - - self.central_widget = MplWidget() - self.setCentralWidget(self.central_widget) - self._setup_axes() - - self._composite = CompositeArray() - self._composite.allocate('image') - - self._im = None - - self.initialize_toolbar() - - if image is not None: - self.set_image(image=image, wcs=wcs, **kwargs) - - def _setup_axes(self): - _, self._axes = init_mpl(self.central_widget.canvas.fig, axes=None, wcs=True) - self._axes.set_aspect('equal', adjustable='datalim') - - @defer_draw - def set_image(self, image=None, wcs=None, **kwargs): - """ - Update the image shown in the widget - """ - if self._im is not None: - self._im.remove() - self._im = None - - kwargs.setdefault('origin', 'upper') - - self._composite.set('image', array=image, color=colormaps.members[0][1]) - self._im = imshow(self._axes, self._composite, **kwargs) - self._im_array = image - self._set_norm(self._contrast_mode) - - if 'extent' in kwargs: - self.axes.set_xlim(kwargs['extent'][:2]) - self.axes.set_ylim(kwargs['extent'][2:]) - else: - ny, nx = image.shape - self.axes.set_xlim(-0.5, nx - 0.5) - self.axes.set_ylim(-0.5, ny - 0.5) - - # FIXME: for a reason I don't quite understand, dataLim doesn't - # get updated immediately here, which means that there are then - # issues in the first draw of the image (the limits are such that - # only part of the image is shown). We just set dataLim manually - # to avoid this issue. This is also done in ImageViewer. - self.axes.dataLim.intervalx = self.axes.get_xlim() - self.axes.dataLim.intervaly = self.axes.get_ylim() - - self._redraw() - - @property - def axes(self): - """ - The Matplotlib axes object for this figure - """ - return self._axes - - def show(self): - super(StandaloneImageViewer, self).show() - self._redraw() - - def _redraw(self): - self.central_widget.canvas.draw_idle() - - def set_cmap(self, cmap): - self._composite.set('image', color=cmap) - self._im.invalidate_cache() - self._redraw() - - def mdi_wrap(self): - """ - Embed this widget in a GlueMdiSubWindow - """ - from glue.app.qt.mdi_area import GlueMdiSubWindow - sub = GlueMdiSubWindow() - sub.setWidget(self) - self.destroyed.connect(sub.close) - self.window_closed.connect(sub.close) - sub.resize(self.size()) - self._mdi_wrapper = sub - return sub - - def closeEvent(self, event): - if self._im is not None: - self._im.remove() - self._im = None - self.window_closed.emit() - return super(StandaloneImageViewer, self).closeEvent(event) - - def _set_norm(self, mode): - """ - Use the `ContrastMouseMode` to adjust the transfer function - """ - - pmin, pmax = mode.get_clip_percentile() - - if pmin is None: - clim = mode.get_vmin_vmax() - else: - clim = (np.nanpercentile(self._im_array, pmin), - np.nanpercentile(self._im_array, pmax)) - - stretch = mode.stretch - self._composite.set('image', clim=clim, stretch=stretch, - bias=mode.bias, contrast=mode.contrast) - - self._im.invalidate_cache() - self._redraw() - - def initialize_toolbar(self): - - from glue.config import viewer_tool - - self.toolbar = self._toolbar_cls(self) - - for tool_id in self.tools: - mode_cls = viewer_tool.members[tool_id] - if tool_id == 'image:contrast': - mode = mode_cls(self, move_callback=self._set_norm) - self._contrast_mode = mode - else: - mode = mode_cls(self) - self.toolbar.add_tool(mode) - - self.addToolBar(self.toolbar) - - def set_status(self, message): - sb = self.statusBar() - sb.showMessage(message) +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.viewers.image.qt.standalone_image_viewer is deprecated, use glue_qt.viewers.image.standalone_image_viewer instead', GlueDeprecationWarning) +from glue_qt.viewers.image.standalone_image_viewer import * # noqa diff --git a/glue/viewers/image/qt/tests/__init__.py b/glue/viewers/image/qt/tests/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/glue/viewers/image/qt/tests/baseline/test_resample_on_zoom.png b/glue/viewers/image/qt/tests/baseline/test_resample_on_zoom.png deleted file mode 100644 index e1c507966..000000000 Binary files a/glue/viewers/image/qt/tests/baseline/test_resample_on_zoom.png and /dev/null differ diff --git a/glue/viewers/image/qt/tests/data/image_cube_v0.glu b/glue/viewers/image/qt/tests/data/image_cube_v0.glu deleted file mode 100644 index 4a5671bf4..000000000 --- a/glue/viewers/image/qt/tests/data/image_cube_v0.glu +++ /dev/null @@ -1,410 +0,0 @@ -{ - "Component": { - "_type": "glue.core.component.Component", - "data": { - "_type": "numpy.ndarray", - "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGk4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDMsIDQsIDIsIDUpLCB9ICAgIAoAAAAAAAAAAAEAAAAAAAAAAgAAAAAAAAADAAAAAAAAAAQAAAAAAAAABQAAAAAAAAAGAAAAAAAAAAcAAAAAAAAACAAAAAAAAAAJAAAAAAAAAAoAAAAAAAAACwAAAAAAAAAMAAAAAAAAAA0AAAAAAAAADgAAAAAAAAAPAAAAAAAAABAAAAAAAAAAEQAAAAAAAAASAAAAAAAAABMAAAAAAAAAFAAAAAAAAAAVAAAAAAAAABYAAAAAAAAAFwAAAAAAAAAYAAAAAAAAABkAAAAAAAAAGgAAAAAAAAAbAAAAAAAAABwAAAAAAAAAHQAAAAAAAAAeAAAAAAAAAB8AAAAAAAAAIAAAAAAAAAAhAAAAAAAAACIAAAAAAAAAIwAAAAAAAAAkAAAAAAAAACUAAAAAAAAAJgAAAAAAAAAnAAAAAAAAACgAAAAAAAAAKQAAAAAAAAAqAAAAAAAAACsAAAAAAAAALAAAAAAAAAAtAAAAAAAAAC4AAAAAAAAALwAAAAAAAAAwAAAAAAAAADEAAAAAAAAAMgAAAAAAAAAzAAAAAAAAADQAAAAAAAAANQAAAAAAAAA2AAAAAAAAADcAAAAAAAAAOAAAAAAAAAA5AAAAAAAAADoAAAAAAAAAOwAAAAAAAAA8AAAAAAAAAD0AAAAAAAAAPgAAAAAAAAA/AAAAAAAAAEAAAAAAAAAAQQAAAAAAAABCAAAAAAAAAEMAAAAAAAAARAAAAAAAAABFAAAAAAAAAEYAAAAAAAAARwAAAAAAAABIAAAAAAAAAEkAAAAAAAAASgAAAAAAAABLAAAAAAAAAEwAAAAAAAAATQAAAAAAAABOAAAAAAAAAE8AAAAAAAAAUAAAAAAAAABRAAAAAAAAAFIAAAAAAAAAUwAAAAAAAABUAAAAAAAAAFUAAAAAAAAAVgAAAAAAAABXAAAAAAAAAFgAAAAAAAAAWQAAAAAAAABaAAAAAAAAAFsAAAAAAAAAXAAAAAAAAABdAAAAAAAAAF4AAAAAAAAAXwAAAAAAAABgAAAAAAAAAGEAAAAAAAAAYgAAAAAAAABjAAAAAAAAAGQAAAAAAAAAZQAAAAAAAABmAAAAAAAAAGcAAAAAAAAAaAAAAAAAAABpAAAAAAAAAGoAAAAAAAAAawAAAAAAAABsAAAAAAAAAG0AAAAAAAAAbgAAAAAAAABvAAAAAAAAAHAAAAAAAAAAcQAAAAAAAAByAAAAAAAAAHMAAAAAAAAAdAAAAAAAAAB1AAAAAAAAAHYAAAAAAAAAdwAAAAAAAAA=" - }, - "units": "" - }, - "CoordinateComponent": { - "_type": "glue.core.component.CoordinateComponent", - "axis": 0, - "world": false - }, - "CoordinateComponentLink": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "World 0", - "World 1", - "World 2", - "World 3" - ], - "index": 1, - "pix2world": false, - "to": [ - "Pixel Axis 1" - ] - }, - "CoordinateComponentLink_0": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "Pixel Axis 0", - "Pixel Axis 1", - "Pixel Axis 2", - "Pixel Axis 3" - ], - "index": 2, - "pix2world": true, - "to": [ - "World 2" - ] - }, - "CoordinateComponentLink_1": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "World 0", - "World 1", - "World 2", - "World 3" - ], - "index": 2, - "pix2world": false, - "to": [ - "Pixel Axis 2" - ] - }, - "CoordinateComponentLink_2": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "Pixel Axis 0", - "Pixel Axis 1", - "Pixel Axis 2", - "Pixel Axis 3" - ], - "index": 3, - "pix2world": true, - "to": [ - "World 3" - ] - }, - "CoordinateComponentLink_3": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "World 0", - "World 1", - "World 2", - "World 3" - ], - "index": 3, - "pix2world": false, - "to": [ - "Pixel Axis 3" - ] - }, - "CoordinateComponentLink_4": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "Pixel Axis 0", - "Pixel Axis 1", - "Pixel Axis 2", - "Pixel Axis 3" - ], - "index": 0, - "pix2world": true, - "to": [ - "World 0" - ] - }, - "CoordinateComponentLink_5": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "World 0", - "World 1", - "World 2", - "World 3" - ], - "index": 0, - "pix2world": false, - "to": [ - "Pixel Axis 0" - ] - }, - "CoordinateComponentLink_6": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "Pixel Axis 0", - "Pixel Axis 1", - "Pixel Axis 2", - "Pixel Axis 3" - ], - "index": 1, - "pix2world": true, - "to": [ - "World 1" - ] - }, - "CoordinateComponent_0": { - "_type": "glue.core.component.CoordinateComponent", - "axis": 1, - "world": false - }, - "CoordinateComponent_1": { - "_type": "glue.core.component.CoordinateComponent", - "axis": 2, - "world": false - }, - "CoordinateComponent_2": { - "_type": "glue.core.component.CoordinateComponent", - "axis": 3, - "world": false - }, - "CoordinateComponent_3": { - "_type": "glue.core.component.CoordinateComponent", - "axis": 0, - "world": true - }, - "CoordinateComponent_4": { - "_type": "glue.core.component.CoordinateComponent", - "axis": 1, - "world": true - }, - "CoordinateComponent_5": { - "_type": "glue.core.component.CoordinateComponent", - "axis": 2, - "world": true - }, - "CoordinateComponent_6": { - "_type": "glue.core.component.CoordinateComponent", - "axis": 3, - "world": true - }, - "Coordinates": { - "_type": "glue.core.coordinates.Coordinates" - }, - "DS9Normalize": { - "_type": "glue.viewers.image.ds9norm.DS9Normalize", - "bias": 0.5, - "clip_hi": 100, - "clip_lo": 0, - "contrast": 1.0, - "stretch": "linear", - "vmax": null, - "vmin": null - }, - "DataCollection": { - "_protocol": 3, - "_type": "glue.core.data_collection.DataCollection", - "cids": [ - "array_0", - "Pixel Axis 0", - "Pixel Axis 1", - "Pixel Axis 2", - "Pixel Axis 3", - "World 0", - "World 1", - "World 2", - "World 3" - ], - "components": [ - "Component", - "CoordinateComponent", - "CoordinateComponent_0", - "CoordinateComponent_1", - "CoordinateComponent_2", - "CoordinateComponent_3", - "CoordinateComponent_4", - "CoordinateComponent_5", - "CoordinateComponent_6" - ], - "data": [ - "array" - ], - "groups": [], - "links": [ - "CoordinateComponentLink", - "CoordinateComponentLink_0", - "CoordinateComponentLink_1", - "CoordinateComponentLink_2", - "CoordinateComponentLink_3", - "CoordinateComponentLink_4", - "CoordinateComponentLink_5", - "CoordinateComponentLink_6" - ], - "subset_group_count": 0 - }, - "ImageWidget": { - "_type": "glue.viewers.image.qt.viewer_widget.ImageWidget", - "layers": [ - { - "_type": "glue.viewers.image.layer_artist.ImageLayerArtist", - "layer": "array", - "norm": "DS9Normalize", - "visible": true, - "zorder": 1 - } - ], - "pos": [ - 0, - 0 - ], - "properties": { - "attribute": "array_0", - "batt": null, - "data": "array", - "gatt": null, - "ratt": null, - "rgb_mode": false, - "rgb_viz": [ - true, - true, - true - ], - "slice": [ - 2, - "y", - "x", - 1 - ] - }, - "session": "Session", - "size": [ - 731, - 529 - ] - }, - "Pixel Axis 0": { - "_type": "glue.core.component_id.PixelComponentID", - "axis": 0, - "hidden": true, - "label": "Pixel Axis 0" - }, - "Pixel Axis 1": { - "_type": "glue.core.component_id.PixelComponentID", - "axis": 1, - "hidden": true, - "label": "Pixel Axis 1" - }, - "Pixel Axis 2": { - "_type": "glue.core.component_id.PixelComponentID", - "axis": 2, - "hidden": true, - "label": "Pixel Axis 2" - }, - "Pixel Axis 3": { - "_type": "glue.core.component_id.PixelComponentID", - "axis": 3, - "hidden": true, - "label": "Pixel Axis 3" - }, - "Session": { - "_type": "glue.core.session.Session" - }, - "World 0": { - "_type": "glue.core.component_id.ComponentID", - "hidden": true, - "label": "World 0" - }, - "World 1": { - "_type": "glue.core.component_id.ComponentID", - "hidden": true, - "label": "World 1" - }, - "World 2": { - "_type": "glue.core.component_id.ComponentID", - "hidden": true, - "label": "World 2" - }, - "World 3": { - "_type": "glue.core.component_id.ComponentID", - "hidden": true, - "label": "World 3" - }, - "__main__": { - "_type": "glue.app.qt.application.GlueApplication", - "data": "DataCollection", - "plugins": [ - "glue.plugins.tools.pv_slicer", - "glue.viewers.histogram", - "glue.viewers.table", - "glue_vispy_viewers.volume", - "glue.plugins.exporters.plotly", - "glue.plugins.export_d3po", - "glue.viewers.image", - "glue.plugins.tools.spectrum_tool", - "glue_vispy_viewers.scatter", - "glue.viewers.scatter", - "glue.plugins.coordinate_helpers", - "glue.core.data_exporters" - ], - "session": "Session", - "tab_names": [ - "Tab 1" - ], - "viewers": [ - [ - "ImageWidget" - ] - ] - }, - "array": { - "_key_joins": [], - "_protocol": 5, - "_type": "glue.core.data.Data", - "components": [ - [ - "array_0", - "Component" - ], - [ - "Pixel Axis 0", - "CoordinateComponent" - ], - [ - "Pixel Axis 1", - "CoordinateComponent_0" - ], - [ - "Pixel Axis 2", - "CoordinateComponent_1" - ], - [ - "Pixel Axis 3", - "CoordinateComponent_2" - ], - [ - "World 0", - "CoordinateComponent_3" - ], - [ - "World 1", - "CoordinateComponent_4" - ], - [ - "World 2", - "CoordinateComponent_5" - ], - [ - "World 3", - "CoordinateComponent_6" - ] - ], - "coords": "Coordinates", - "label": "array", - "primary_owner": [ - "array_0", - "Pixel Axis 0", - "Pixel Axis 1", - "Pixel Axis 2", - "Pixel Axis 3", - "World 0", - "World 1", - "World 2", - "World 3" - ], - "style": { - "_type": "glue.core.visual.VisualAttributes", - "alpha": 0.8, - "color": "0.35", - "linestyle": "solid", - "linewidth": 1, - "marker": "o", - "markersize": 3 - }, - "subsets": [], - "uuid": "5e2335ce-5613-4fb1-aa58-1cf342f7b755" - }, - "array_0": { - "_type": "glue.core.component_id.ComponentID", - "hidden": false, - "label": "array" - } -} \ No newline at end of file diff --git a/glue/viewers/image/qt/tests/data/image_cube_v1.glu b/glue/viewers/image/qt/tests/data/image_cube_v1.glu deleted file mode 100644 index fef3667c4..000000000 --- a/glue/viewers/image/qt/tests/data/image_cube_v1.glu +++ /dev/null @@ -1,562 +0,0 @@ -{ - "CallbackList": { - "_type": "glue.external.echo.list.CallbackList", - "values": [ - "ImageLayerState" - ] - }, - "Component": { - "_type": "glue.core.component.Component", - "data": { - "_type": "numpy.ndarray", - "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGk4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDMsIDQsIDIsIDUpLCB9ICAgIAoAAAAAAAAAAAEAAAAAAAAAAgAAAAAAAAADAAAAAAAAAAQAAAAAAAAABQAAAAAAAAAGAAAAAAAAAAcAAAAAAAAACAAAAAAAAAAJAAAAAAAAAAoAAAAAAAAACwAAAAAAAAAMAAAAAAAAAA0AAAAAAAAADgAAAAAAAAAPAAAAAAAAABAAAAAAAAAAEQAAAAAAAAASAAAAAAAAABMAAAAAAAAAFAAAAAAAAAAVAAAAAAAAABYAAAAAAAAAFwAAAAAAAAAYAAAAAAAAABkAAAAAAAAAGgAAAAAAAAAbAAAAAAAAABwAAAAAAAAAHQAAAAAAAAAeAAAAAAAAAB8AAAAAAAAAIAAAAAAAAAAhAAAAAAAAACIAAAAAAAAAIwAAAAAAAAAkAAAAAAAAACUAAAAAAAAAJgAAAAAAAAAnAAAAAAAAACgAAAAAAAAAKQAAAAAAAAAqAAAAAAAAACsAAAAAAAAALAAAAAAAAAAtAAAAAAAAAC4AAAAAAAAALwAAAAAAAAAwAAAAAAAAADEAAAAAAAAAMgAAAAAAAAAzAAAAAAAAADQAAAAAAAAANQAAAAAAAAA2AAAAAAAAADcAAAAAAAAAOAAAAAAAAAA5AAAAAAAAADoAAAAAAAAAOwAAAAAAAAA8AAAAAAAAAD0AAAAAAAAAPgAAAAAAAAA/AAAAAAAAAEAAAAAAAAAAQQAAAAAAAABCAAAAAAAAAEMAAAAAAAAARAAAAAAAAABFAAAAAAAAAEYAAAAAAAAARwAAAAAAAABIAAAAAAAAAEkAAAAAAAAASgAAAAAAAABLAAAAAAAAAEwAAAAAAAAATQAAAAAAAABOAAAAAAAAAE8AAAAAAAAAUAAAAAAAAABRAAAAAAAAAFIAAAAAAAAAUwAAAAAAAABUAAAAAAAAAFUAAAAAAAAAVgAAAAAAAABXAAAAAAAAAFgAAAAAAAAAWQAAAAAAAABaAAAAAAAAAFsAAAAAAAAAXAAAAAAAAABdAAAAAAAAAF4AAAAAAAAAXwAAAAAAAABgAAAAAAAAAGEAAAAAAAAAYgAAAAAAAABjAAAAAAAAAGQAAAAAAAAAZQAAAAAAAABmAAAAAAAAAGcAAAAAAAAAaAAAAAAAAABpAAAAAAAAAGoAAAAAAAAAawAAAAAAAABsAAAAAAAAAG0AAAAAAAAAbgAAAAAAAABvAAAAAAAAAHAAAAAAAAAAcQAAAAAAAAByAAAAAAAAAHMAAAAAAAAAdAAAAAAAAAB1AAAAAAAAAHYAAAAAAAAAdwAAAAAAAAA=" - }, - "units": "" - }, - "CoordinateComponent": { - "_type": "glue.core.component.CoordinateComponent", - "axis": 0, - "world": false - }, - "CoordinateComponentLink": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "World 0", - "World 1", - "World 2", - "World 3" - ], - "index": 2, - "pix2world": false, - "to": [ - "Pixel Axis 2" - ] - }, - "CoordinateComponentLink_0": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "World 0", - "World 1", - "World 2", - "World 3" - ], - "index": 3, - "pix2world": false, - "to": [ - "Pixel Axis 3" - ] - }, - "CoordinateComponentLink_1": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "World 0", - "World 1", - "World 2", - "World 3" - ], - "index": 2, - "pix2world": false, - "to": [ - "Pixel Axis 2" - ] - }, - "CoordinateComponentLink_10": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "Pixel Axis 0", - "Pixel Axis 1", - "Pixel Axis 2", - "Pixel Axis 3" - ], - "index": 1, - "pix2world": true, - "to": [ - "World 1" - ] - }, - "CoordinateComponentLink_11": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "Pixel Axis 0", - "Pixel Axis 1", - "Pixel Axis 2", - "Pixel Axis 3" - ], - "index": 2, - "pix2world": true, - "to": [ - "World 2" - ] - }, - "CoordinateComponentLink_12": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "World 0", - "World 1", - "World 2", - "World 3" - ], - "index": 0, - "pix2world": false, - "to": [ - "Pixel Axis 0" - ] - }, - "CoordinateComponentLink_13": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "Pixel Axis 0", - "Pixel Axis 1", - "Pixel Axis 2", - "Pixel Axis 3" - ], - "index": 1, - "pix2world": true, - "to": [ - "World 1" - ] - }, - "CoordinateComponentLink_14": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "Pixel Axis 0", - "Pixel Axis 1", - "Pixel Axis 2", - "Pixel Axis 3" - ], - "index": 0, - "pix2world": true, - "to": [ - "World 0" - ] - }, - "CoordinateComponentLink_2": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "World 0", - "World 1", - "World 2", - "World 3" - ], - "index": 3, - "pix2world": false, - "to": [ - "Pixel Axis 3" - ] - }, - "CoordinateComponentLink_3": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "World 0", - "World 1", - "World 2", - "World 3" - ], - "index": 0, - "pix2world": false, - "to": [ - "Pixel Axis 0" - ] - }, - "CoordinateComponentLink_4": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "Pixel Axis 0", - "Pixel Axis 1", - "Pixel Axis 2", - "Pixel Axis 3" - ], - "index": 2, - "pix2world": true, - "to": [ - "World 2" - ] - }, - "CoordinateComponentLink_5": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "Pixel Axis 0", - "Pixel Axis 1", - "Pixel Axis 2", - "Pixel Axis 3" - ], - "index": 0, - "pix2world": true, - "to": [ - "World 0" - ] - }, - "CoordinateComponentLink_6": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "Pixel Axis 0", - "Pixel Axis 1", - "Pixel Axis 2", - "Pixel Axis 3" - ], - "index": 3, - "pix2world": true, - "to": [ - "World 3" - ] - }, - "CoordinateComponentLink_7": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "World 0", - "World 1", - "World 2", - "World 3" - ], - "index": 1, - "pix2world": false, - "to": [ - "Pixel Axis 1" - ] - }, - "CoordinateComponentLink_8": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "Pixel Axis 0", - "Pixel Axis 1", - "Pixel Axis 2", - "Pixel Axis 3" - ], - "index": 3, - "pix2world": true, - "to": [ - "World 3" - ] - }, - "CoordinateComponentLink_9": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "World 0", - "World 1", - "World 2", - "World 3" - ], - "index": 1, - "pix2world": false, - "to": [ - "Pixel Axis 1" - ] - }, - "CoordinateComponent_0": { - "_type": "glue.core.component.CoordinateComponent", - "axis": 1, - "world": false - }, - "CoordinateComponent_1": { - "_type": "glue.core.component.CoordinateComponent", - "axis": 2, - "world": false - }, - "CoordinateComponent_2": { - "_type": "glue.core.component.CoordinateComponent", - "axis": 3, - "world": false - }, - "CoordinateComponent_3": { - "_type": "glue.core.component.CoordinateComponent", - "axis": 0, - "world": true - }, - "CoordinateComponent_4": { - "_type": "glue.core.component.CoordinateComponent", - "axis": 1, - "world": true - }, - "CoordinateComponent_5": { - "_type": "glue.core.component.CoordinateComponent", - "axis": 2, - "world": true - }, - "CoordinateComponent_6": { - "_type": "glue.core.component.CoordinateComponent", - "axis": 3, - "world": true - }, - "Coordinates": { - "_type": "glue.core.coordinates.Coordinates" - }, - "DataCollection": { - "_protocol": 3, - "_type": "glue.core.data_collection.DataCollection", - "cids": [ - "array_0", - "Pixel Axis 0", - "Pixel Axis 1", - "Pixel Axis 2", - "Pixel Axis 3", - "World 0", - "World 1", - "World 2", - "World 3" - ], - "components": [ - "Component", - "CoordinateComponent", - "CoordinateComponent_0", - "CoordinateComponent_1", - "CoordinateComponent_2", - "CoordinateComponent_3", - "CoordinateComponent_4", - "CoordinateComponent_5", - "CoordinateComponent_6" - ], - "data": [ - "array" - ], - "groups": [], - "links": [ - "CoordinateComponentLink", - "CoordinateComponentLink_0", - "CoordinateComponentLink_1", - "CoordinateComponentLink_2", - "CoordinateComponentLink_3", - "CoordinateComponentLink_4", - "CoordinateComponentLink_5", - "CoordinateComponentLink_6", - "CoordinateComponentLink_7", - "CoordinateComponentLink_8", - "CoordinateComponentLink_9", - "CoordinateComponentLink_10", - "CoordinateComponentLink_11", - "CoordinateComponentLink_12", - "CoordinateComponentLink_13", - "CoordinateComponentLink_14" - ], - "subset_group_count": 0 - }, - "ImageLayerState": { - "_type": "glue.viewers.image.state.ImageLayerState", - "values": { - "alpha": 0.8, - "attribute": "array_0", - "bias": 0.5, - "cmap": "LinearSegmentedColormap", - "color": "st__0.35", - "contrast": 1.0, - "global_sync": true, - "layer": "array", - "percentile": 100, - "stretch": "st__linear", - "v_max": 119.0, - "v_min": 0.0, - "visible": true, - "zorder": 2 - } - }, - "ImageViewer": { - "_protocol": 1, - "_type": "glue.viewers.image.qt.data_viewer.ImageViewer", - "layers": [ - { - "_type": "glue.viewers.image.layer_artist.ImageLayerArtist", - "state": "ImageLayerState" - } - ], - "pos": [ - 0, - 0 - ], - "session": "Session", - "size": [ - 731, - 529 - ], - "state": { - "values": { - "aspect": "st__equal", - "color_mode": "st__Colormaps", - "layers": "CallbackList", - "reference_data": "array", - "slices": [ - 2, - 0, - 0, - 1 - ], - "x_att": "Pixel Axis 2", - "x_att_world": "World 2", - "x_log": false, - "x_max": 4.075455766965458, - "x_min": -2.932007545418843, - "y_att": "Pixel Axis 1", - "y_att_world": "World 1", - "y_log": false, - "y_max": 3.6720458592035916, - "y_min": -0.6781873651245816 - } - } - }, - "LinearSegmentedColormap": { - "_type": "matplotlib.colors.LinearSegmentedColormap", - "cmap": "gray" - }, - "Pixel Axis 0": { - "_type": "glue.core.component_id.PixelComponentID", - "axis": 0, - "hidden": true, - "label": "Pixel Axis 0" - }, - "Pixel Axis 1": { - "_type": "glue.core.component_id.PixelComponentID", - "axis": 1, - "hidden": true, - "label": "Pixel Axis 1" - }, - "Pixel Axis 2": { - "_type": "glue.core.component_id.PixelComponentID", - "axis": 2, - "hidden": true, - "label": "Pixel Axis 2" - }, - "Pixel Axis 3": { - "_type": "glue.core.component_id.PixelComponentID", - "axis": 3, - "hidden": true, - "label": "Pixel Axis 3" - }, - "Session": { - "_type": "glue.core.session.Session" - }, - "World 0": { - "_type": "glue.core.component_id.ComponentID", - "hidden": true, - "label": "World 0" - }, - "World 1": { - "_type": "glue.core.component_id.ComponentID", - "hidden": true, - "label": "World 1" - }, - "World 2": { - "_type": "glue.core.component_id.ComponentID", - "hidden": true, - "label": "World 2" - }, - "World 3": { - "_type": "glue.core.component_id.ComponentID", - "hidden": true, - "label": "World 3" - }, - "__main__": { - "_type": "glue.app.qt.application.GlueApplication", - "data": "DataCollection", - "plugins": [ - "glue.viewers.scatter", - "glue.viewers.table", - "glue_medical", - "specviz.app", - "glue.plugins.exporters.plotly", - "glue.core.data_exporters", - "glue.plugins.export_d3po", - "glue_vispy_viewers.volume", - "glue.plugins.tools.spectrum_tool", - "glue.plugins.coordinate_helpers", - "glue.viewers.histogram", - "glue.plugins.tools.pv_slicer", - "glue_vispy_viewers.scatter", - "glue.viewers.image" - ], - "session": "Session", - "tab_names": [ - "Tab 1" - ], - "viewers": [ - [ - "ImageViewer" - ] - ] - }, - "array": { - "_key_joins": [], - "_protocol": 5, - "_type": "glue.core.data.Data", - "components": [ - [ - "array_0", - "Component" - ], - [ - "Pixel Axis 0", - "CoordinateComponent" - ], - [ - "Pixel Axis 1", - "CoordinateComponent_0" - ], - [ - "Pixel Axis 2", - "CoordinateComponent_1" - ], - [ - "Pixel Axis 3", - "CoordinateComponent_2" - ], - [ - "World 0", - "CoordinateComponent_3" - ], - [ - "World 1", - "CoordinateComponent_4" - ], - [ - "World 2", - "CoordinateComponent_5" - ], - [ - "World 3", - "CoordinateComponent_6" - ] - ], - "coords": "Coordinates", - "label": "array", - "primary_owner": [ - "array_0", - "Pixel Axis 0", - "Pixel Axis 1", - "Pixel Axis 2", - "Pixel Axis 3", - "World 0", - "World 1", - "World 2", - "World 3" - ], - "style": { - "_type": "glue.core.visual.VisualAttributes", - "alpha": 0.8, - "color": "0.35", - "linestyle": "solid", - "linewidth": 1, - "marker": "o", - "markersize": 3 - }, - "subsets": [], - "uuid": "5e2335ce-5613-4fb1-aa58-1cf342f7b755" - }, - "array_0": { - "_type": "glue.core.component_id.ComponentID", - "hidden": false, - "label": "array" - } -} \ No newline at end of file diff --git a/glue/viewers/image/qt/tests/data/image_rgb_v0.glu b/glue/viewers/image/qt/tests/data/image_rgb_v0.glu deleted file mode 100644 index 167ce8a1e..000000000 --- a/glue/viewers/image/qt/tests/data/image_rgb_v0.glu +++ /dev/null @@ -1,337 +0,0 @@ -{ - "Component": { - "_type": "glue.core.component.Component", - "data": { - "_type": "numpy.ndarray", - "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGk4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDIsIDIpLCB9ICAgICAgICAgIAoAAAAAAAAAAAEAAAAAAAAAAgAAAAAAAAADAAAAAAAAAA==" - }, - "units": "" - }, - "Component_0": { - "_type": "glue.core.component.Component", - "data": { - "_type": "numpy.ndarray", - "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGk4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDIsIDIpLCB9ICAgICAgICAgIAoIAAAAAAAAAAkAAAAAAAAACgAAAAAAAAALAAAAAAAAAA==" - }, - "units": "" - }, - "Component_1": { - "_type": "glue.core.component.Component", - "data": { - "_type": "numpy.ndarray", - "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGk4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDIsIDIpLCB9ICAgICAgICAgIAoEAAAAAAAAAAUAAAAAAAAABgAAAAAAAAAHAAAAAAAAAA==" - }, - "units": "" - }, - "CoordinateComponent": { - "_type": "glue.core.component.CoordinateComponent", - "axis": 0, - "world": false - }, - "CoordinateComponentLink": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "Pixel Axis 0 [y]", - "Pixel Axis 1 [x]" - ], - "index": 0, - "pix2world": true, - "to": [ - "World 0" - ] - }, - "CoordinateComponentLink_0": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "World 0", - "World 1" - ], - "index": 1, - "pix2world": false, - "to": [ - "Pixel Axis 1 [x]" - ] - }, - "CoordinateComponentLink_1": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "World 0", - "World 1" - ], - "index": 0, - "pix2world": false, - "to": [ - "Pixel Axis 0 [y]" - ] - }, - "CoordinateComponentLink_2": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "Pixel Axis 0 [y]", - "Pixel Axis 1 [x]" - ], - "index": 1, - "pix2world": true, - "to": [ - "World 1" - ] - }, - "CoordinateComponent_0": { - "_type": "glue.core.component.CoordinateComponent", - "axis": 1, - "world": false - }, - "CoordinateComponent_1": { - "_type": "glue.core.component.CoordinateComponent", - "axis": 0, - "world": true - }, - "CoordinateComponent_2": { - "_type": "glue.core.component.CoordinateComponent", - "axis": 1, - "world": true - }, - "Coordinates": { - "_type": "glue.core.coordinates.Coordinates" - }, - "DS9Normalize": { - "_type": "glue.viewers.image.ds9norm.DS9Normalize", - "bias": 0.5, - "clip_hi": null, - "clip_lo": null, - "contrast": 1.0, - "stretch": "arcsinh", - "vmax": 3, - "vmin": 0 - }, - "DS9Normalize_0": { - "_type": "glue.viewers.image.ds9norm.DS9Normalize", - "bias": 0.5, - "clip_hi": 99, - "clip_lo": 1, - "contrast": 1.0, - "stretch": "linear", - "vmax": null, - "vmin": null - }, - "DS9Normalize_1": { - "_type": "glue.viewers.image.ds9norm.DS9Normalize", - "bias": 0.5, - "clip_hi": null, - "clip_lo": null, - "contrast": 1.0, - "stretch": "linear", - "vmax": 5.0, - "vmin": -5.0 - }, - "DataCollection": { - "_protocol": 3, - "_type": "glue.core.data_collection.DataCollection", - "cids": [ - "a", - "Pixel Axis 0 [y]", - "Pixel Axis 1 [x]", - "World 0", - "World 1", - "c", - "b" - ], - "components": [ - "Component", - "CoordinateComponent", - "CoordinateComponent_0", - "CoordinateComponent_1", - "CoordinateComponent_2", - "Component_0", - "Component_1" - ], - "data": [ - "rgbcube" - ], - "groups": [], - "links": [ - "CoordinateComponentLink", - "CoordinateComponentLink_0", - "CoordinateComponentLink_1", - "CoordinateComponentLink_2" - ], - "subset_group_count": 0 - }, - "ImageWidget": { - "_type": "glue.viewers.image.qt.viewer_widget.ImageWidget", - "layers": [ - { - "_type": "glue.viewers.image.layer_artist.RGBImageLayerArtist", - "b": "b", - "bnorm": "DS9Normalize", - "color_visible": [ - true, - false, - true - ], - "g": "c", - "gnorm": "DS9Normalize_0", - "layer": "rgbcube", - "norm": "DS9Normalize_0", - "r": "a", - "rnorm": "DS9Normalize_1", - "visible": true, - "zorder": 1 - } - ], - "pos": [ - 0, - 0 - ], - "properties": { - "attribute": "a", - "batt": "b", - "data": "rgbcube", - "gatt": "c", - "ratt": "a", - "rgb_mode": true, - "rgb_viz": [ - true, - false, - true - ], - "slice": [ - "y", - "x" - ] - }, - "session": "Session", - "size": [ - 600, - 400 - ] - }, - "Pixel Axis 0 [y]": { - "_type": "glue.core.component_id.PixelComponentID", - "axis": 0, - "hidden": true, - "label": "Pixel Axis 0 [y]" - }, - "Pixel Axis 1 [x]": { - "_type": "glue.core.component_id.PixelComponentID", - "axis": 1, - "hidden": true, - "label": "Pixel Axis 1 [x]" - }, - "Session": { - "_type": "glue.core.session.Session" - }, - "World 0": { - "_type": "glue.core.component_id.ComponentID", - "hidden": true, - "label": "World 0" - }, - "World 1": { - "_type": "glue.core.component_id.ComponentID", - "hidden": true, - "label": "World 1" - }, - "__main__": { - "_type": "glue.app.qt.application.GlueApplication", - "data": "DataCollection", - "plugins": [ - "glue.plugins.tools.pv_slicer", - "glue.viewers.histogram", - "glue.viewers.table", - "glue_vispy_viewers.volume", - "glue.plugins.exporters.plotly", - "glue.plugins.export_d3po", - "glue.viewers.image", - "glue.plugins.tools.spectrum_tool", - "glue_vispy_viewers.scatter", - "glue.viewers.scatter", - "glue.plugins.coordinate_helpers", - "glue.core.data_exporters" - ], - "session": "Session", - "tab_names": [ - "Tab 1" - ], - "viewers": [ - [ - "ImageWidget" - ] - ] - }, - "a": { - "_type": "glue.core.component_id.ComponentID", - "hidden": false, - "label": "a" - }, - "b": { - "_type": "glue.core.component_id.ComponentID", - "hidden": false, - "label": "b" - }, - "c": { - "_type": "glue.core.component_id.ComponentID", - "hidden": false, - "label": "c" - }, - "rgbcube": { - "_key_joins": [], - "_protocol": 5, - "_type": "glue.core.data.Data", - "components": [ - [ - "a", - "Component" - ], - [ - "Pixel Axis 0 [y]", - "CoordinateComponent" - ], - [ - "Pixel Axis 1 [x]", - "CoordinateComponent_0" - ], - [ - "World 0", - "CoordinateComponent_1" - ], - [ - "World 1", - "CoordinateComponent_2" - ], - [ - "c", - "Component_0" - ], - [ - "b", - "Component_1" - ] - ], - "coords": "Coordinates", - "label": "rgbcube", - "primary_owner": [ - "a", - "Pixel Axis 0 [y]", - "Pixel Axis 1 [x]", - "World 0", - "World 1", - "c", - "b" - ], - "style": { - "_type": "glue.core.visual.VisualAttributes", - "alpha": 0.8, - "color": "0.35", - "linestyle": "solid", - "linewidth": 1, - "marker": "o", - "markersize": 3 - }, - "subsets": [], - "uuid": "d2fdde54-ab42-4370-9670-4b9906da51f2" - } -} \ No newline at end of file diff --git a/glue/viewers/image/qt/tests/data/image_rgb_v1.glu b/glue/viewers/image/qt/tests/data/image_rgb_v1.glu deleted file mode 100644 index 7bd2a3b4f..000000000 --- a/glue/viewers/image/qt/tests/data/image_rgb_v1.glu +++ /dev/null @@ -1,434 +0,0 @@ -{ - "CallbackList": { - "_type": "glue.external.echo.list.CallbackList", - "values": [ - "ImageLayerState", - "ImageLayerState_0", - "ImageLayerState_1" - ] - }, - "Component": { - "_type": "glue.core.component.Component", - "data": { - "_type": "numpy.ndarray", - "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGk4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDIsIDIpLCB9ICAgICAgICAgIAoAAAAAAAAAAAEAAAAAAAAAAgAAAAAAAAADAAAAAAAAAA==" - }, - "units": "" - }, - "Component_0": { - "_type": "glue.core.component.Component", - "data": { - "_type": "numpy.ndarray", - "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGk4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDIsIDIpLCB9ICAgICAgICAgIAoIAAAAAAAAAAkAAAAAAAAACgAAAAAAAAALAAAAAAAAAA==" - }, - "units": "" - }, - "Component_1": { - "_type": "glue.core.component.Component", - "data": { - "_type": "numpy.ndarray", - "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGk4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDIsIDIpLCB9ICAgICAgICAgIAoEAAAAAAAAAAUAAAAAAAAABgAAAAAAAAAHAAAAAAAAAA==" - }, - "units": "" - }, - "CoordinateComponent": { - "_type": "glue.core.component.CoordinateComponent", - "axis": 0, - "world": false - }, - "CoordinateComponentLink": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "Pixel Axis 0 [y]", - "Pixel Axis 1 [x]" - ], - "index": 0, - "pix2world": true, - "to": [ - "World 0" - ] - }, - "CoordinateComponentLink_0": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "Pixel Axis 0 [y]", - "Pixel Axis 1 [x]" - ], - "index": 1, - "pix2world": true, - "to": [ - "World 1" - ] - }, - "CoordinateComponentLink_1": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "World 0", - "World 1" - ], - "index": 1, - "pix2world": false, - "to": [ - "Pixel Axis 1 [x]" - ] - }, - "CoordinateComponentLink_2": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "Pixel Axis 0 [y]", - "Pixel Axis 1 [x]" - ], - "index": 0, - "pix2world": true, - "to": [ - "World 0" - ] - }, - "CoordinateComponentLink_3": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "World 0", - "World 1" - ], - "index": 0, - "pix2world": false, - "to": [ - "Pixel Axis 0 [y]" - ] - }, - "CoordinateComponentLink_4": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "Pixel Axis 0 [y]", - "Pixel Axis 1 [x]" - ], - "index": 1, - "pix2world": true, - "to": [ - "World 1" - ] - }, - "CoordinateComponentLink_5": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "World 0", - "World 1" - ], - "index": 1, - "pix2world": false, - "to": [ - "Pixel Axis 1 [x]" - ] - }, - "CoordinateComponentLink_6": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "World 0", - "World 1" - ], - "index": 0, - "pix2world": false, - "to": [ - "Pixel Axis 0 [y]" - ] - }, - "CoordinateComponent_0": { - "_type": "glue.core.component.CoordinateComponent", - "axis": 1, - "world": false - }, - "CoordinateComponent_1": { - "_type": "glue.core.component.CoordinateComponent", - "axis": 0, - "world": true - }, - "CoordinateComponent_2": { - "_type": "glue.core.component.CoordinateComponent", - "axis": 1, - "world": true - }, - "Coordinates": { - "_type": "glue.core.coordinates.Coordinates" - }, - "DataCollection": { - "_protocol": 3, - "_type": "glue.core.data_collection.DataCollection", - "cids": [ - "a", - "Pixel Axis 0 [y]", - "Pixel Axis 1 [x]", - "World 0", - "World 1", - "c", - "b" - ], - "components": [ - "Component", - "CoordinateComponent", - "CoordinateComponent_0", - "CoordinateComponent_1", - "CoordinateComponent_2", - "Component_0", - "Component_1" - ], - "data": [ - "rgbcube" - ], - "groups": [], - "links": [ - "CoordinateComponentLink", - "CoordinateComponentLink_0", - "CoordinateComponentLink_1", - "CoordinateComponentLink_2", - "CoordinateComponentLink_3", - "CoordinateComponentLink_4", - "CoordinateComponentLink_5", - "CoordinateComponentLink_6" - ], - "subset_group_count": 0 - }, - "ImageLayerState": { - "_type": "glue.viewers.image.state.ImageLayerState", - "values": { - "alpha": 0.8, - "attribute": "a", - "bias": 0.5, - "cmap": "LinearSegmentedColormap", - "color": "st__r", - "contrast": 1.0, - "global_sync": false, - "layer": "rgbcube", - "percentile": "st__Custom", - "stretch": "st__linear", - "v_max": 5.0, - "v_min": -5.0, - "visible": true, - "zorder": 2 - } - }, - "ImageLayerState_0": { - "_type": "glue.viewers.image.state.ImageLayerState", - "values": { - "alpha": 0.8, - "attribute": "c", - "bias": 0.5, - "cmap": "LinearSegmentedColormap", - "color": "st__g", - "contrast": 1.0, - "global_sync": false, - "layer": "rgbcube", - "percentile": 99, - "stretch": "st__linear", - "v_max": 10.985, - "v_min": 8.015, - "visible": false, - "zorder": 3 - } - }, - "ImageLayerState_1": { - "_type": "glue.viewers.image.state.ImageLayerState", - "values": { - "alpha": 0.8, - "attribute": "b", - "bias": 0.5, - "cmap": "LinearSegmentedColormap", - "color": "st__b", - "contrast": 1.0, - "global_sync": false, - "layer": "rgbcube", - "percentile": "st__Custom", - "stretch": "st__arcsinh", - "v_max": 3, - "v_min": 0, - "visible": true, - "zorder": 4 - } - }, - "ImageViewer": { - "_protocol": 1, - "_type": "glue.viewers.image.qt.data_viewer.ImageViewer", - "layers": [ - { - "_type": "glue.viewers.image.layer_artist.ImageLayerArtist", - "state": "ImageLayerState" - }, - { - "_type": "glue.viewers.image.layer_artist.ImageLayerArtist", - "state": "ImageLayerState_0" - }, - { - "_type": "glue.viewers.image.layer_artist.ImageLayerArtist", - "state": "ImageLayerState_1" - } - ], - "pos": [ - 0, - 0 - ], - "session": "Session", - "size": [ - 600, - 400 - ], - "state": { - "values": { - "aspect": "st__equal", - "color_mode": "st__One color per layer", - "layers": "CallbackList", - "reference_data": "rgbcube", - "slices": [ - 0, - 0 - ], - "x_att": "Pixel Axis 1 [x]", - "x_att_world": "World 1", - "x_log": false, - "x_max": 2.5253556681900653, - "x_min": -1.4613891239424421, - "y_att": "Pixel Axis 0 [y]", - "y_att_world": "World 0", - "y_log": false, - "y_max": 1.555040949314538, - "y_min": -0.5351753331368783 - } - } - }, - "LinearSegmentedColormap": { - "_type": "matplotlib.colors.LinearSegmentedColormap", - "cmap": "gray" - }, - "Pixel Axis 0 [y]": { - "_type": "glue.core.component_id.PixelComponentID", - "axis": 0, - "hidden": true, - "label": "Pixel Axis 0 [y]" - }, - "Pixel Axis 1 [x]": { - "_type": "glue.core.component_id.PixelComponentID", - "axis": 1, - "hidden": true, - "label": "Pixel Axis 1 [x]" - }, - "Session": { - "_type": "glue.core.session.Session" - }, - "World 0": { - "_type": "glue.core.component_id.ComponentID", - "hidden": true, - "label": "World 0" - }, - "World 1": { - "_type": "glue.core.component_id.ComponentID", - "hidden": true, - "label": "World 1" - }, - "__main__": { - "_type": "glue.app.qt.application.GlueApplication", - "data": "DataCollection", - "plugins": [ - "glue.core.data_exporters", - "glue.viewers.image", - "glue.viewers.scatter", - "glue_vispy_viewers.volume", - "glue_medical", - "glue.plugins.tools.pv_slicer", - "glue.plugins.coordinate_helpers", - "glue.viewers.table", - "glue_vispy_viewers.scatter", - "glue.plugins.export_d3po", - "glue.viewers.histogram", - "glue.plugins.tools.spectrum_tool", - "glue.plugins.exporters.plotly", - "specviz.app" - ], - "session": "Session", - "tab_names": [ - "Tab 1" - ], - "viewers": [ - [ - "ImageViewer" - ] - ] - }, - "a": { - "_type": "glue.core.component_id.ComponentID", - "hidden": false, - "label": "a" - }, - "b": { - "_type": "glue.core.component_id.ComponentID", - "hidden": false, - "label": "b" - }, - "c": { - "_type": "glue.core.component_id.ComponentID", - "hidden": false, - "label": "c" - }, - "rgbcube": { - "_key_joins": [], - "_protocol": 5, - "_type": "glue.core.data.Data", - "components": [ - [ - "a", - "Component" - ], - [ - "Pixel Axis 0 [y]", - "CoordinateComponent" - ], - [ - "Pixel Axis 1 [x]", - "CoordinateComponent_0" - ], - [ - "World 0", - "CoordinateComponent_1" - ], - [ - "World 1", - "CoordinateComponent_2" - ], - [ - "c", - "Component_0" - ], - [ - "b", - "Component_1" - ] - ], - "coords": "Coordinates", - "label": "rgbcube", - "primary_owner": [ - "a", - "Pixel Axis 0 [y]", - "Pixel Axis 1 [x]", - "World 0", - "World 1", - "c", - "b" - ], - "style": { - "_type": "glue.core.visual.VisualAttributes", - "alpha": 0.8, - "color": "0.35", - "linestyle": "solid", - "linewidth": 1, - "marker": "o", - "markersize": 3 - }, - "subsets": [], - "uuid": "d2fdde54-ab42-4370-9670-4b9906da51f2" - } -} \ No newline at end of file diff --git a/glue/viewers/image/qt/tests/data/image_v0.glu b/glue/viewers/image/qt/tests/data/image_v0.glu deleted file mode 100644 index 7c1716413..000000000 --- a/glue/viewers/image/qt/tests/data/image_v0.glu +++ /dev/null @@ -1,724 +0,0 @@ -{ - "Component": { - "_type": "glue.core.component.Component", - "data": { - "_type": "numpy.ndarray", - "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGk4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDIsIDIpLCB9ICAgICAgICAgIAoBAAAAAAAAAAIAAAAAAAAAAwAAAAAAAAAEAAAAAAAAAA==" - }, - "units": "" - }, - "ComponentLink": { - "_type": "glue.core.component_link.ComponentLink", - "frm": [ - "World 0" - ], - "hidden": false, - "inverse": { - "_type": "types.FunctionType", - "function": "glue.core.component_link.identity" - }, - "to": [ - "a" - ], - "using": { - "_type": "types.FunctionType", - "function": "glue.core.component_link.identity" - } - }, - "ComponentLink_0": { - "_type": "glue.core.component_link.ComponentLink", - "frm": [ - "World 1" - ], - "hidden": false, - "inverse": { - "_type": "types.FunctionType", - "function": "glue.core.component_link.identity" - }, - "to": [ - "b" - ], - "using": { - "_type": "types.FunctionType", - "function": "glue.core.component_link.identity" - } - }, - "ComponentLink_1": { - "_type": "glue.core.component_link.ComponentLink", - "frm": [ - "b" - ], - "hidden": false, - "inverse": { - "_type": "types.FunctionType", - "function": "glue.core.component_link.identity" - }, - "to": [ - "World 1" - ], - "using": { - "_type": "types.FunctionType", - "function": "glue.core.component_link.identity" - } - }, - "ComponentLink_2": { - "_type": "glue.core.component_link.ComponentLink", - "frm": [ - "a" - ], - "hidden": false, - "inverse": { - "_type": "types.FunctionType", - "function": "glue.core.component_link.identity" - }, - "to": [ - "World 0" - ], - "using": { - "_type": "types.FunctionType", - "function": "glue.core.component_link.identity" - } - }, - "Component_0": { - "_type": "glue.core.component.Component", - "data": { - "_type": "numpy.ndarray", - "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGY4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDIsKSwgfSAgICAgICAgICAgIAoAAAAAAAD4PwAAAAAAAABA" - }, - "units": "" - }, - "Component_1": { - "_type": "glue.core.component.Component", - "data": { - "_type": "numpy.ndarray", - "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGY4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDIsKSwgfSAgICAgICAgICAgIAoAAAAAAADgPwAAAAAAAABA" - }, - "units": "" - }, - "CoordinateComponent": { - "_type": "glue.core.component.CoordinateComponent", - "axis": 0, - "world": false - }, - "CoordinateComponentLink": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "Pixel Axis 0 [y]", - "Pixel Axis 1 [x]" - ], - "index": 0, - "pix2world": true, - "to": [ - "World 0" - ] - }, - "CoordinateComponentLink_0": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates_0", - "frm": [ - "World 0_0" - ], - "index": 0, - "pix2world": false, - "to": [ - "Pixel Axis 0 [x]" - ] - }, - "CoordinateComponentLink_1": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "Pixel Axis 0 [y]", - "Pixel Axis 1 [x]" - ], - "index": 1, - "pix2world": true, - "to": [ - "World 1" - ] - }, - "CoordinateComponentLink_2": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "World 0", - "World 1" - ], - "index": 1, - "pix2world": false, - "to": [ - "Pixel Axis 1 [x]" - ] - }, - "CoordinateComponentLink_3": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "World 0", - "World 1" - ], - "index": 0, - "pix2world": false, - "to": [ - "Pixel Axis 0 [y]" - ] - }, - "CoordinateComponentLink_4": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates_0", - "frm": [ - "Pixel Axis 0 [x]" - ], - "index": 0, - "pix2world": true, - "to": [ - "World 0_0" - ] - }, - "CoordinateComponent_0": { - "_type": "glue.core.component.CoordinateComponent", - "axis": 1, - "world": false - }, - "CoordinateComponent_1": { - "_type": "glue.core.component.CoordinateComponent", - "axis": 0, - "world": true - }, - "CoordinateComponent_2": { - "_type": "glue.core.component.CoordinateComponent", - "axis": 1, - "world": true - }, - "CoordinateComponent_3": { - "_type": "glue.core.component.CoordinateComponent", - "axis": 0, - "world": false - }, - "CoordinateComponent_4": { - "_type": "glue.core.component.CoordinateComponent", - "axis": 0, - "world": true - }, - "Coordinates": { - "_type": "glue.core.coordinates.Coordinates" - }, - "Coordinates_0": { - "_type": "glue.core.coordinates.Coordinates" - }, - "DS9Normalize": { - "_type": "glue.viewers.image.ds9norm.DS9Normalize", - "bias": 0.5, - "clip_hi": 99.0, - "clip_lo": 1.0, - "contrast": 1.0, - "stretch": "sqrt", - "vmax": null, - "vmin": null - }, - "DS9Normalize_0": { - "_type": "glue.viewers.image.ds9norm.DS9Normalize", - "bias": 0.5, - "clip_hi": null, - "clip_lo": null, - "contrast": 1.0, - "stretch": "linear", - "vmax": 2.0, - "vmin": -2.0 - }, - "DS9Normalize_1": { - "_type": "glue.viewers.image.ds9norm.DS9Normalize", - "bias": 0.5, - "clip_hi": null, - "clip_lo": null, - "contrast": 1.0, - "stretch": "arcsinh", - "vmax": 4, - "vmin": 1 - }, - "DataCollection": { - "_protocol": 3, - "_type": "glue.core.data_collection.DataCollection", - "cids": [ - "data1_0", - "Pixel Axis 0 [y]", - "Pixel Axis 1 [x]", - "World 0", - "World 1", - "a", - "b", - "a", - "Pixel Axis 0 [x]", - "World 0_0", - "b", - "Pixel Axis 1 [x]", - "World 1", - "Pixel Axis 0 [y]", - "World 0" - ], - "components": [ - "Component", - "CoordinateComponent", - "CoordinateComponent_0", - "CoordinateComponent_1", - "CoordinateComponent_2", - "DerivedComponent", - "DerivedComponent_0", - "Component_0", - "CoordinateComponent_3", - "CoordinateComponent_4", - "Component_1", - "DerivedComponent_1", - "DerivedComponent_2", - "DerivedComponent_3", - "DerivedComponent_4" - ], - "data": [ - "data1", - "data2" - ], - "groups": [ - "Subset 1" - ], - "links": [ - "CoordinateComponentLink", - "CoordinateComponentLink_0", - "CoordinateComponentLink_1", - "ComponentLink", - "CoordinateComponentLink_2", - "CoordinateComponentLink_3", - "ComponentLink_0", - "CoordinateComponentLink_4" - ], - "subset_group_count": 1 - }, - "DerivedComponent": { - "_type": "glue.core.component.DerivedComponent", - "link": "ComponentLink" - }, - "DerivedComponent_0": { - "_type": "glue.core.component.DerivedComponent", - "link": "ComponentLink_0" - }, - "DerivedComponent_1": { - "_type": "glue.core.component.DerivedComponent", - "link": "CoordinateComponentLink_2" - }, - "DerivedComponent_2": { - "_type": "glue.core.component.DerivedComponent", - "link": "ComponentLink_1" - }, - "DerivedComponent_3": { - "_type": "glue.core.component.DerivedComponent", - "link": "CoordinateComponentLink_3" - }, - "DerivedComponent_4": { - "_type": "glue.core.component.DerivedComponent", - "link": "ComponentLink_2" - }, - "ImageWidget": { - "_type": "glue.viewers.image.qt.viewer_widget.ImageWidget", - "layers": [ - { - "_type": "glue.viewers.image.layer_artist.ImageLayerArtist", - "layer": "data1", - "norm": "DS9Normalize", - "visible": true, - "zorder": 1 - }, - { - "_type": "glue.viewers.scatter.layer_artist.ScatterLayerArtist", - "layer": "data2", - "visible": true, - "xatt": "Pixel Axis 1 [x]", - "yatt": "Pixel Axis 0 [y]", - "zorder": 2 - }, - { - "_type": "glue.viewers.image.layer_artist.SubsetImageLayerArtist", - "layer": "Subset 1_0", - "visible": false, - "zorder": 3 - } - ], - "pos": [ - -1, - 1 - ], - "properties": { - "attribute": "data1_0", - "batt": null, - "data": "data1", - "gatt": null, - "ratt": null, - "rgb_mode": false, - "rgb_viz": [ - true, - true, - true - ], - "slice": [ - "y", - "x" - ] - }, - "session": "Session", - "size": [ - 568, - 490 - ] - }, - "ImageWidget_0": { - "_type": "glue.viewers.image.qt.viewer_widget.ImageWidget", - "layers": [ - { - "_type": "glue.viewers.image.layer_artist.ImageLayerArtist", - "layer": "data1", - "norm": "DS9Normalize_1", - "visible": true, - "zorder": 1 - }, - { - "_type": "glue.viewers.image.layer_artist.SubsetImageLayerArtist", - "layer": "Subset 1_0", - "visible": true, - "zorder": 2 - } - ], - "pos": [ - 568, - 1 - ], - "properties": { - "attribute": "data1_0", - "batt": null, - "data": "data1", - "gatt": null, - "ratt": null, - "rgb_mode": false, - "rgb_viz": [ - true, - true, - true - ], - "slice": [ - "y", - "x" - ] - }, - "session": "Session", - "size": [ - 606, - 489 - ] - }, - "ImageWidget_1": { - "_type": "glue.viewers.image.qt.viewer_widget.ImageWidget", - "layers": [ - { - "_type": "glue.viewers.image.layer_artist.ImageLayerArtist", - "layer": "data1", - "norm": "DS9Normalize_0", - "visible": true, - "zorder": 1 - }, - { - "_type": "glue.viewers.image.layer_artist.SubsetImageLayerArtist", - "layer": "Subset 1_0", - "visible": true, - "zorder": 2 - } - ], - "pos": [ - 568, - 487 - ], - "properties": { - "attribute": "data1_0", - "batt": null, - "data": "data1", - "gatt": null, - "ratt": null, - "rgb_mode": false, - "rgb_viz": [ - true, - true, - true - ], - "slice": [ - "y", - "x" - ] - }, - "session": "Session", - "size": [ - 600, - 400 - ] - }, - "Pixel Axis 0 [x]": { - "_type": "glue.core.component_id.PixelComponentID", - "axis": 0, - "hidden": true, - "label": "Pixel Axis 0 [x]" - }, - "Pixel Axis 0 [y]": { - "_type": "glue.core.component_id.PixelComponentID", - "axis": 0, - "hidden": true, - "label": "Pixel Axis 0 [y]" - }, - "Pixel Axis 1 [x]": { - "_type": "glue.core.component_id.PixelComponentID", - "axis": 1, - "hidden": true, - "label": "Pixel Axis 1 [x]" - }, - "PolygonalROI": { - "_type": "glue.core.roi.PolygonalROI", - "vx": [ - 0.7380952380952381, - 1.847619047619048, - 1.847619047619048, - 0.7380952380952381, - 0.7380952380952381 - ], - "vy": [ - 0.5866666666666664, - 0.5866666666666664, - 1.7666666666666662, - 1.7666666666666662, - 0.5866666666666664 - ] - }, - "RoiSubsetState": { - "_type": "glue.core.subset.RoiSubsetState", - "roi": "PolygonalROI", - "xatt": "Pixel Axis 1 [x]", - "yatt": "Pixel Axis 0 [y]" - }, - "Session": { - "_type": "glue.core.session.Session" - }, - "Subset 1": { - "_type": "glue.core.subset_group.SubsetGroup", - "label": "Subset 1", - "state": "RoiSubsetState", - "style": { - "_type": "glue.core.visual.VisualAttributes", - "alpha": 0.5, - "color": "#e31a1c", - "linestyle": "solid", - "linewidth": 1, - "marker": "o", - "markersize": 7 - }, - "subsets": [ - "Subset 1_0", - "Subset 1_1" - ] - }, - "Subset 1_0": { - "_type": "glue.core.subset_group.GroupedSubset", - "group": "Subset 1", - "style": { - "_type": "glue.core.visual.VisualAttributes", - "alpha": 0.5, - "color": "#e31a1c", - "linestyle": "solid", - "linewidth": 1, - "marker": "o", - "markersize": 7 - } - }, - "Subset 1_1": { - "_type": "glue.core.subset_group.GroupedSubset", - "group": "Subset 1", - "style": { - "_type": "glue.core.visual.VisualAttributes", - "alpha": 0.5, - "color": "#e31a1c", - "linestyle": "solid", - "linewidth": 1, - "marker": "o", - "markersize": 7 - } - }, - "World 0": { - "_type": "glue.core.component_id.ComponentID", - "hidden": true, - "label": "World 0" - }, - "World 0_0": { - "_type": "glue.core.component_id.ComponentID", - "hidden": true, - "label": "World 0" - }, - "World 1": { - "_type": "glue.core.component_id.ComponentID", - "hidden": true, - "label": "World 1" - }, - "__main__": { - "_type": "glue.app.qt.application.GlueApplication", - "data": "DataCollection", - "plugins": [ - "glue.plugins.tools.pv_slicer", - "glue.viewers.histogram", - "glue.viewers.table", - "glue_vispy_viewers.volume", - "glue.plugins.exporters.plotly", - "glue.plugins.export_d3po", - "glue.viewers.image", - "glue.plugins.tools.spectrum_tool", - "glue_vispy_viewers.scatter", - "glue.viewers.scatter", - "glue.plugins.coordinate_helpers", - "glue.core.data_exporters" - ], - "session": "Session", - "tab_names": [ - "Tab 1" - ], - "viewers": [ - [ - "ImageWidget", - "ImageWidget_0", - "ImageWidget_1" - ] - ] - }, - "a": { - "_type": "glue.core.component_id.ComponentID", - "hidden": false, - "label": "a" - }, - "b": { - "_type": "glue.core.component_id.ComponentID", - "hidden": false, - "label": "b" - }, - "data1": { - "_key_joins": [], - "_protocol": 5, - "_type": "glue.core.data.Data", - "components": [ - [ - "data1_0", - "Component" - ], - [ - "Pixel Axis 0 [y]", - "CoordinateComponent" - ], - [ - "Pixel Axis 1 [x]", - "CoordinateComponent_0" - ], - [ - "World 0", - "CoordinateComponent_1" - ], - [ - "World 1", - "CoordinateComponent_2" - ], - [ - "a", - "DerivedComponent" - ], - [ - "b", - "DerivedComponent_0" - ] - ], - "coords": "Coordinates", - "label": "data1", - "primary_owner": [ - "data1_0", - "Pixel Axis 0 [y]", - "Pixel Axis 1 [x]", - "World 0", - "World 1" - ], - "style": { - "_type": "glue.core.visual.VisualAttributes", - "alpha": 0.8, - "color": "0.35", - "linestyle": "solid", - "linewidth": 1, - "marker": "o", - "markersize": 3 - }, - "subsets": [ - "Subset 1_0" - ], - "uuid": "daad10a3-6ad4-4dd4-841a-ca2071dbe467" - }, - "data1_0": { - "_type": "glue.core.component_id.ComponentID", - "hidden": false, - "label": "data1" - }, - "data2": { - "_key_joins": [], - "_protocol": 5, - "_type": "glue.core.data.Data", - "components": [ - [ - "a", - "Component_0" - ], - [ - "Pixel Axis 0 [x]", - "CoordinateComponent_3" - ], - [ - "World 0_0", - "CoordinateComponent_4" - ], - [ - "b", - "Component_1" - ], - [ - "Pixel Axis 1 [x]", - "DerivedComponent_1" - ], - [ - "World 1", - "DerivedComponent_2" - ], - [ - "Pixel Axis 0 [y]", - "DerivedComponent_3" - ], - [ - "World 0", - "DerivedComponent_4" - ] - ], - "coords": "Coordinates_0", - "label": "data2", - "primary_owner": [ - "a", - "Pixel Axis 0 [x]", - "World 0_0", - "b" - ], - "style": { - "_type": "glue.core.visual.VisualAttributes", - "alpha": 1.0, - "color": "#e60010", - "linestyle": "solid", - "linewidth": 1, - "marker": "^", - "markersize": 3 - }, - "subsets": [ - "Subset 1_1" - ], - "uuid": "e0521c8f-3f02-41b8-8746-a71aa9ae5f49" - } -} diff --git a/glue/viewers/image/qt/tests/data/image_v1.glu b/glue/viewers/image/qt/tests/data/image_v1.glu deleted file mode 100644 index 0a0b98062..000000000 --- a/glue/viewers/image/qt/tests/data/image_v1.glu +++ /dev/null @@ -1,903 +0,0 @@ -{ - "CallbackList": { - "_type": "glue.external.echo.list.CallbackList", - "values": [ - "ImageLayerState", - "ScatterLayerState", - "ImageSubsetLayerState" - ] - }, - "CallbackList_0": { - "_type": "glue.external.echo.list.CallbackList", - "values": [ - "ImageLayerState_0", - "ImageSubsetLayerState_0" - ] - }, - "CallbackList_1": { - "_type": "glue.external.echo.list.CallbackList", - "values": [ - "ImageLayerState_1", - "ImageSubsetLayerState_1" - ] - }, - "Component": { - "_type": "glue.core.component.Component", - "data": { - "_type": "numpy.ndarray", - "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGk4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDIsIDIpLCB9ICAgICAgICAgIAoBAAAAAAAAAAIAAAAAAAAAAwAAAAAAAAAEAAAAAAAAAA==" - }, - "units": "" - }, - "ComponentLink": { - "_type": "glue.core.component_link.ComponentLink", - "frm": [ - "a" - ], - "hidden": false, - "inverse": { - "_type": "types.FunctionType", - "function": "glue.core.component_link.identity" - }, - "to": [ - "World 0" - ], - "using": { - "_type": "types.FunctionType", - "function": "glue.core.component_link.identity" - } - }, - "ComponentLink_0": { - "_type": "glue.core.component_link.ComponentLink", - "frm": [ - "World 1" - ], - "hidden": false, - "inverse": { - "_type": "types.FunctionType", - "function": "glue.core.component_link.identity" - }, - "to": [ - "b" - ], - "using": { - "_type": "types.FunctionType", - "function": "glue.core.component_link.identity" - } - }, - "ComponentLink_1": { - "_type": "glue.core.component_link.ComponentLink", - "frm": [ - "World 0" - ], - "hidden": false, - "inverse": { - "_type": "types.FunctionType", - "function": "glue.core.component_link.identity" - }, - "to": [ - "a" - ], - "using": { - "_type": "types.FunctionType", - "function": "glue.core.component_link.identity" - } - }, - "ComponentLink_2": { - "_type": "glue.core.component_link.ComponentLink", - "frm": [ - "b" - ], - "hidden": false, - "inverse": { - "_type": "types.FunctionType", - "function": "glue.core.component_link.identity" - }, - "to": [ - "World 1" - ], - "using": { - "_type": "types.FunctionType", - "function": "glue.core.component_link.identity" - } - }, - "Component_0": { - "_type": "glue.core.component.Component", - "data": { - "_type": "numpy.ndarray", - "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGY4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDIsKSwgfSAgICAgICAgICAgIAoAAAAAAAD4PwAAAAAAAABA" - }, - "units": "" - }, - "Component_1": { - "_type": "glue.core.component.Component", - "data": { - "_type": "numpy.ndarray", - "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGY4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDIsKSwgfSAgICAgICAgICAgIAoAAAAAAADgPwAAAAAAAABA" - }, - "units": "" - }, - "CoordinateComponent": { - "_type": "glue.core.component.CoordinateComponent", - "axis": 0, - "world": false - }, - "CoordinateComponentLink": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "Pixel Axis 0 [y]", - "Pixel Axis 1 [x]" - ], - "index": 0, - "pix2world": true, - "to": [ - "World 0" - ] - }, - "CoordinateComponentLink_0": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates_0", - "frm": [ - "World 0_0" - ], - "index": 0, - "pix2world": false, - "to": [ - "Pixel Axis 0 [x]" - ] - }, - "CoordinateComponentLink_1": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates_0", - "frm": [ - "Pixel Axis 0 [x]" - ], - "index": 0, - "pix2world": true, - "to": [ - "World 0_0" - ] - }, - "CoordinateComponentLink_10": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "World 0", - "World 1" - ], - "index": 1, - "pix2world": false, - "to": [ - "Pixel Axis 1 [x]" - ] - }, - "CoordinateComponentLink_2": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "Pixel Axis 0 [y]", - "Pixel Axis 1 [x]" - ], - "index": 1, - "pix2world": true, - "to": [ - "World 1" - ] - }, - "CoordinateComponentLink_3": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "World 0", - "World 1" - ], - "index": 1, - "pix2world": false, - "to": [ - "Pixel Axis 1 [x]" - ] - }, - "CoordinateComponentLink_4": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates_0", - "frm": [ - "Pixel Axis 0 [x]" - ], - "index": 0, - "pix2world": true, - "to": [ - "World 0_0" - ] - }, - "CoordinateComponentLink_5": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "Pixel Axis 0 [y]", - "Pixel Axis 1 [x]" - ], - "index": 1, - "pix2world": true, - "to": [ - "World 1" - ] - }, - "CoordinateComponentLink_6": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates_0", - "frm": [ - "World 0_0" - ], - "index": 0, - "pix2world": false, - "to": [ - "Pixel Axis 0 [x]" - ] - }, - "CoordinateComponentLink_7": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "World 0", - "World 1" - ], - "index": 0, - "pix2world": false, - "to": [ - "Pixel Axis 0 [y]" - ] - }, - "CoordinateComponentLink_8": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "Pixel Axis 0 [y]", - "Pixel Axis 1 [x]" - ], - "index": 0, - "pix2world": true, - "to": [ - "World 0" - ] - }, - "CoordinateComponentLink_9": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "World 0", - "World 1" - ], - "index": 0, - "pix2world": false, - "to": [ - "Pixel Axis 0 [y]" - ] - }, - "CoordinateComponent_0": { - "_type": "glue.core.component.CoordinateComponent", - "axis": 1, - "world": false - }, - "CoordinateComponent_1": { - "_type": "glue.core.component.CoordinateComponent", - "axis": 0, - "world": true - }, - "CoordinateComponent_2": { - "_type": "glue.core.component.CoordinateComponent", - "axis": 1, - "world": true - }, - "CoordinateComponent_3": { - "_type": "glue.core.component.CoordinateComponent", - "axis": 0, - "world": false - }, - "CoordinateComponent_4": { - "_type": "glue.core.component.CoordinateComponent", - "axis": 0, - "world": true - }, - "Coordinates": { - "_type": "glue.core.coordinates.Coordinates" - }, - "Coordinates_0": { - "_type": "glue.core.coordinates.Coordinates" - }, - "DataCollection": { - "_protocol": 3, - "_type": "glue.core.data_collection.DataCollection", - "cids": [ - "data1_0", - "Pixel Axis 0 [y]", - "Pixel Axis 1 [x]", - "World 0", - "World 1", - "a", - "b", - "a", - "Pixel Axis 0 [x]", - "World 0_0", - "b", - "Pixel Axis 1 [x]", - "World 1", - "Pixel Axis 0 [y]", - "World 0" - ], - "components": [ - "Component", - "CoordinateComponent", - "CoordinateComponent_0", - "CoordinateComponent_1", - "CoordinateComponent_2", - "DerivedComponent", - "DerivedComponent_0", - "Component_0", - "CoordinateComponent_3", - "CoordinateComponent_4", - "Component_1", - "DerivedComponent_1", - "DerivedComponent_2", - "DerivedComponent_3", - "DerivedComponent_4" - ], - "data": [ - "data1", - "data2" - ], - "groups": [ - "Subset 1" - ], - "links": [ - "ComponentLink", - "ComponentLink_0", - "CoordinateComponentLink", - "CoordinateComponentLink_0", - "ComponentLink_1", - "CoordinateComponentLink_1", - "CoordinateComponentLink_2", - "CoordinateComponentLink_3", - "CoordinateComponentLink_4", - "CoordinateComponentLink_5", - "CoordinateComponentLink_6", - "CoordinateComponentLink_7", - "CoordinateComponentLink_8", - "ComponentLink_2", - "CoordinateComponentLink_9", - "CoordinateComponentLink_10" - ], - "subset_group_count": 1 - }, - "DerivedComponent": { - "_type": "glue.core.component.DerivedComponent", - "link": "ComponentLink_1" - }, - "DerivedComponent_0": { - "_type": "glue.core.component.DerivedComponent", - "link": "ComponentLink_0" - }, - "DerivedComponent_1": { - "_type": "glue.core.component.DerivedComponent", - "link": "CoordinateComponentLink_3" - }, - "DerivedComponent_2": { - "_type": "glue.core.component.DerivedComponent", - "link": "ComponentLink_2" - }, - "DerivedComponent_3": { - "_type": "glue.core.component.DerivedComponent", - "link": "CoordinateComponentLink_7" - }, - "DerivedComponent_4": { - "_type": "glue.core.component.DerivedComponent", - "link": "ComponentLink" - }, - "ImageLayerState": { - "_type": "glue.viewers.image.state.ImageLayerState", - "values": { - "alpha": 0.8, - "attribute": "data1_0", - "bias": 0.5, - "cmap": "LinearSegmentedColormap", - "color": "st__0.35", - "contrast": 1.0, - "global_sync": true, - "layer": "data1", - "percentile": 99, - "stretch": "st__sqrt", - "v_max": 3.985, - "v_min": 1.015, - "visible": true, - "zorder": 2 - } - }, - "ImageLayerState_0": { - "_type": "glue.viewers.image.state.ImageLayerState", - "values": { - "alpha": 0.8, - "attribute": "data1_0", - "bias": 0.5, - "cmap": "LinearSegmentedColormap", - "color": "st__0.35", - "contrast": 1.0, - "global_sync": true, - "layer": "data1", - "percentile": "st__Custom", - "stretch": "st__arcsinh", - "v_max": 4, - "v_min": 1, - "visible": true, - "zorder": 2 - } - }, - "ImageLayerState_1": { - "_type": "glue.viewers.image.state.ImageLayerState", - "values": { - "alpha": 0.8, - "attribute": "data1_0", - "bias": 0.5, - "cmap": "LinearSegmentedColormap", - "color": "st__0.35", - "contrast": 1.0, - "global_sync": true, - "layer": "data1", - "percentile": "st__Custom", - "stretch": "st__linear", - "v_max": 2.0, - "v_min": -2.0, - "visible": true, - "zorder": 2 - } - }, - "ImageSubsetLayerState": { - "_type": "glue.viewers.image.state.ImageSubsetLayerState", - "values": { - "alpha": 0.5, - "color": "st__#e31a1c", - "layer": "Subset 1_0", - "visible": false, - "zorder": 4 - } - }, - "ImageSubsetLayerState_0": { - "_type": "glue.viewers.image.state.ImageSubsetLayerState", - "values": { - "alpha": 0.5, - "color": "st__#e31a1c", - "layer": "Subset 1_0", - "visible": true, - "zorder": 3 - } - }, - "ImageSubsetLayerState_1": { - "_type": "glue.viewers.image.state.ImageSubsetLayerState", - "values": { - "alpha": 0.5, - "color": "st__#e31a1c", - "layer": "Subset 1_0", - "visible": true, - "zorder": 3 - } - }, - "ImageViewer": { - "_protocol": 1, - "_type": "glue.viewers.image.qt.data_viewer.ImageViewer", - "layers": [ - { - "_type": "glue.viewers.image.layer_artist.ImageLayerArtist", - "state": "ImageLayerState" - }, - { - "_type": "glue.viewers.scatter.layer_artist.ScatterLayerArtist", - "state": "ScatterLayerState" - }, - { - "_type": "glue.viewers.image.layer_artist.ImageSubsetLayerArtist", - "state": "ImageSubsetLayerState" - } - ], - "pos": [ - -1, - 1 - ], - "session": "Session", - "size": [ - 568, - 490 - ], - "state": { - "values": { - "aspect": "st__equal", - "color_mode": "st__Colormaps", - "layers": "CallbackList", - "reference_data": "data1", - "slices": [ - 0, - 0 - ], - "x_att": "Pixel Axis 1 [x]", - "x_att_world": "World 1", - "x_log": false, - "x_max": 1.937, - "x_min": -0.936, - "y_att": "Pixel Axis 0 [y]", - "y_att_world": "World 0", - "y_log": false, - "y_max": 1.5, - "y_min": -0.5 - } - } - }, - "ImageViewer_0": { - "_protocol": 1, - "_type": "glue.viewers.image.qt.data_viewer.ImageViewer", - "layers": [ - { - "_type": "glue.viewers.image.layer_artist.ImageLayerArtist", - "state": "ImageLayerState_0" - }, - { - "_type": "glue.viewers.image.layer_artist.ImageSubsetLayerArtist", - "state": "ImageSubsetLayerState_0" - } - ], - "pos": [ - 568, - 1 - ], - "session": "Session", - "size": [ - 606, - 489 - ], - "state": { - "values": { - "aspect": "st__equal", - "color_mode": "st__Colormaps", - "layers": "CallbackList_0", - "reference_data": "data1", - "slices": [ - 0, - 0 - ], - "x_att": "Pixel Axis 1 [x]", - "x_att_world": "World 1", - "x_log": false, - "x_max": 1.937, - "x_min": -0.936, - "y_att": "Pixel Axis 0 [y]", - "y_att_world": "World 0", - "y_log": false, - "y_max": 1.5, - "y_min": -0.5 - } - } - }, - "ImageViewer_1": { - "_protocol": 1, - "_type": "glue.viewers.image.qt.data_viewer.ImageViewer", - "layers": [ - { - "_type": "glue.viewers.image.layer_artist.ImageLayerArtist", - "state": "ImageLayerState_1" - }, - { - "_type": "glue.viewers.image.layer_artist.ImageSubsetLayerArtist", - "state": "ImageSubsetLayerState_1" - } - ], - "pos": [ - 569, - 488 - ], - "session": "Session", - "size": [ - 600, - 400 - ], - "state": { - "values": { - "aspect": "st__equal", - "color_mode": "st__Colormaps", - "layers": "CallbackList_1", - "reference_data": "data1", - "slices": [ - 0, - 0 - ], - "x_att": "Pixel Axis 1 [x]", - "x_att_world": "World 1", - "x_log": false, - "x_max": 1.937, - "x_min": -0.936, - "y_att": "Pixel Axis 0 [y]", - "y_att_world": "World 0", - "y_log": false, - "y_max": 1.5, - "y_min": -0.5 - } - } - }, - "LinearSegmentedColormap": { - "_type": "matplotlib.colors.LinearSegmentedColormap", - "cmap": "gray" - }, - "Pixel Axis 0 [x]": { - "_type": "glue.core.component_id.PixelComponentID", - "axis": 0, - "hidden": true, - "label": "Pixel Axis 0 [x]" - }, - "Pixel Axis 0 [y]": { - "_type": "glue.core.component_id.PixelComponentID", - "axis": 0, - "hidden": true, - "label": "Pixel Axis 0 [y]" - }, - "Pixel Axis 1 [x]": { - "_type": "glue.core.component_id.PixelComponentID", - "axis": 1, - "hidden": true, - "label": "Pixel Axis 1 [x]" - }, - "PolygonalROI": { - "_type": "glue.core.roi.PolygonalROI", - "vx": [ - 0.7380952380952381, - 1.847619047619048, - 1.847619047619048, - 0.7380952380952381, - 0.7380952380952381 - ], - "vy": [ - 0.5866666666666664, - 0.5866666666666664, - 1.7666666666666662, - 1.7666666666666662, - 0.5866666666666664 - ] - }, - "RoiSubsetState": { - "_type": "glue.core.subset.RoiSubsetState", - "roi": "PolygonalROI", - "xatt": "Pixel Axis 1 [x]", - "yatt": "Pixel Axis 0 [y]" - }, - "ScatterLayerState": { - "_type": "glue.viewers.scatter.state.ScatterLayerState", - "values": { - "alpha": 1.0, - "color": "st__#e60010", - "layer": "data2", - "size": 3, - "visible": true, - "zorder": 3 - } - }, - "Session": { - "_type": "glue.core.session.Session" - }, - "Subset 1": { - "_type": "glue.core.subset_group.SubsetGroup", - "label": "Subset 1", - "state": "RoiSubsetState", - "style": { - "_type": "glue.core.visual.VisualAttributes", - "alpha": 0.5, - "color": "#e31a1c", - "linestyle": "solid", - "linewidth": 1, - "marker": "o", - "markersize": 7 - }, - "subsets": [ - "Subset 1_0", - "Subset 1_1" - ] - }, - "Subset 1_0": { - "_type": "glue.core.subset_group.GroupedSubset", - "group": "Subset 1", - "style": { - "_type": "glue.core.visual.VisualAttributes", - "alpha": 0.5, - "color": "#e31a1c", - "linestyle": "solid", - "linewidth": 1, - "marker": "o", - "markersize": 7 - } - }, - "Subset 1_1": { - "_type": "glue.core.subset_group.GroupedSubset", - "group": "Subset 1", - "style": { - "_type": "glue.core.visual.VisualAttributes", - "alpha": 0.5, - "color": "#e31a1c", - "linestyle": "solid", - "linewidth": 1, - "marker": "o", - "markersize": 7 - } - }, - "World 0": { - "_type": "glue.core.component_id.ComponentID", - "hidden": true, - "label": "World 0" - }, - "World 0_0": { - "_type": "glue.core.component_id.ComponentID", - "hidden": true, - "label": "World 0" - }, - "World 1": { - "_type": "glue.core.component_id.ComponentID", - "hidden": true, - "label": "World 1" - }, - "__main__": { - "_type": "glue.app.qt.application.GlueApplication", - "data": "DataCollection", - "plugins": [ - "glue.viewers.image", - "glue.plugins.exporters.plotly", - "specviz.app", - "glue.viewers.scatter", - "glue.viewers.histogram", - "glue.core.data_exporters", - "glue.plugins.tools.spectrum_tool", - "glue.viewers.table", - "glue_medical", - "glue_vispy_viewers.scatter", - "glue_vispy_viewers.volume", - "glue.plugins.coordinate_helpers", - "glue.plugins.tools.pv_slicer", - "glue.plugins.export_d3po" - ], - "session": "Session", - "tab_names": [ - "Tab 1" - ], - "viewers": [ - [ - "ImageViewer", - "ImageViewer_0", - "ImageViewer_1" - ] - ] - }, - "a": { - "_type": "glue.core.component_id.ComponentID", - "hidden": false, - "label": "a" - }, - "b": { - "_type": "glue.core.component_id.ComponentID", - "hidden": false, - "label": "b" - }, - "data1": { - "_key_joins": [], - "_protocol": 5, - "_type": "glue.core.data.Data", - "components": [ - [ - "data1_0", - "Component" - ], - [ - "Pixel Axis 0 [y]", - "CoordinateComponent" - ], - [ - "Pixel Axis 1 [x]", - "CoordinateComponent_0" - ], - [ - "World 0", - "CoordinateComponent_1" - ], - [ - "World 1", - "CoordinateComponent_2" - ], - [ - "a", - "DerivedComponent" - ], - [ - "b", - "DerivedComponent_0" - ] - ], - "coords": "Coordinates", - "label": "data1", - "primary_owner": [ - "data1_0", - "Pixel Axis 0 [y]", - "Pixel Axis 1 [x]", - "World 0", - "World 1" - ], - "style": { - "_type": "glue.core.visual.VisualAttributes", - "alpha": 0.8, - "color": "0.35", - "linestyle": "solid", - "linewidth": 1, - "marker": "o", - "markersize": 3 - }, - "subsets": [ - "Subset 1_0" - ], - "uuid": "daad10a3-6ad4-4dd4-841a-ca2071dbe467" - }, - "data1_0": { - "_type": "glue.core.component_id.ComponentID", - "hidden": false, - "label": "data1" - }, - "data2": { - "_key_joins": [], - "_protocol": 5, - "_type": "glue.core.data.Data", - "components": [ - [ - "a", - "Component_0" - ], - [ - "Pixel Axis 0 [x]", - "CoordinateComponent_3" - ], - [ - "World 0_0", - "CoordinateComponent_4" - ], - [ - "b", - "Component_1" - ], - [ - "Pixel Axis 1 [x]", - "DerivedComponent_1" - ], - [ - "World 1", - "DerivedComponent_2" - ], - [ - "Pixel Axis 0 [y]", - "DerivedComponent_3" - ], - [ - "World 0", - "DerivedComponent_4" - ] - ], - "coords": "Coordinates_0", - "label": "data2", - "primary_owner": [ - "a", - "Pixel Axis 0 [x]", - "World 0_0", - "b" - ], - "style": { - "_type": "glue.core.visual.VisualAttributes", - "alpha": 1.0, - "color": "#e60010", - "linestyle": "solid", - "linewidth": 1, - "marker": "^", - "markersize": 3 - }, - "subsets": [ - "Subset 1_1" - ], - "uuid": "e0521c8f-3f02-41b8-8746-a71aa9ae5f49" - } -} diff --git a/glue/viewers/image/qt/tests/test_data_viewer.py b/glue/viewers/image/qt/tests/test_data_viewer.py deleted file mode 100644 index c4a5031c7..000000000 --- a/glue/viewers/image/qt/tests/test_data_viewer.py +++ /dev/null @@ -1,973 +0,0 @@ -# pylint: disable=I0011,W0613,W0201,W0212,E1101,E1103 - -import os -import gc -from collections import Counter - -import pytest - -from astropy.wcs import WCS - -import numpy as np -from numpy.testing import assert_allclose - -from glue.viewers.image.frb_artist import FRBArtist -from glue.core.coordinates import IdentityCoordinates -from glue.core.message import SubsetUpdateMessage -from glue.core import HubListener, Data -from glue.core.roi import XRangeROI, RectangularROI -from glue.core.subset import RoiSubsetState -from glue.utils.qt import combo_as_string, process_events -from glue.viewers.matplotlib.qt.tests.test_data_viewer import BaseTestMatplotlibDataViewer -from glue.core.state import GlueUnSerializer -from glue.app.qt.layer_tree_widget import LayerTreeWidget -from glue.viewers.scatter.state import ScatterLayerState -from glue.viewers.image.state import ImageLayerState, ImageSubsetLayerState, AggregateSlice -from glue.core.link_helpers import LinkSame -from glue.app.qt import GlueApplication -from glue.core.fixed_resolution_buffer import ARRAY_CACHE, PIXEL_CACHE -from glue.core.data_derived import IndexedData - -from ..data_viewer import ImageViewer - -DATA = os.path.join(os.path.dirname(__file__), 'data') - - -class TestImageCommon(BaseTestMatplotlibDataViewer): - - def init_data(self): - return Data(label='d1', x=np.arange(24).reshape((2, 3, 4)), y=np.ones((2, 3, 4))) - - viewer_cls = ImageViewer - - @pytest.mark.skip() - def test_double_add_ignored(self): - pass - - def test_update_data_processed_if_data_present(self): - - # Patch for the main test of the same name - we need to explicilty set - # global_sync to True here for things to work correctly. - - self.init_draw_count() - self.data_collection.append(self.data) - self.viewer.add_data(self.data) - ct0 = self.draw_count - self.viewer.state.layers[0].global_sync = True - self.data.style.color = 'blue' - assert self.draw_count > ct0 - - def test_slice_change_single_draw(self): - - # Regression test for a bug that caused Matplotlib to draw once per - # data/subset when changing slices. - - self.viewer.add_data(self.data) - - self.data_collection.new_subset_group(label='a', subset_state=self.data.id['x'] > 1) - self.data_collection.new_subset_group(label='b', subset_state=self.data.id['x'] > 2) - self.data_collection.new_subset_group(label='c', subset_state=self.data.id['x'] > 3) - - self.init_draw_count() - - assert self.draw_count == 0 - self.viewer.state.slices = (1, 1, 1) - assert self.draw_count == 1 - - -class MyCoords(IdentityCoordinates): - - def __init__(self, n_dim=2): - super().__init__(n_dim=n_dim) - - @property - def world_axis_names(self): - return ['Apple', 'Banana'] - - -class TestImageViewer(object): - - def setup_method(self, method): - - self.coords = MyCoords() - self.image1 = Data(label='image1', x=[[1, 2], [3, 4]], y=[[4, 5], [2, 3]]) - self.image2 = Data(label='image2', a=[[3, 3], [2, 2]], b=[[4, 4], [3, 2]], - coords=self.coords) - self.catalog = Data(label='catalog', c=[1, 3, 2], d=[4, 3, 3]) - self.hypercube = Data(label='hypercube', x=np.arange(120).reshape((2, 3, 4, 5))) - - # Create data versions with WCS coordinates - self.image1_wcs = Data(label='image1_wcs', x=self.image1['x'], - coords=WCS(naxis=2)) - self.hypercube_wcs = Data(label='hypercube_wcs', x=self.hypercube['x'], - coords=WCS(naxis=4)) - - self.application = GlueApplication() - - self.session = self.application.session - - self.hub = self.session.hub - - self.data_collection = self.session.data_collection - self.data_collection.append(self.image1) - self.data_collection.append(self.image2) - self.data_collection.append(self.catalog) - self.data_collection.append(self.hypercube) - self.data_collection.append(self.image1_wcs) - self.data_collection.append(self.hypercube_wcs) - - self.viewer = self.application.new_data_viewer(ImageViewer) - - self.data_collection.register_to_hub(self.hub) - self.viewer.register_to_hub(self.hub) - - self.options_widget = self.viewer.options_widget() - - def teardown_method(self, method): - - # Properly close viewer and application - self.viewer.close() - self.viewer = None - self.application.close() - self.application = None - - # Make sure cache is empty - if len(PIXEL_CACHE) > 0: - raise Exception("Pixel cache contains {0} elements".format(len(PIXEL_CACHE))) - if len(ARRAY_CACHE) > 0: - raise Exception("Array cache contains {0} elements".format(len(ARRAY_CACHE))) - - def test_basic(self): - - # Check defaults when we add data - - self.viewer.add_data(self.image1) - - assert combo_as_string(self.options_widget.ui.combosel_x_att_world) == 'Coordinate components:Pixel Axis 0 [y]:Pixel Axis 1 [x]' - assert combo_as_string(self.options_widget.ui.combosel_y_att_world) == 'Coordinate components:Pixel Axis 0 [y]:Pixel Axis 1 [x]' - - assert self.viewer.axes.get_xlabel() == 'Pixel Axis 1 [x]' - assert self.viewer.state.x_att_world is self.image1.id['Pixel Axis 1 [x]'] - assert self.viewer.state.x_att is self.image1.pixel_component_ids[1] - assert_allclose(self.viewer.state.x_min, -0.8419913419913423) - assert_allclose(self.viewer.state.x_max, +1.8419913419913423) - - assert self.viewer.axes.get_ylabel() == 'Pixel Axis 0 [y]' - assert self.viewer.state.y_att_world is self.image1.id['Pixel Axis 0 [y]'] - assert self.viewer.state.y_att is self.image1.pixel_component_ids[0] - assert self.viewer.state.y_min == -0.5 - assert self.viewer.state.y_max == +1.5 - - assert not self.viewer.state.x_log - assert not self.viewer.state.y_log - - assert len(self.viewer.state.layers) == 1 - - def test_custom_coords(self): - - # Check defaults when we add data with coordinates - - self.viewer.add_data(self.image2) - - assert combo_as_string(self.options_widget.ui.combosel_x_att_world) == 'Coordinate components:Banana:Apple' - assert combo_as_string(self.options_widget.ui.combosel_x_att_world) == 'Coordinate components:Banana:Apple' - - assert self.viewer.axes.get_xlabel() == 'Apple' - assert self.viewer.state.x_att_world is self.image2.id['Apple'] - assert self.viewer.state.x_att is self.image2.pixel_component_ids[1] - assert self.viewer.axes.get_ylabel() == 'Banana' - assert self.viewer.state.y_att_world is self.image2.id['Banana'] - assert self.viewer.state.y_att is self.image2.pixel_component_ids[0] - - def test_flip(self): - - self.viewer.add_data(self.image1) - - x_min_start = self.viewer.state.x_min - x_max_start = self.viewer.state.x_max - - self.options_widget.button_flip_x.click() - - assert self.viewer.state.x_min == x_max_start - assert self.viewer.state.x_max == x_min_start - - y_min_start = self.viewer.state.y_min - y_max_start = self.viewer.state.y_max - - self.options_widget.button_flip_y.click() - - assert self.viewer.state.y_min == y_max_start - assert self.viewer.state.y_max == y_min_start - - def test_combo_updates_with_component_add(self): - self.viewer.add_data(self.image1) - self.image1.add_component([[9, 9], [8, 8]], 'z') - assert self.viewer.state.x_att_world is self.image1.id['Pixel Axis 1 [x]'] - assert self.viewer.state.y_att_world is self.image1.id['Pixel Axis 0 [y]'] - # TODO: there should be an easier way to do this - layer_style_editor = self.viewer._view.layout_style_widgets[self.viewer.layers[0]] - assert combo_as_string(layer_style_editor.ui.combosel_attribute) == 'x:y:z' - - def test_apply_roi(self): - - self.viewer.add_data(self.image1) - - roi = RectangularROI(0.4, 1.6, -0.6, 0.6) - - assert len(self.viewer.layers) == 1 - - self.viewer.apply_roi(roi) - - assert len(self.viewer.layers) == 2 - assert len(self.image1.subsets) == 1 - - assert_allclose(self.image1.subsets[0].to_mask(), [[0, 1], [0, 0]]) - - state = self.image1.subsets[0].subset_state - assert isinstance(state, RoiSubsetState) - - def test_apply_roi_empty(self): - # Make sure that doing an ROI selection on an empty viewer doesn't - # produce error messsages - roi = XRangeROI(-0.2, 0.1) - self.viewer.apply_roi(roi) - - def test_identical(self): - - # Check what happens if we set both attributes to the same coordinates - - self.viewer.add_data(self.image2) - - assert self.viewer.state.x_att_world is self.image2.id['Apple'] - assert self.viewer.state.y_att_world is self.image2.id['Banana'] - - self.viewer.state.y_att_world = self.image2.id['Apple'] - - assert self.viewer.state.x_att_world is self.image2.id['Banana'] - assert self.viewer.state.y_att_world is self.image2.id['Apple'] - - self.viewer.state.x_att_world = self.image2.id['Apple'] - - assert self.viewer.state.x_att_world is self.image2.id['Apple'] - assert self.viewer.state.y_att_world is self.image2.id['Banana'] - - def test_duplicate_subsets(self): - - # Regression test: make sure that when adding a seconda layer for the - # same dataset, we don't add the subsets all over again. - - self.viewer.add_data(self.image1) - self.data_collection.new_subset_group(subset_state=self.image1.id['x'] > 1, label='A') - - assert len(self.viewer.layers) == 2 - - self.viewer.add_data(self.image1) - - assert len(self.viewer.layers) == 3 - - def test_aspect_subset(self): - - self.viewer.add_data(self.image1) - - assert self.viewer.state.aspect == 'equal' - - self.viewer.state.aspect = 'auto' - - self.data_collection.new_subset_group('s1', self.image1.id['x'] > 0.) - - assert len(self.viewer.state.layers) == 2 - - assert self.viewer.state.aspect == 'auto' - - self.viewer.state.aspect = 'equal' - - self.data_collection.new_subset_group('s2', self.image1.id['x'] > 1.) - - assert len(self.viewer.state.layers) == 3 - - assert self.viewer.state.aspect == 'equal' - - def test_hypercube(self): - - # Check defaults when we add data - - self.viewer.add_data(self.hypercube) - - assert combo_as_string(self.options_widget.ui.combosel_x_att_world) == 'Coordinate components:Pixel Axis 0:Pixel Axis 1:Pixel Axis 2:Pixel Axis 3' - assert combo_as_string(self.options_widget.ui.combosel_x_att_world) == 'Coordinate components:Pixel Axis 0:Pixel Axis 1:Pixel Axis 2:Pixel Axis 3' - - assert self.viewer.axes.get_xlabel() == 'Pixel Axis 3' - assert self.viewer.state.x_att_world is self.hypercube.id['Pixel Axis 3'] - assert self.viewer.state.x_att is self.hypercube.pixel_component_ids[3] - assert_allclose(self.viewer.state.x_min, -0.6839826839826846) - assert_allclose(self.viewer.state.x_max, +4.6839826839826846) - - assert self.viewer.axes.get_ylabel() == 'Pixel Axis 2' - assert self.viewer.state.y_att_world is self.hypercube.id['Pixel Axis 2'] - assert self.viewer.state.y_att is self.hypercube.pixel_component_ids[2] - assert self.viewer.state.y_min == -0.5 - assert self.viewer.state.y_max == +3.5 - - assert not self.viewer.state.x_log - assert not self.viewer.state.y_log - - assert len(self.viewer.state.layers) == 1 - - def test_hypercube_world(self): - - # Check defaults when we add data - - wcs = WCS(naxis=4) - hypercube2 = Data() - hypercube2.coords = wcs - hypercube2.add_component(np.random.random((2, 3, 4, 5)), 'a') - - self.data_collection.append(hypercube2) - - self.viewer.add_data(hypercube2) - - def test_incompatible_subset(self): - self.viewer.add_data(self.image1) - self.data_collection.new_subset_group(subset_state=self.catalog.id['c'] > 1, label='A') - - def test_invisible_subset(self): - - # Regression test for a bug that caused a subset layer that started - # off as invisible to have issues when made visible. We emulate the - # initial invisible (but enabled) state by invalidating the cache. - - self.viewer.add_data(self.image1) - self.data_collection.new_subset_group(subset_state=self.image1.id['x'] > 1, label='A') - self.viewer.layers[1].visible = False - self.viewer.layers[1].image_artist.invalidate_cache() - self.viewer.layers[1].redraw() - process_events() - assert not np.any(self.viewer.layers[1].image_artist._A.mask) - self.viewer.layers[1].visible = True - assert not np.any(self.viewer.layers[1].image_artist._A.mask) - - def test_apply_roi_single(self): - - # Regression test for a bug that caused mode.update to be called - # multiple times and resulted in all other viewers receiving many - # messages regarding subset updates (this occurred when multiple) - # datasets were present. - - layer_tree = LayerTreeWidget(session=self.session) - layer_tree.set_checkable(False) - layer_tree.setup(self.data_collection) - layer_tree.bind_selection_to_edit_subset() - - class Client(HubListener): - - def __init__(self, *args, **kwargs): - super(Client, self).__init__(*args, **kwargs) - self.count = Counter() - - def ping(self, message): - self.count[message.sender] += 1 - - def register_to_hub(self, hub): - hub.subscribe(self, SubsetUpdateMessage, handler=self.ping) - - d1 = Data(a=[[1, 2], [3, 4]], label='d1') - d2 = Data(b=[[1, 2], [3, 4]], label='d2') - d3 = Data(c=[[1, 2], [3, 4]], label='d3') - d4 = Data(d=[[1, 2], [3, 4]], label='d4') - - self.data_collection.append(d1) - self.data_collection.append(d2) - self.data_collection.append(d3) - self.data_collection.append(d4) - - client = Client() - client.register_to_hub(self.hub) - - self.viewer.add_data(d1) - self.viewer.add_data(d3) - - roi = XRangeROI(2.5, 3.5) - self.viewer.apply_roi(roi) - - for subset in client.count: - assert client.count[subset] == 1 - - def test_disable_incompatible(self): - - # Test to make sure that image and image subset layers are disabled if - # their pixel coordinates are not compatible with the ones of the - # reference data. - - self.viewer.add_data(self.image1) - self.viewer.add_data(self.image2) - - assert self.viewer.state.reference_data is self.image1 - - self.data_collection.new_subset_group() - - process_events() - - assert len(self.viewer.layers) == 4 - - # Only the two layers associated with the reference data should be enabled - for layer_artist in self.viewer.layers: - if layer_artist.layer in (self.image1, self.image1.subsets[0]): - assert layer_artist.enabled - else: - assert not layer_artist.enabled - - py1, px1 = self.image1.pixel_component_ids - py2, px2 = self.image2.pixel_component_ids - - link1 = LinkSame(px1, px2) - self.data_collection.add_link(link1) - - process_events() - - # One link isn't enough, second dataset layers are still not enabled - - for layer_artist in self.viewer.layers: - if layer_artist.layer in (self.image1, self.image1.subsets[0]): - assert layer_artist.enabled - else: - assert not layer_artist.enabled - - link2 = LinkSame(py1, py2) - self.data_collection.add_link(link2) - - process_events() - - # All layers should now be enabled - - for layer_artist in self.viewer.layers: - assert layer_artist.enabled - - self.data_collection.remove_link(link2) - - process_events() - - # We should now be back to the original situation - - for layer_artist in self.viewer.layers: - if layer_artist.layer in (self.image1, self.image1.subsets[0]): - assert layer_artist.enabled - else: - assert not layer_artist.enabled - - def test_change_reference_data(self, capsys): - - # Test to make sure everything works fine if we change the reference data. - - self.viewer.add_data(self.image1) - self.viewer.add_data(self.image2) - - assert self.viewer.state.reference_data is self.image1 - assert self.viewer.state.x_att_world is self.image1.pixel_component_ids[-1] - assert self.viewer.state.y_att_world is self.image1.pixel_component_ids[-2] - assert self.viewer.state.x_att is self.image1.pixel_component_ids[-1] - assert self.viewer.state.y_att is self.image1.pixel_component_ids[-2] - - self.viewer.state.reference_data = self.image2 - - assert self.viewer.state.reference_data is self.image2 - assert self.viewer.state.x_att_world is self.image2.world_component_ids[-1] - assert self.viewer.state.y_att_world is self.image2.world_component_ids[-2] - assert self.viewer.state.x_att is self.image2.pixel_component_ids[-1] - assert self.viewer.state.y_att is self.image2.pixel_component_ids[-2] - - self.viewer.state.reference_data = self.image1 - - assert self.viewer.state.reference_data is self.image1 - assert self.viewer.state.x_att_world is self.image1.pixel_component_ids[-1] - assert self.viewer.state.y_att_world is self.image1.pixel_component_ids[-2] - assert self.viewer.state.x_att is self.image1.pixel_component_ids[-1] - assert self.viewer.state.y_att is self.image1.pixel_component_ids[-2] - - # Some exceptions used to happen during callbacks, and these show up - # in stderr but don't interrupt the code, so we make sure here that - # nothing was printed to stdout nor stderr. - - out, err = capsys.readouterr() - - assert out.strip() == "" - assert err.strip() == "" - - @pytest.mark.parametrize('wcs', [False, True]) - def test_change_reference_data_dimensionality(self, capsys, wcs): - - # Regression test for a bug that caused an exception when changing - # the dimensionality of the reference data - - if wcs: - first = self.image1_wcs - second = self.hypercube_wcs - else: - first = self.image1 - second = self.hypercube - - self.viewer.add_data(first) - self.viewer.add_data(second) - - assert self.viewer.state.reference_data is first - if wcs: - assert self.viewer.state.x_att_world is first.world_component_ids[-1] - assert self.viewer.state.y_att_world is first.world_component_ids[-2] - else: - assert self.viewer.state.x_att_world is first.pixel_component_ids[-1] - assert self.viewer.state.y_att_world is first.pixel_component_ids[-2] - assert self.viewer.state.x_att is first.pixel_component_ids[-1] - assert self.viewer.state.y_att is first.pixel_component_ids[-2] - - self.viewer.state.reference_data = second - - assert self.viewer.state.reference_data is second - if wcs: - assert self.viewer.state.x_att_world is second.world_component_ids[-1] - assert self.viewer.state.y_att_world is second.world_component_ids[-2] - else: - assert self.viewer.state.x_att_world is second.pixel_component_ids[-1] - assert self.viewer.state.y_att_world is second.pixel_component_ids[-2] - assert self.viewer.state.x_att is second.pixel_component_ids[-1] - assert self.viewer.state.y_att is second.pixel_component_ids[-2] - - self.viewer.state.reference_data = first - - assert self.viewer.state.reference_data is first - if wcs: - assert self.viewer.state.x_att_world is first.world_component_ids[-1] - assert self.viewer.state.y_att_world is first.world_component_ids[-2] - else: - assert self.viewer.state.x_att_world is first.pixel_component_ids[-1] - assert self.viewer.state.y_att_world is first.pixel_component_ids[-2] - assert self.viewer.state.x_att is first.pixel_component_ids[-1] - assert self.viewer.state.y_att is first.pixel_component_ids[-2] - - # Some exceptions used to happen during callbacks, and these show up - # in stderr but don't interrupt the code, so we make sure here that - # nothing was printed to stdout nor stderr. - - out, err = capsys.readouterr() - - assert out.strip() == "" - assert err.strip() == "" - - def test_scatter_overlay(self): - self.viewer.add_data(self.image1) - self.viewer.add_data(self.catalog) - - def test_removed_subset(self): - - # Regression test for a bug in v0.11.0 that meant that if a subset - # was removed, the image viewer would then crash when changing view - # (e.g. zooming in). The bug was caused by undeleted references to - # FRBArtist due to circular references. We therefore check in this - # test how many FRBArtist objects exist. - - def get_frb_artists(): - mi = [] - gc.collect() - for obj in gc.get_objects(): - try: - if isinstance(obj, FRBArtist): - mi.append(obj) - except ReferenceError: - pass - return mi - - # The viewer starts off with one FRBArtist. This is also a good test - # that other FRBArtist in other tests have been removed. - assert len(get_frb_artists()) == 1 - - large_image = Data(x=np.random.random((2048, 2048))) - self.data_collection.append(large_image) - - # The subset group can be made from any dataset - subset_group = self.data_collection.new_subset_group(subset_state=self.image1.id['x'] > 1, label='A') - - self.viewer.add_data(large_image) - - # Since the dataset added has a subset, and each subset has its own - # FRBArtist, this increases the count. - assert len(get_frb_artists()) == 2 - - assert len(self.viewer.layers) == 2 - - self.data_collection.remove_subset_group(subset_group) - - # Removing the subset should bring the count back to 1 again - assert len(get_frb_artists()) == 1 - - def test_select_previously_incompatible_layer(self): - - # Regression test for a bug that caused a selection in a previously disabled - # layer to enable the layer without updating the subset view - - self.viewer.add_data(self.image1) - self.viewer.add_data(self.catalog) - self.catalog.add_component([4, 5, 6], 'e') - - link1 = LinkSame(self.catalog.id['c'], self.image1.pixel_component_ids[0]) - link2 = LinkSame(self.catalog.id['d'], self.image1.pixel_component_ids[1]) - self.data_collection.add_link(link1) - self.data_collection.add_link(link2) - - self.data_collection.new_subset_group(subset_state=self.catalog.id['e'] > 4) - - process_events() - - assert self.viewer.layers[0].enabled # image - assert self.viewer.layers[1].enabled # scatter - assert not self.viewer.layers[2].enabled # image subset - assert self.viewer.layers[3].enabled # scatter subset - - assert not self.viewer.layers[2].image_artist.get_visible() - - self.data_collection.subset_groups[0].subset_state = self.catalog.id['c'] > -1 - - process_events() - - assert self.viewer.layers[0].enabled # image - assert self.viewer.layers[1].enabled # scatter - assert self.viewer.layers[2].enabled # image subset - assert self.viewer.layers[3].enabled # scatter subset - - assert self.viewer.layers[2].image_artist.get_visible() - - def test_linking_and_enabling(self): - - # Regression test for a bug that caused layers not not be correctly - # enabled/disabled. - - self.viewer.add_data(self.image1) - self.viewer.add_data(self.catalog) - self.catalog.add_component([4, 5, 6], 'e') - - self.data_collection.new_subset_group(subset_state=self.catalog.id['e'] > 4) - - process_events() - - assert self.viewer.layers[0].enabled # image - assert not self.viewer.layers[1].enabled # scatter - assert not self.viewer.layers[2].enabled # image subset - assert not self.viewer.layers[3].enabled # scatter subset - - link1 = LinkSame(self.catalog.id['c'], self.image1.pixel_component_ids[0]) - link2 = LinkSame(self.catalog.id['d'], self.image1.pixel_component_ids[1]) - self.data_collection.add_link(link1) - self.data_collection.add_link(link2) - - process_events() - - assert self.viewer.layers[0].enabled # image - assert self.viewer.layers[1].enabled # scatter - assert not self.viewer.layers[2].enabled # image subset - assert self.viewer.layers[3].enabled # scatter subset - - def test_save_aggregate_slice(self, tmpdir): - - # Regression test to make sure that image viewers that include - # aggregate slice objects in the slices can be saved/restored - - self.viewer.add_data(self.hypercube) - self.viewer.state.slices = AggregateSlice(slice(1, 3), 10, np.sum), 3, 0, 0 - - filename = tmpdir.join('session.glu').strpath - - self.application.save_session(filename) - self.application.close() - - app2 = GlueApplication.restore_session(filename) - viewer_state = app2.viewers[0][0].state - slices = viewer_state.slices - assert isinstance(slices[0], AggregateSlice) - assert slices[0].slice == slice(1, 3) - assert slices[0].center == 10 - assert slices[0].function is np.sum - assert slices[1:] == (3, 0, 0) - - app2.close() - - def test_subset_cube_image(self): - - # Regression test to make sure that if an image and cube are present - # in an image viewer and a subset is also present, we don't get an - # error when trying to access the subset shape - - self.viewer.add_data(self.image1) - self.data_collection.new_subset_group(label='subset', - subset_state=self.image1.id['x'] > 1.5) - - self.viewer.add_data(self.hypercube) - self.viewer.state.reference_data = self.hypercube - - assert self.viewer.layers[1].subset_array.shape == (4, 5) - assert self.viewer.layers[3].subset_array.shape == (4, 5) - - def test_preserve_slice(self): - - # Regression test to make sure that when adding a second dataset to - # an image viewer, the current slice in a cube does not change. - - self.viewer.add_data(self.hypercube) - self.viewer.state.slices = (1, 2, 3, 4) - self.viewer.add_data(self.image1) - assert self.viewer.state.slices == (1, 2, 3, 4) - - def test_close(self): - - # Regression test for a bug that caused an error related to the toolbar - # and _mpl_nav not being present when closing the viewer. - - self.viewer.toolbar.active_tool = self.viewer.toolbar.tools['mpl:zoom'] - self.viewer.close(warn=False) - - def test_legend(self): - from matplotlib.colors import to_hex - - viewer_state = self.viewer.state - self.viewer.add_data(self.image1) - self.viewer.state.legend.visible = True - - handles, labels, handler_dict = self.viewer.get_handles_legend() - assert len(handles) == 1 - assert labels[0] == 'image1' - - self.data_collection.new_subset_group('test', self.image1.id['x'] > 1) - assert len(viewer_state.layers) == 2 - handles, labels, handler_dict = self.viewer.get_handles_legend() - assert len(handles) == 2 - assert labels[1] == 'test' - - assert to_hex(handles[1].get_facecolor()) == viewer_state.layers[1].color - - def test_limit_resetting(self): - - # Test to make sure that the limits only change if the reference data - # is changed - - self.viewer.state.aspect = 'auto' - - self.viewer.add_data(self.image1) - - self.viewer.state.x_min = 0.2 - self.viewer.state.x_max = 0.4 - self.viewer.state.y_min = 0.3 - self.viewer.state.y_max = 0.5 - - self.viewer.add_data(self.image2) - - assert self.viewer.state.x_min == 0.2 - assert self.viewer.state.x_max == 0.4 - assert self.viewer.state.y_min == 0.3 - assert self.viewer.state.y_max == 0.5 - - self.viewer.state.reference_data = self.image2 - - assert self.viewer.state.x_min == -0.5 - assert self.viewer.state.x_max == 1.5 - assert self.viewer.state.y_min == -0.5 - assert self.viewer.state.y_max == 1.5 - - -class TestSessions(object): - - @pytest.mark.parametrize('protocol', [0, 1]) - def test_session_back_compat(self, protocol): - - filename = os.path.join(DATA, 'image_v{0}.glu'.format(protocol)) - - with open(filename, 'r') as f: - session = f.read() - - state = GlueUnSerializer.loads(session) - - ga = state.object('__main__') - - dc = ga.session.data_collection - - assert len(dc) == 2 - - assert dc[0].label == 'data1' - assert dc[1].label == 'data2' - - viewer1 = ga.viewers[0][0] - - assert len(viewer1.state.layers) == 3 - - assert viewer1.state.x_att_world is dc[0].id['World 1'] - assert viewer1.state.y_att_world is dc[0].id['World 0'] - - assert viewer1.state.x_min < -0.5 - assert viewer1.state.x_max > 1.5 - assert viewer1.state.y_min <= -0.5 - assert viewer1.state.y_max >= 1.5 - - layer_state = viewer1.state.layers[0] - assert isinstance(layer_state, ImageLayerState) - assert layer_state.visible - assert layer_state.bias == 0.5 - assert layer_state.contrast == 1.0 - assert layer_state.stretch == 'sqrt' - assert layer_state.percentile == 99 - - layer_state = viewer1.state.layers[1] - assert isinstance(layer_state, ScatterLayerState) - assert layer_state.visible - - layer_state = viewer1.state.layers[2] - assert isinstance(layer_state, ImageSubsetLayerState) - assert not layer_state.visible - - viewer2 = ga.viewers[0][1] - - assert len(viewer2.state.layers) == 2 - - assert viewer2.state.x_att_world is dc[0].id['World 1'] - assert viewer2.state.y_att_world is dc[0].id['World 0'] - - assert viewer2.state.x_min < -0.5 - assert viewer2.state.x_max > 1.5 - assert viewer2.state.y_min <= -0.5 - assert viewer2.state.y_max >= 1.5 - - layer_state = viewer2.state.layers[0] - assert layer_state.visible - assert layer_state.stretch == 'arcsinh' - assert layer_state.v_min == 1 - assert layer_state.v_max == 4 - - layer_state = viewer2.state.layers[1] - assert layer_state.visible - - viewer3 = ga.viewers[0][2] - - assert len(viewer3.state.layers) == 2 - - assert viewer3.state.x_att_world is dc[0].id['World 1'] - assert viewer3.state.y_att_world is dc[0].id['World 0'] - - assert viewer3.state.x_min < -0.5 - assert viewer3.state.x_max > 1.5 - assert viewer3.state.y_min <= -0.5 - assert viewer3.state.y_max >= 1.5 - - layer_state = viewer3.state.layers[0] - assert layer_state.visible - assert layer_state.stretch == 'linear' - assert layer_state.v_min == -2 - assert layer_state.v_max == 2 - - layer_state = viewer3.state.layers[1] - assert layer_state.visible - - ga.close() - - @pytest.mark.parametrize('protocol', [0, 1]) - def test_session_cube_back_compat(self, protocol): - - filename = os.path.join(DATA, 'image_cube_v{0}.glu'.format(protocol)) - - with open(filename, 'r') as f: - session = f.read() - - state = GlueUnSerializer.loads(session) - - ga = state.object('__main__') - - dc = ga.session.data_collection - - assert len(dc) == 1 - - assert dc[0].label == 'array' - - viewer1 = ga.viewers[0][0] - - assert len(viewer1.state.layers) == 1 - - assert viewer1.state.x_att_world is dc[0].id['World 2'] - assert viewer1.state.y_att_world is dc[0].id['World 1'] - assert viewer1.state.slices == [2, 0, 0, 1] - - ga.close() - - @pytest.mark.parametrize('protocol', [0, 1]) - def test_session_rgb_back_compat(self, protocol): - - filename = os.path.join(DATA, 'image_rgb_v{0}.glu'.format(protocol)) - - with open(filename, 'r') as f: - session = f.read() - - state = GlueUnSerializer.loads(session) - - ga = state.object('__main__') - - dc = ga.session.data_collection - - assert len(dc) == 1 - - assert dc[0].label == 'rgbcube' - - viewer1 = ga.viewers[0][0] - - assert len(viewer1.state.layers) == 3 - assert viewer1.state.color_mode == 'One color per layer' - - layer_state = viewer1.state.layers[0] - assert layer_state.visible - assert layer_state.attribute.label == 'a' - assert layer_state.color == 'r' - - layer_state = viewer1.state.layers[1] - assert not layer_state.visible - assert layer_state.attribute.label == 'c' - assert layer_state.color == 'g' - - layer_state = viewer1.state.layers[2] - assert layer_state.visible - assert layer_state.attribute.label == 'b' - assert layer_state.color == 'b' - - ga.close() - - -def test_indexed_data(capsys): - - # Make sure that the image viewer works properly with IndexedData objects - - data_4d = Data(label='hypercube_wcs', - x=np.random.random((3, 5, 4, 3)), - coords=WCS(naxis=4)) - - data_2d = IndexedData(data_4d, (2, None, 3, None)) - - application = GlueApplication() - - session = application.session - - hub = session.hub - - data_collection = session.data_collection - data_collection.append(data_4d) - data_collection.append(data_2d) - - viewer = application.new_data_viewer(ImageViewer) - viewer.add_data(data_2d) - - assert viewer.state.x_att is data_2d.pixel_component_ids[1] - assert viewer.state.y_att is data_2d.pixel_component_ids[0] - assert viewer.state.x_att_world is data_2d.world_component_ids[1] - assert viewer.state.y_att_world is data_2d.world_component_ids[0] - - process_events() - - application.close() - - # Some exceptions used to happen during callbacks, and these show up - # in stderr but don't interrupt the code, so we make sure here that - # nothing was printed to stdout nor stderr. - - out, err = capsys.readouterr() - - assert out.strip() == "" - assert err.strip() == "" diff --git a/glue/viewers/image/qt/tests/test_python_export.py b/glue/viewers/image/qt/tests/test_python_export.py deleted file mode 100644 index cfee1c89a..000000000 --- a/glue/viewers/image/qt/tests/test_python_export.py +++ /dev/null @@ -1,126 +0,0 @@ -import pytest -import numpy as np -import matplotlib.pyplot as plt -from astropy.utils import NumpyRNGContext -from astropy.wcs import WCS - -from glue.core import Data, DataCollection -from glue.core.coordinates import AffineCoordinates -from glue.app.qt.application import GlueApplication -from glue.viewers.image.qt import ImageViewer -from glue.viewers.matplotlib.qt.tests.test_python_export import BaseTestExportPython - - -class TestExportPython(BaseTestExportPython): - - def setup_method(self, method): - - with NumpyRNGContext(12345): - self.data = Data(cube=np.random.random((30, 50, 20))) - # Create data versions with WCS and affine coordinates - matrix = np.array([[2, 3, 4, -1], [1, 2, 2, 2], [1, 1, 1, -2], [0, 0, 0, 1]]) - affine = AffineCoordinates(matrix, units=['Mm', 'Mm', 'km'], labels=['xw', 'yw', 'zw']) - - self.data_wcs = Data(label='cube', cube=self.data['cube'], coords=WCS(naxis=3)) - self.data_affine = Data(label='cube', cube=self.data['cube'], coords=affine) - self.data_collection = DataCollection([self.data, self.data_wcs, self.data_affine]) - self.app = GlueApplication(self.data_collection) - self.viewer = self.app.new_data_viewer(ImageViewer) - self.viewer.add_data(self.data) - # FIXME: On some platforms, using an integer label size - # causes some of the labels to be non-deterministically - # shifted by one pixel, so we pick a non-round font size - # to avoid this. - self.viewer.state.x_ticklabel_size = 8.21334111 - self.viewer.state.y_ticklabel_size = 8.21334111 - - def teardown_method(self, method): - self.viewer.close() - self.viewer = None - self.app.close() - self.app = None - - def assert_same(self, tmpdir, tol=0.1): - BaseTestExportPython.assert_same(self, tmpdir, tol=tol) - - def viewer_load(self, coords): - if coords is not None: - self.viewer.add_data(getattr(self, f'data_{coords}')) - self.viewer.remove_data(self.data) - - @pytest.mark.parametrize('coords', [None, 'wcs', 'affine']) - def test_simple(self, tmpdir, coords): - self.viewer_load(coords) - self.assert_same(tmpdir) - - @pytest.mark.parametrize('coords', [None, 'wcs', 'affine']) - def test_simple_legend(self, tmpdir, coords): - self.viewer_load(coords) - self.viewer.state.show_legend = True - self.assert_same(tmpdir) - - @pytest.mark.parametrize('coords', [None, 'wcs', 'affine']) - def test_simple_att(self, tmpdir, coords): - self.viewer_load(coords) - self.viewer.state.x_att = self.viewer.state.reference_data.pixel_component_ids[1] - self.viewer.state.y_att = self.viewer.state.reference_data.pixel_component_ids[0] - if coords == 'affine': - pytest.xfail('Known issue with axis label rendering') - self.assert_same(tmpdir) - - @pytest.mark.parametrize('coords', [None, 'wcs', 'affine']) - def test_simple_visual(self, tmpdir, coords): - self.viewer_load(coords) - self.viewer.state.legend.visible = True - self.viewer.state.layers[0].cmap = plt.cm.RdBu - self.viewer.state.layers[0].v_min = 0.2 - self.viewer.state.layers[0].v_max = 0.8 - self.viewer.state.layers[0].stretch = 'sqrt' - self.viewer.state.layers[0].stretch = 'sqrt' - self.viewer.state.layers[0].contrast = 0.9 - self.viewer.state.layers[0].bias = 0.6 - self.assert_same(tmpdir) - - @pytest.mark.parametrize('coords', [None, 'wcs', 'affine']) - def test_slice(self, tmpdir, coords): - self.viewer_load(coords) - self.viewer.state.x_att = self.viewer.state.reference_data.pixel_component_ids[1] - self.viewer.state.y_att = self.viewer.state.reference_data.pixel_component_ids[0] - self.viewer.state.slices = (2, 3, 4) - if coords == 'affine': - pytest.xfail('Known issue with axis label rendering') - self.assert_same(tmpdir) - - @pytest.mark.parametrize('coords', [None, 'wcs', 'affine']) - def test_aspect(self, tmpdir, coords): - self.viewer_load(coords) - self.viewer.state.aspect = 'auto' - self.assert_same(tmpdir) - - @pytest.mark.parametrize('coords', [None, 'wcs', 'affine']) - def test_subset(self, tmpdir, coords): - self.viewer_load(coords) - self.data_collection.new_subset_group('mysubset', self.data.id['cube'] > 0.5) - self.assert_same(tmpdir) - - @pytest.mark.parametrize('coords', [None, 'wcs', 'affine']) - def test_subset_legend(self, tmpdir, coords): - self.viewer_load(coords) - self.data_collection.new_subset_group('mysubset', - self.viewer.state.reference_data.id['cube'] > 0.5) - self.viewer.state.legend.visible = True - self.assert_same(tmpdir, tol=0.15) # transparency and such - - @pytest.mark.parametrize('coords', [None, 'wcs', 'affine']) - def test_subset_slice(self, tmpdir, coords): - self.viewer_load(coords) - self.data_collection.new_subset_group('mysubset', self.data.id['cube'] > 0.5) - self.test_slice(tmpdir, coords) - - @pytest.mark.parametrize('coords', [None, 'wcs', 'affine']) - def test_subset_transposed(self, tmpdir, coords): - self.viewer_load(coords) - self.data_collection.new_subset_group('mysubset', self.data.id['cube'] > 0.5) - self.viewer.state.x_att = self.data.pixel_component_ids[0] - self.viewer.state.y_att = self.data.pixel_component_ids[1] - self.assert_same(tmpdir) diff --git a/glue/viewers/image/qt/tests/test_regression.py b/glue/viewers/image/qt/tests/test_regression.py deleted file mode 100644 index d4b07587a..000000000 --- a/glue/viewers/image/qt/tests/test_regression.py +++ /dev/null @@ -1,47 +0,0 @@ -# Miscellaneous regression tests for the image viewer - -import pytest -import numpy as np - -from glue.core import Data -from glue.viewers.image.qt import ImageViewer -from glue.core.tests.util import simple_session -from glue.tests.helpers import PYSIDE2_INSTALLED # noqa - - -@pytest.mark.skipif('PYSIDE2_INSTALLED') -@pytest.mark.mpl_image_compare(tolerance=1, savefig_kwargs={'dpi': 50}) -def test_resample_on_zoom(): - - # For images where the aspect ratio of pixels is fixed to be square, when - # the user zooms in, the limits of the axes are actually changed twice by - # matplotlib - a second time when the aspect ratio is enforced. So we need - # to make sure that we update the FRB artist when this is the case. - - session = simple_session() - - np.random.seed(12345) - - data = Data(x=np.random.random((2048, 2048)), label='image') - session.data_collection.append(data) - - image = ImageViewer(session=session) - image.add_data(data) - - image.show() - - try: - device_ratio = image.axes.figure.canvas.devicePixelRatio() - except AttributeError: - device_ratio = 1. - - image.axes.figure.canvas.key_press_event('o') - image.axes.figure.canvas.button_press_event(200 * device_ratio, 200 * device_ratio, 1) - image.axes.figure.canvas.motion_notify_event(400 * device_ratio, 210 * device_ratio) - image.axes.figure.canvas.button_release_event(400 * device_ratio, 210 * device_ratio, 1) - - figure = image.axes.figure - - image.close() - - return figure diff --git a/glue/viewers/image/state.py b/glue/viewers/image/state.py index 0c6ae51b3..ecb1db944 100644 --- a/glue/viewers/image/state.py +++ b/glue/viewers/image/state.py @@ -2,7 +2,7 @@ from collections import defaultdict from glue.core import BaseData -from glue.config import colormaps +from glue.config import colormaps, stretches from glue.viewers.matplotlib.state import (MatplotlibDataViewerState, MatplotlibLayerState, DeferredDrawCallbackProperty as DDCProperty, @@ -525,13 +525,8 @@ def __init__(self, layer=None, viewer_state=None, **kwargs): ImageLayerState.percentile.set_choices(self, [100, 99.5, 99, 95, 90, 'Custom']) ImageLayerState.percentile.set_display_func(self, percentile_display.get) - stretch_display = {'linear': 'Linear', - 'sqrt': 'Square Root', - 'arcsinh': 'Arcsinh', - 'log': 'Logarithmic'} - - ImageLayerState.stretch.set_choices(self, ['linear', 'sqrt', 'arcsinh', 'log']) - ImageLayerState.stretch.set_display_func(self, stretch_display.get) + ImageLayerState.stretch.set_choices(self, list(stretches.members)) + ImageLayerState.stretch.set_display_func(self, stretches.display_func) self.add_callback('global_sync', self._update_syncing) self.add_callback('layer', self._update_attribute) diff --git a/glue/viewers/image/tests/test_composite_array.py b/glue/viewers/image/tests/test_composite_array.py index 40438ff9e..2ba6d53a2 100644 --- a/glue/viewers/image/tests/test_composite_array.py +++ b/glue/viewers/image/tests/test_composite_array.py @@ -66,15 +66,19 @@ def test_cmap_blending(self): expected_a = np.array([[cm.Blues(1.), cm.Blues(0.5)], [cm.Blues(0.), cm.Blues(0.)]]) - expected_b = np.array([[cm.Reds(0.), cm.Reds(1.)], - [cm.Reds(0.), cm.Reds(0.)]]) + bad_value = cm.Reds.with_extremes( + # set "bad" to the default color and alpha=1 + bad=cm.Reds.get_bad().tolist()[:3] + [1.] + )(np.nan) + expected_b = np.array( + [[bad_value, cm.Reds(1.)], + [cm.Reds(0.), cm.Reds(0.)]] + ) # If both layers have alpha=1, the top layer should be the only one visible - assert_allclose(self.composite(bounds=self.default_bounds), expected_b) # If the top layer has alpha=0, the bottom layer should be the only one visible - self.composite.set('b', alpha=0.) assert_allclose(self.composite(bounds=self.default_bounds), expected_a) @@ -96,7 +100,7 @@ def test_cmap_alphas(self): cmap=cm.Blues, clim=(0, 2)) self.composite.set('b', zorder=1, visible=True, array=self.array2, - cmap=lambda x: cm.Reds(x, alpha=abs(np.nan_to_num(x))), clim=(0, 1)) + cmap=lambda x: cm.Reds(x, alpha=abs(x)), clim=(0, 1)) # Determine expected result for each layer individually in the absence # of transparency @@ -104,14 +108,18 @@ def test_cmap_alphas(self): expected_a = np.array([[cm.Blues(1.), cm.Blues(0.5)], [cm.Blues(0.), cm.Blues(0.)]]) - expected_b = np.array([[cm.Reds(0.), cm.Reds(1.)], - [cm.Reds(0.), cm.Reds(0.)]]) + bad_value = cm.Reds.with_extremes( + # set "bad" to the default color and alpha=0 + bad=cm.Reds.get_bad().tolist()[:3] + [0.] + )(np.nan) + expected_b_1 = np.array([[bad_value, cm.Reds(1.)], + [cm.Reds(0.), cm.Reds(0.)]]) # If the top layer has alpha=1 with a colormap alpha fading proportional to absval, # it should be visible only at the nonzero value [0, 1] assert_allclose(self.composite(bounds=self.default_bounds), - [[expected_a[0, 0], expected_b[0, 1]], expected_a[1]]) + [[expected_a[0, 0], expected_b_1[0, 1]], expected_a[1]]) # For the same case with the top layer alpha=0.5 that value should become an equal # blend of both layers again @@ -119,7 +127,7 @@ def test_cmap_alphas(self): self.composite.set('b', alpha=0.5) assert_allclose(self.composite(bounds=self.default_bounds), - [[expected_a[0, 0], 0.5 * (expected_a[0, 1] + expected_b[0, 1])], + [[expected_a[0, 0], 0.5 * (expected_a[0, 1] + expected_b_1[0, 1])], expected_a[1]]) # A third layer added at the bottom should not be visible in the output @@ -129,21 +137,27 @@ def test_cmap_alphas(self): cmap=cm.Greens, clim=(0, 2)) assert_allclose(self.composite(bounds=self.default_bounds), - [[expected_a[0, 0], 0.5 * (expected_a[0, 1] + expected_b[0, 1])], + [[expected_a[0, 0], 0.5 * (expected_a[0, 1] + expected_b_1[0, 1])], expected_a[1]]) # For only the bottom layer having such colormap, the top layer should appear just the same - self.composite.set('a', alpha=1., cmap=lambda x: cm.Blues(x, alpha=abs(np.nan_to_num(x)))) + self.composite.set('a', alpha=1., cmap=lambda x: cm.Blues(x, alpha=abs(x))) self.composite.set('b', alpha=1., cmap=cm.Reds) - assert_allclose(self.composite(bounds=self.default_bounds), expected_b) + bad_value = cm.Reds.with_extremes( + # set "bad" to the default color and alpha=1 + bad=cm.Reds.get_bad().tolist()[:3] + [1.] + )(np.nan) + expected_b_2 = np.array([[bad_value, cm.Reds(1.)], + [cm.Reds(0.), cm.Reds(0.)]]) + assert_allclose(self.composite(bounds=self.default_bounds), expected_b_2) - # Settin the third layer on top with alpha=0 should not affect the appearance + # Setting the third layer on top with alpha=0 should not affect the appearance self.composite.set('c', zorder=2, alpha=0.) - assert_allclose(self.composite(bounds=self.default_bounds), expected_b) + assert_allclose(self.composite(bounds=self.default_bounds), expected_b_2) def test_color_blending(self): diff --git a/glue/viewers/image/viewer.py b/glue/viewers/image/viewer.py index 97288583e..02f23e243 100644 --- a/glue/viewers/image/viewer.py +++ b/glue/viewers/image/viewer.py @@ -127,7 +127,7 @@ def _set_wcs(self, before=None, after=None, relim=True): self.state.reset_limits() # Determine whether changing slices requires changing the WCS - if ref_coords is None or type(ref_coords) == Coordinates: + if ref_coords is None or type(ref_coords) is Coordinates: self._changing_slice_requires_wcs_update = False else: ix = self.state.x_att.axis diff --git a/glue/viewers/matplotlib/qt/__init__.py b/glue/viewers/matplotlib/qt/__init__.py index 090d5595a..2d725e620 100644 --- a/glue/viewers/matplotlib/qt/__init__.py +++ b/glue/viewers/matplotlib/qt/__init__.py @@ -1 +1,4 @@ -from . import toolbar_mode # noqa +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.viewers.matplotlib.qt is deprecated, use glue_qt.viewers.matplotlib instead', GlueDeprecationWarning) +from glue_qt.viewers.matplotlib import * # noqa diff --git a/glue/viewers/matplotlib/qt/axes_editor.py b/glue/viewers/matplotlib/qt/axes_editor.py index d81b3d18f..dbcf092e9 100644 --- a/glue/viewers/matplotlib/qt/axes_editor.py +++ b/glue/viewers/matplotlib/qt/axes_editor.py @@ -1,15 +1,4 @@ -import os - -from qtpy import QtWidgets - -from glue.utils.qt import load_ui - -__all__ = ['AxesEditorWidget'] - - -class AxesEditorWidget(QtWidgets.QWidget): - - def __init__(self, parent=None): - super(AxesEditorWidget, self).__init__(parent=parent) - self.ui = load_ui('axes_editor.ui', self, - directory=os.path.dirname(__file__)) +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.viewers.matplotlib.qt.axes_editor is deprecated, use glue_qt.viewers.matplotlib.axes_editor instead', GlueDeprecationWarning) +from glue_qt.viewers.matplotlib.axes_editor import * # noqa diff --git a/glue/viewers/matplotlib/qt/axes_editor.ui b/glue/viewers/matplotlib/qt/axes_editor.ui deleted file mode 100644 index eee77d0dd..000000000 --- a/glue/viewers/matplotlib/qt/axes_editor.ui +++ /dev/null @@ -1,213 +0,0 @@ - - - Form - - - - 0 - 0 - 272 - 286 - - - - Form - - - - 5 - - - 5 - - - 5 - - - 5 - - - 5 - - - - - y label: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - - 70 - 0 - - - - - - - - - - - - - - x label: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - tick label size - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - Qt::Horizontal - - - - - - - - 70 - 0 - - - - - - - - axis label size - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - axis label weight - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - 50 - false - - - - x - - - Qt::AlignCenter - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Apply to all plots - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - - Qt::Vertical - - - - 20 - 0 - - - - - - - - - 50 - false - - - - y - - - Qt::AlignCenter - - - - - - - - - - - diff --git a/glue/viewers/matplotlib/qt/compute_worker.py b/glue/viewers/matplotlib/qt/compute_worker.py index f2528be88..34e2f794b 100644 --- a/glue/viewers/matplotlib/qt/compute_worker.py +++ b/glue/viewers/matplotlib/qt/compute_worker.py @@ -1,62 +1,4 @@ -import sys -import time -import queue - -from glue.utils import queue_to_list -from qtpy.QtCore import Signal, QThread - - -# For some viewers, we make use of a thread that continuously listens for -# requests to update the profile and we run these as needed. In future, -# we should add the ability to interrupt compute jobs if a newer compute -# job is requested. - - -class ComputeWorker(QThread): - - compute_start = Signal() - compute_end = Signal() - compute_error = Signal(object) - - def __init__(self, function): - super(ComputeWorker, self).__init__() - self.function = function - self.running = False - self.work_queue = queue.Queue() - - def run(self): - - error = None - - while True: - - time.sleep(1 / 25) - - msgs = queue_to_list(self.work_queue) - - if 'stop' in msgs: - return - - elif len(msgs) == 0: - # We change this here rather than in the try...except below - # to avoid stopping and starting in quick succession. - if self.running: - self.running = False - if error is None: - self.compute_end.emit() - else: - self.compute_error.emit(error) - error = None - continue - - # If any resets were requested, honor this - reset = any(msgs) - - try: - self.running = True - self.compute_start.emit() - self.function(reset=reset) - except Exception: - error = sys.exc_info() - else: - error = None +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.viewers.matplotlib.qt.compute_worker is deprecated, use glue_qt.viewers.matplotlib.compute_worker instead', GlueDeprecationWarning) +from glue_qt.viewers.matplotlib.compute_worker import * # noqa diff --git a/glue/viewers/matplotlib/qt/data_viewer.py b/glue/viewers/matplotlib/qt/data_viewer.py index 742934034..aa334f43c 100644 --- a/glue/viewers/matplotlib/qt/data_viewer.py +++ b/glue/viewers/matplotlib/qt/data_viewer.py @@ -1,76 +1,4 @@ -from qtpy.QtCore import QTimer - -from glue.core.message import ComputationStartedMessage -from glue.viewers.common.qt.data_viewer import DataViewer -from glue.viewers.matplotlib.qt.widget import MplWidget -from glue.viewers.matplotlib.mpl_axes import init_mpl -from glue.utils import defer_draw, decorate_all_methods -from glue.viewers.matplotlib.state import MatplotlibDataViewerState -from glue.viewers.matplotlib.viewer import MatplotlibViewerMixin - -# The following import is required to register the viewer tools -from glue.viewers.matplotlib.qt import toolbar # noqa - -__all__ = ['MatplotlibDataViewer'] - - -@decorate_all_methods(defer_draw) -class MatplotlibDataViewer(MatplotlibViewerMixin, DataViewer): - - _state_cls = MatplotlibDataViewerState - - tools = ['mpl:home', 'mpl:pan', 'mpl:zoom'] - subtools = {'save': ['mpl:save']} - - def __init__(self, session, parent=None, wcs=None, state=None, projection=None): - - super(MatplotlibDataViewer, self).__init__(session, parent=parent, state=state) - - # Use MplWidget to set up a Matplotlib canvas inside the Qt window - self.mpl_widget = MplWidget() - self.setCentralWidget(self.mpl_widget) - - # TODO: shouldn't have to do this - self.central_widget = self.mpl_widget - - self.figure, self.axes = init_mpl(self.mpl_widget.canvas.fig, wcs=wcs, projection=projection) - - MatplotlibViewerMixin.setup_callbacks(self) - - self.central_widget.resize(600, 400) - self.resize(self.central_widget.size()) - - self._monitor_computation = QTimer() - self._monitor_computation.setInterval(500) - self._monitor_computation.timeout.connect(self._update_computation) - - def _update_computation(self, message=None): - - # If we get a ComputationStartedMessage and the timer isn't currently - # active, then we start the timer but we then return straight away. - # This is to avoid showing the 'Computing' message straight away in the - # case of reasonably fast operations. - if isinstance(message, ComputationStartedMessage): - if not self._monitor_computation.isActive(): - self._monitor_computation.start() - return - - for layer_artist in self.layers: - if layer_artist.is_computing: - self.loading_rectangle.set_visible(True) - text = self.loading_text.get_text() - if text.count('.') > 2: - text = 'Computing' - else: - text += '.' - self.loading_text.set_text(text) - self.loading_text.set_visible(True) - self.redraw() - return - - self.loading_rectangle.set_visible(False) - self.loading_text.set_visible(False) - self.redraw() - - # If we get here, the computation has stopped so we can stop the timer - self._monitor_computation.stop() +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.viewers.matplotlib.qt.data_viewer is deprecated, use glue_qt.viewers.matplotlib.data_viewer instead', GlueDeprecationWarning) +from glue_qt.viewers.matplotlib.data_viewer import * # noqa diff --git a/glue/viewers/matplotlib/qt/legend_editor.py b/glue/viewers/matplotlib/qt/legend_editor.py old mode 100755 new mode 100644 index c4974f04a..762f23046 --- a/glue/viewers/matplotlib/qt/legend_editor.py +++ b/glue/viewers/matplotlib/qt/legend_editor.py @@ -1,15 +1,4 @@ -import os - -from qtpy import QtWidgets - -from glue.utils.qt import load_ui - -__all__ = ['LegendEditorWidget'] - - -class LegendEditorWidget(QtWidgets.QWidget): - - def __init__(self, parent=None): - super(LegendEditorWidget, self).__init__(parent=parent) - self.ui = load_ui('legend_editor.ui', self, - directory=os.path.dirname(__file__)) +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.viewers.matplotlib.qt.legend_editor is deprecated, use glue_qt.viewers.matplotlib.legend_editor instead', GlueDeprecationWarning) +from glue_qt.viewers.matplotlib.legend_editor import * # noqa diff --git a/glue/viewers/matplotlib/qt/legend_editor.ui b/glue/viewers/matplotlib/qt/legend_editor.ui deleted file mode 100644 index e991e2865..000000000 --- a/glue/viewers/matplotlib/qt/legend_editor.ui +++ /dev/null @@ -1,179 +0,0 @@ - - - Form - - - - 0 - 0 - 272 - 286 - - - - Form - - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - Qt::AlignRight|Qt::AlignTop|Qt::AlignTrailing - - - 5 - - - 5 - - - 5 - - - 5 - - - - - - 0 - 0 - - - - enable - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - - - - - location - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - opacity - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Qt::Horizontal - - - - - - - - 0 - 0 - - - - title - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - label size - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - box color - - - - - - - - 0 - 0 - - - - - - - - - - - - - - - - - - text color - - - - - - - - - - - - - - box edge - - - - - - - - QColorBox - QLabel -
glue.utils.qt.colors
-
-
- - -
diff --git a/glue/viewers/matplotlib/qt/tests/__init__.py b/glue/viewers/matplotlib/qt/tests/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/glue/viewers/matplotlib/qt/tests/test_data_viewer.py b/glue/viewers/matplotlib/qt/tests/test_data_viewer.py deleted file mode 100644 index 95fc9beee..000000000 --- a/glue/viewers/matplotlib/qt/tests/test_data_viewer.py +++ /dev/null @@ -1,681 +0,0 @@ -# pylint: disable=I0011,W0613,W0201,W0212,E1101,E1103 - -import sys - -import pytest -from numpy.testing import assert_allclose - -try: - import objgraph -except ImportError: - OBJGRAPH_INSTALLED = False -else: - OBJGRAPH_INSTALLED = True - -from glue.core import Data -from glue.core.exceptions import IncompatibleDataException -from glue.app.qt.application import GlueApplication -from glue.core.roi import XRangeROI -from glue.utils.qt import process_events -from glue.tests.helpers import requires_matplotlib_ge_22 - - -class MatplotlibDrawCounter(object): - - def __init__(self, figure): - self.figure = figure - # For recent versions of Matplotlib it seems that we need - # to process events at least twice to really flush out any - # unprocessed events - process_events() - process_events() - self.start = self.figure.canvas._draw_count - - @property - def draw_count(self): - process_events() - process_events() - return self.figure.canvas._draw_count - self.start - - -class BaseTestMatplotlibDataViewer(object): - """ - Base class to test viewers based on MatplotlibDataViewer. This only runs - a subset of tests that relate to functionality implemented in - MatplotlibDataViewer and specific viewers are responsible for implementing - a more complete test suite. - - Viewers based on this should inherit from this test class and define the - following attributes: - - * ``data``: an instance of a data object that works by default in the viewer - * ``viewer_cls``: the viewer class - - It is then safe to assume that ``data_collection``, ``viewer``, and ``hub`` - are defined when writing tests. - """ - - def setup_method(self, method): - - if OBJGRAPH_INSTALLED: - self.viewer_count_start = self.viewer_count - - self.data = self.init_data() - - self.application = GlueApplication() - self.session = self.application.session - self.hub = self.session.hub - - self.data_collection = self.session.data_collection - self.data_collection.append(self.data) - - self.viewer = self.viewer_cls(self.session) - - self.data_collection.register_to_hub(self.hub) - self.viewer.register_to_hub(self.hub) - - def init_subset(self): - cid = self.data.main_components[0] - self.data_collection.new_subset_group('subset 1', cid > 0) - - @property - def viewer_count(self): - process_events() - obj = objgraph.by_type(self.viewer_cls.__name__) - return len(obj) - - def teardown_method(self, method): - - if self.viewer is not None: - self.viewer.close() - self.application.close() - - # Matplotlib 3.5 introduced a memory leak when resizing the viewer - # in https://github.com/matplotlib/matplotlib/pull/19255 so for now - # we skip the affected test for the objgraph testing - if method.__name__ == 'test_aspect_resize': - return - - # The following seems to fail on Python 3.10 - to be investigated - if sys.version_info[:2] >= (3, 10) and method.__name__ == 'test_session_round_trip': - return - - # The following is a check to make sure that once the viewer and - # application have been closed, there are no leftover references to - # the data viewer. This was introduced because there were previously - # circular references that meant that viewer instances were not - # properly garbage collected, which in turn meant they still reacted - # in some cases to events. - if OBJGRAPH_INSTALLED: - self.viewer = None - self.application = None - if self.viewer_count > self.viewer_count_start: - objgraph.show_backrefs(objgraph.by_type(self.viewer_cls.__name__)) - raise ValueError("No net viewers should be created in tests") - - def test_add_data(self): - - # Add a dataset with no subsets and make sure the appropriate layer - # state and layer artists are created - - self.viewer.add_data(self.data) - - assert len(self.viewer.layers) == 1 - assert self.viewer.layers[0].layer is self.data - - assert len(self.viewer.state.layers) == 1 - assert self.viewer.state.layers[0].layer is self.data - - def test_add_data_with_subset(self): - - # Make sure that if subsets are present in the data, they are added - # automatically - - self.init_subset() - self.viewer.add_data(self.data) - - assert len(self.viewer.layers) == 2 - assert self.viewer.layers[0].layer is self.data - assert self.viewer.layers[1].layer is self.data.subsets[0] - - assert len(self.viewer.state.layers) == 2 - assert self.viewer.state.layers[0].layer is self.data - assert self.viewer.state.layers[1].layer is self.data.subsets[0] - - def test_add_data_then_subset(self): - - # Make sure that if a subset is created in a dataset that has already - # been added to a viewer, the subset gets added - - self.viewer.add_data(self.data) - - assert len(self.viewer.layers) == 1 - assert self.viewer.layers[0].layer is self.data - - assert len(self.viewer.state.layers) == 1 - assert self.viewer.state.layers[0].layer is self.data - - self.init_subset() - - assert len(self.viewer.layers) == 2 - assert self.viewer.layers[0].layer is self.data - assert self.viewer.layers[1].layer is self.data.subsets[0] - - assert len(self.viewer.state.layers) == 2 - assert self.viewer.state.layers[0].layer is self.data - assert self.viewer.state.layers[1].layer is self.data.subsets[0] - - def init_draw_count(self): - self.mpl_counter = MatplotlibDrawCounter(self.viewer.axes.figure) - - @property - def draw_count(self): - return self.mpl_counter.draw_count - - def test_single_draw(self): - # Make sure that the number of draws is kept to a minimum - self.init_draw_count() - self.init_subset() - assert self.draw_count == 0 - self.viewer.add_data(self.data) - assert self.draw_count == 1 - - def test_update_subset(self): - - self.init_draw_count() - - # Check that updating a subset causes the plot to be updated - - self.init_subset() - - assert self.draw_count == 0 - - self.viewer.add_data(self.data) - - count_before = self.draw_count - - # Change the subset - cid = self.data.main_components[0] - self.data.subsets[0].subset_state = cid > 1 - - # Make sure the figure has been redrawn - assert self.draw_count - count_before > 0 - - def test_double_add_ignored(self): - self.viewer.add_data(self.data) - assert len(self.viewer.state.layers) == 1 - self.viewer.add_data(self.data) - assert len(self.viewer.state.layers) == 1 - - def test_removing_data_removes_layer_state(self): - # Removing data from data collection should remove data from viewer - self.viewer.add_data(self.data) - assert len(self.viewer.state.layers) == 1 - self.data_collection.remove(self.data) - assert len(self.viewer.state.layers) == 0 - - def test_removing_data_removes_subsets(self): - # Removing data from data collection should remove subsets from viewer - self.init_subset() - self.viewer.add_data(self.data) - assert len(self.viewer.state.layers) == 2 - self.data_collection.remove(self.data) - assert len(self.viewer.state.layers) == 0 - - def test_removing_subset_removes_layers(self): - - # Removing a layer artist removes the corresponding layer state. We need - # to do this with a subset otherwise the viewer is closed - - self.init_subset() - self.viewer.add_data(self.data) - - assert len(self.viewer.layers) == 2 - assert len(self.viewer.state.layers) == 2 - - self.data_collection.remove_subset_group(self.data_collection.subset_groups[0]) - - assert len(self.viewer.layers) == 1 - assert self.viewer.layers[0].layer is self.data - - assert len(self.viewer.state.layers) == 1 - assert self.viewer.state.layers[0].layer is self.data - - def test_removing_layer_artist_removes_layer_state(self): - - # Removing a layer artist removes the corresponding layer state. We need - # to do this with a subset otherwise the viewer is closed - - self.init_subset() - self.viewer.add_data(self.data) - - assert len(self.viewer.layers) == 2 - assert len(self.viewer.state.layers) == 2 - - # self.layers is a copy so we need to remove from the original list - self.viewer._layer_artist_container.remove(self.viewer.layers[1]) - - assert len(self.viewer.layers) == 1 - assert self.viewer.layers[0].layer is self.data - - assert len(self.viewer.state.layers) == 1 - assert self.viewer.state.layers[0].layer is self.data - - def test_removing_layer_state_removes_layer_artist(self): - - # Removing a layer artist removes the corresponding layer state. We need - # to do this with a subset otherwise the viewer is closed - - self.init_subset() - self.viewer.add_data(self.data) - - assert len(self.viewer.layers) == 2 - assert len(self.viewer.state.layers) == 2 - - # self.layers is a copy so we need to remove from the original list - self.viewer.state.layers.pop(1) - - assert len(self.viewer.layers) == 1 - assert self.viewer.layers[0].layer is self.data - - assert len(self.viewer.state.layers) == 1 - assert self.viewer.state.layers[0].layer is self.data - - def test_new_subset_after_remove_data(self): - - # Once we remove a dataset, if we make a new subset, it will not be - # added to the viewer - - self.init_subset() - self.viewer.add_data(self.data) - - assert len(self.viewer.layers) == 2 - assert len(self.viewer.state.layers) == 2 - - self.viewer.state.layers.pop(0) - - self.init_subset() # makes a new subset - - assert len(self.data.subsets) == 2 - - assert len(self.viewer.layers) == 1 - assert self.viewer.layers[0].layer is self.data.subsets[0] - - assert len(self.viewer.state.layers) == 1 - assert self.viewer.state.layers[0].layer is self.data.subsets[0] - - def test_remove_not_present_ignored(self): - data = Data(label='not in viewer') - self.viewer.remove_data(data) - - def test_limits_sync(self): - - viewer_state = self.viewer.state - axes = self.viewer.axes - - if axes.get_adjustable() == 'datalim': - pytest.xfail() - - # Make sure that the viewer state and matplotlib viewer limits and log - # settings are in sync. We start by modifying the state and making sure - # that the axes follow. - - viewer_state.x_min = 3 - viewer_state.x_max = 9 - - viewer_state.y_min = -2 - viewer_state.y_max = 3 - - assert axes.get_xlim() == (3, 9) - assert axes.get_ylim() == (-2, 3) - assert axes.get_xscale() == 'linear' - assert axes.get_yscale() == 'linear' - - viewer_state.x_log = True - - assert axes.get_xlim() == (3, 9) - assert axes.get_ylim() == (-2, 3) - assert axes.get_xscale() == 'log' - assert axes.get_yscale() == 'linear' - - viewer_state.y_log = True - - # FIXME: the limits for y don't seem right, should be adjusted because of log? - assert axes.get_xlim() == (3, 9) - assert axes.get_ylim() == (-2, 3) - assert axes.get_xscale() == 'log' - assert axes.get_yscale() == 'log' - - # Check that changing the axes changes the state - - # NOTE: at the moment this doesn't work because Matplotlib doesn't - # emit events for changing xscale/yscale. This isn't crucial anyway for - # glue, but leaving the tests below in case this is fixed some day. The - # Matplotlib issue is https://github.com/matplotlib/matplotlib/issues/8439 - - # axes.set_xscale('linear') - # - # assert viewer_state.x_min == 3 - # assert viewer_state.x_max == 9 - # assert viewer_state.y_min == -2 - # assert viewer_state.y_max == 3 - # assert not viewer_state.x_log - # assert viewer_state.y_log - # - # axes.set_yscale('linear') - # - # assert viewer_state.x_min == 3 - # assert viewer_state.x_max == 9 - # assert viewer_state.y_min == -2 - # assert viewer_state.y_max == 3 - # assert not viewer_state.x_log - # assert not viewer_state.y_log - - viewer_state.x_log = False - viewer_state.y_log = False - - axes.set_xlim(-1, 4) - - assert viewer_state.x_min == -1 - assert viewer_state.x_max == 4 - assert viewer_state.y_min == -2 - assert viewer_state.y_max == 3 - # assert not viewer_state.x_log - # assert not viewer_state.y_log - - axes.set_ylim(5, 6) - - assert viewer_state.x_min == -1 - assert viewer_state.x_max == 4 - assert viewer_state.y_min == 5 - assert viewer_state.y_max == 6 - # assert not viewer_state.x_log - # assert not viewer_state.y_log - - # TODO: the following test should deal gracefully with the fact that - # some viewers will want to show a Qt error for IncompatibleDataException - def test_add_invalid_data(self): - data2 = Data() - with pytest.raises(IncompatibleDataException): - self.viewer.add_data(data2) - - # Communication tests - - def test_ignore_data_add_message(self): - self.data_collection.append(self.data) - assert len(self.viewer.layers) == 0 - - def test_update_data_ignored_if_data_not_present(self): - self.init_draw_count() - self.data_collection.append(self.data) - ct0 = self.draw_count - self.data.style.color = 'blue' - assert self.draw_count == ct0 - - def test_update_data_processed_if_data_present(self): - self.init_draw_count() - self.data_collection.append(self.data) - self.viewer.add_data(self.data) - ct0 = self.draw_count - self.data.style.color = 'blue' - assert self.draw_count > ct0 - - def test_add_subset_ignored_if_data_not_present(self): - self.data_collection.append(self.data) - sub = self.data.new_subset() - assert sub not in self.viewer._layer_artist_container - - def test_add_subset_processed_if_data_present(self): - self.data_collection.append(self.data) - self.viewer.add_data(self.data) - sub = self.data.new_subset() - assert sub in self.viewer._layer_artist_container - - def test_update_subset_ignored_if_not_present(self): - # This can be quite a difficult test to pass because it makes sure that - # there are absolutely no references to the layer state left over once - # a subset is removed - when originally written this identified quite - # a few places where references were being accidentally kept, and - # resulted in weakref being needed in a number of places. But ultimately - # this test should pass! No cheating :) - self.init_draw_count() - self.data_collection.append(self.data) - self.viewer.add_data(self.data) - sub = self.data.new_subset() - self.viewer.remove_subset(sub) - ct0 = self.draw_count - sub.style.color = 'blue' - assert self.draw_count == ct0 - - def test_update_subset_processed_if_present(self): - self.init_draw_count() - self.data_collection.append(self.data) - self.viewer.add_data(self.data) - sub = self.data.new_subset() - ct0 = self.draw_count - sub.style.color = 'blue' - assert self.draw_count > ct0 - - def test_data_remove_message(self): - self.data_collection.append(self.data) - self.viewer.add_data(self.data) - self.data_collection.remove(self.data) - assert self.data not in self.viewer._layer_artist_container - - def test_subset_remove_message(self): - self.data_collection.append(self.data) - self.viewer.add_data(self.data) - sub = self.data.new_subset() - assert sub in self.viewer._layer_artist_container - sub.delete() - assert sub not in self.viewer._layer_artist_container - - def test_session_round_trip(self, tmpdir): - - self.init_subset() - - ga = GlueApplication(self.data_collection) - ga.show() - - viewer = ga.new_data_viewer(self.viewer_cls) - viewer.add_data(self.data) - - session_file = tmpdir.join('test_session_round_trip.glu').strpath - ga.save_session(session_file) - ga.close() - - ga2 = GlueApplication.restore_session(session_file) - ga2.show() - - viewer2 = ga2.viewers[0][0] - - data2 = ga2.data_collection[0] - - assert viewer2.layers[0].layer is data2 - assert viewer2.layers[1].layer is data2.subsets[0] - - ga2.close() - - def test_apply_roi_undo(self): - - self.data_collection.append(self.data) - self.viewer.add_data(self.data) - - roi = XRangeROI(1, 2) - self.viewer.apply_roi(roi) - - assert len(self.data.subsets) == 1 - - lo1 = self.data.subsets[0].subset_state.lo - hi1 = self.data.subsets[0].subset_state.hi - - roi = XRangeROI(0, 3) - self.viewer.apply_roi(roi) - - assert len(self.data.subsets) == 1 - - lo2 = self.data.subsets[0].subset_state.lo - hi2 = self.data.subsets[0].subset_state.hi - - assert lo2 != lo1 - assert hi2 != hi1 - - self.application.undo() - - assert len(self.data.subsets) == 1 - - assert self.data.subsets[0].subset_state.lo == lo1 - assert self.data.subsets[0].subset_state.hi == hi1 - - self.application.redo() - - assert len(self.data.subsets) == 1 - - assert self.data.subsets[0].subset_state.lo == lo2 - assert self.data.subsets[0].subset_state.hi == hi2 - - def test_numerical_data_changed(self): - self.init_draw_count() - self.init_subset() - assert self.draw_count == 0 - self.viewer.add_data(self.data) - assert self.draw_count == 1 - data = Data(label=self.data.label) - data.coords = self.data.coords - for cid in self.data.main_components: - if self.data.get_kind(cid) == 'numerical': - data.add_component(self.data[cid] * 2, cid.label) - else: - data.add_component(self.data[cid], cid.label) - self.data.update_values_from_data(data) - assert self.draw_count == 2 - - @requires_matplotlib_ge_22 - def test_aspect_resize(self): - - # Make sure that the limits are adjusted appropriately when resizing - # depending on the aspect ratio mode. Note that we don't add any data - # here since it isn't needed for this test. - - # This test works with Matplotlib 2.0 and 2.2 but not 2.1, hence we - # skip it with Matplotlib 2.1 above. - - # Note that we need to explicitly call draw() below because otherwise - # draw_idle is used, which has no guarantee of being effective. - - # Set initial limits to deterministic values - self.viewer.state.aspect = 'auto' - self.viewer.state.x_min = 0. - self.viewer.state.x_max = 1. - self.viewer.state.y_min = 0. - self.viewer.state.y_max = 1. - - self.viewer.state.aspect = 'equal' - - # Resize events only work if widget is visible - self.viewer.show() - self.viewer.figure.canvas.draw() - process_events(wait=0.1) - - def limits(viewer): - return (viewer.state.x_min, viewer.state.x_max, - viewer.state.y_min, viewer.state.y_max) - - # Set viewer to an initial size and save limits - self.viewer.viewer_size = (800, 400) - self.viewer.figure.canvas.draw() - process_events(wait=0.1) - initial_limits = limits(self.viewer) - - # Change the viewer size, and make sure the limits are adjusted - self.viewer.viewer_size = (400, 400) - self.viewer.figure.canvas.draw() - process_events(wait=0.1) - with pytest.raises(AssertionError): - assert_allclose(limits(self.viewer), initial_limits) - - # Now change the viewer size a number of times and make sure if we - # return to the original size, the limits match the initial ones. - self.viewer.viewer_size = (350, 800) - self.viewer.figure.canvas.draw() - process_events(wait=0.1) - self.viewer.viewer_size = (900, 300) - self.viewer.figure.canvas.draw() - process_events(wait=0.1) - self.viewer.viewer_size = (600, 600) - self.viewer.figure.canvas.draw() - process_events(wait=0.1) - self.viewer.viewer_size = (800, 400) - self.viewer.figure.canvas.draw() - process_events(wait=0.1) - assert_allclose(limits(self.viewer), initial_limits) - - # Now check that the limits don't change in 'auto' mode - self.viewer.state.aspect = 'auto' - self.viewer.viewer_size = (900, 300) - self.viewer.figure.canvas.draw() - process_events(wait=0.1) - assert_allclose(limits(self.viewer), initial_limits) - - def test_update_data_values(self): - - # Regression test for a bug that caused some viewers to not behave - # correctly if the data values were updated. - - self.viewer.add_data(self.data) - - data = self.init_data() - self.data_collection.append(data) - - self.data.update_values_from_data(data) - - def test_legend(self): - viewer_state = self.viewer.state - - self.viewer.add_data(self.data) - - # no legend by default - assert self.viewer.axes.get_legend() is None - - self.viewer.state.legend.visible = True - - # a legend appears - legend = self.viewer.axes.get_legend() - assert not (legend is None) - - handles, labels, handler_dict = self.viewer.get_handles_legend() - assert len(handles) == 1 - assert labels[0] == self.data.label - - self.init_subset() - assert len(viewer_state.layers) == 2 - handles, labels, handler_dict = self.viewer.get_handles_legend() - assert len(handles) == 2 - assert labels[1] == 'subset 1' - - # The next set of test check that the legend does not create extra draws ! - def test_legend_single_draw(self): - # Make sure that the number of draws is kept to a minimum - self.viewer.state.legend.visible = True - self.init_draw_count() - self.init_subset() - assert self.draw_count == 0 - self.viewer.add_data(self.data) - assert self.draw_count == 1 - - def test_legend_numerical_data_changed(self): - self.viewer.state.legend.visible = True - self.init_draw_count() - self.init_subset() - assert self.draw_count == 0 - self.viewer.add_data(self.data) - assert self.draw_count == 1 - data = Data(label=self.data.label) - data.coords = self.data.coords - for cid in self.data.main_components: - if self.data.get_kind(cid) == 'numerical': - data.add_component(self.data[cid] * 2, cid.label) - else: - data.add_component(self.data[cid], cid.label) - self.data.update_values_from_data(data) - assert self.draw_count == 2 diff --git a/glue/viewers/matplotlib/qt/tests/test_python_export.py b/glue/viewers/matplotlib/qt/tests/test_python_export.py deleted file mode 100644 index 24213c8f2..000000000 --- a/glue/viewers/matplotlib/qt/tests/test_python_export.py +++ /dev/null @@ -1,62 +0,0 @@ -import os -import sys -import pytest -import subprocess - -from glue.config import settings - -import numpy as np - -from matplotlib.testing.compare import compare_images - -__all__ = ['random_with_nan', 'BaseTestExportPython'] - - -def random_with_nan(nsamples, nan_index): - x = np.random.random(nsamples) - x[nan_index] = np.nan - return x - - -class BaseTestExportPython: - - def assert_same(self, tmpdir, tol=0.1): - - os.chdir(tmpdir.strpath) - - expected = tmpdir.join('expected.png').strpath - script = tmpdir.join('actual.py').strpath - actual = tmpdir.join('glue_plot.png').strpath - - self.viewer.axes.figure.savefig(expected) - - self.viewer.export_as_script(script) - subprocess.call([sys.executable, script]) - - msg = compare_images(expected, actual, tol=tol) - - if msg: - - from base64 import b64encode - - print("SCRIPT:") - with open(script, 'r') as f: - print(f.read()) - - print("EXPECTED:") - with open(expected, 'rb') as f: - print(b64encode(f.read()).decode()) - - print("ACTUAL:") - with open(actual, 'rb') as f: - print(b64encode(f.read()).decode()) - - pytest.fail(msg, pytrace=False) - - def test_color_settings(self, tmpdir): - settings.FOREGROUND_COLOR = '#a51d2d' - settings.BACKGROUND_COLOR = '#99c1f1' - self.viewer._update_appearance_from_settings() - self.assert_same(tmpdir) - settings.reset_defaults() - self.viewer._update_appearance_from_settings() diff --git a/glue/viewers/matplotlib/qt/tests/test_toolbar.py b/glue/viewers/matplotlib/qt/tests/test_toolbar.py deleted file mode 100644 index b7142b72d..000000000 --- a/glue/viewers/matplotlib/qt/tests/test_toolbar.py +++ /dev/null @@ -1,80 +0,0 @@ -# pylint: disable=I0011,W0613,W0201,W0212,E1101,E1103 - -from glue.viewers.matplotlib.qt.data_viewer import MatplotlibDataViewer -from glue.viewers.matplotlib.toolbar_mode import ToolbarModeBase -from glue.core.tests.util import simple_session - - -class ToolbarModeTest(ToolbarModeBase): - - tool_id = 'test' - tool_tip = 'just testing' - icon = 'glue_square' - - def __init__(self, axes, release_callback=None): - super(ToolbarModeTest, self).__init__(axes, release_callback=release_callback) - self.action_text = 'test text' - self.last_mode = None - - def press(self, event): - self.last_mode = 'PRESS' - - def move(self, event): - self.last_mode = 'MOVE' - - -class ExampleViewer(MatplotlibDataViewer): - - def __init__(self, session, parent=None): - super(ExampleViewer, self).__init__(session, parent=parent) - self.axes.plot([1, 2, 3])[0] - - def initialize_toolbar(self): - super(ExampleViewer, self).initialize_toolbar() - self.tool = ToolbarModeTest(self, release_callback=self.callback) - self.toolbar.add_tool(self.tool) - - def callback(self, mode): - self._called_back = True - - -class TestToolbar(object): - - def setup_method(self, method): - self.session = simple_session() - self.viewer = ExampleViewer(self.session) - self._called_back = False - - def teardown_method(self, method): - self.viewer.close() - - def assert_valid_mode_state(self, target_mode): - for tool_id in self.viewer.toolbar.actions: - if tool_id == target_mode and self.viewer.toolbar.actions[tool_id].isCheckable(): - assert self.viewer.toolbar.actions[tool_id].isChecked() - self.viewer.toolbar._active == target_mode - else: - assert not self.viewer.toolbar.actions[tool_id].isChecked() - - def test_callback(self): - self.viewer.toolbar.actions['mpl:home'].trigger() - self.viewer.tool.release(None) - assert self.viewer._called_back - - def test_change_mode(self): - - self.viewer.toolbar.actions['mpl:pan'].toggle() - assert self.viewer.toolbar.active_tool.tool_id == 'mpl:pan' - assert self.viewer._mpl_nav.mode == 'pan/zoom' - - self.viewer.toolbar.actions['mpl:pan'].toggle() - assert self.viewer.toolbar.active_tool is None - assert self.viewer._mpl_nav.mode == '' - - self.viewer.toolbar.actions['mpl:zoom'].trigger() - assert self.viewer.toolbar.active_tool.tool_id == 'mpl:zoom' - assert self.viewer._mpl_nav.mode == 'zoom rect' - - self.viewer.toolbar.actions['test'].trigger() - assert self.viewer.toolbar.active_tool.tool_id == 'test' - assert self.viewer._mpl_nav.mode == '' diff --git a/glue/viewers/matplotlib/qt/tests/test_toolbar_mode.py b/glue/viewers/matplotlib/qt/tests/test_toolbar_mode.py deleted file mode 100644 index b1500b148..000000000 --- a/glue/viewers/matplotlib/qt/tests/test_toolbar_mode.py +++ /dev/null @@ -1,40 +0,0 @@ -from glue.viewers.matplotlib.tests.test_mouse_mode import TestMouseMode, Event - -from ..toolbar_mode import ContrastMode - - -class TestContrastMode(TestMouseMode): - - def mode_factory(self): - return ContrastMode - - def test_move_ignored_if_not_right_drag(self): - e = Event(1, 2, button=1) - self.mode.move(e) - count = self.mode._axes.figure.canvas.get_width_height.call_count - assert count == 0 - - def test_clip_percentile(self): - assert self.mode.get_clip_percentile() == (1, 99) - self.mode.set_clip_percentile(2, 33) - assert self.mode.get_clip_percentile() == (2, 33) - - def test_vmin_vmax(self): - assert self.mode.get_vmin_vmax() == (None, None) - self.mode.set_vmin_vmax(3, 4) - assert self.mode.get_vmin_vmax() == (3, 4) - assert self.mode.get_clip_percentile() == (None, None) - - # TODO: at the moment, this doesn't work because the dialog is non-modal - # assert self.mode.get_vmin_vmax() == (5, 7) - # def test_choose_vmin_vmax(self): - # - # assert self.mode.get_vmin_vmax() == (None, None) - # - # def fill_apply(dialog): - # dialog.vmin.setText('5') - # dialog.vmax.setText('7') - # dialog.accept() - # - # with process_dialog(delay=500, function=fill_apply): - # self.mode.choose_vmin_vmax() diff --git a/glue/viewers/matplotlib/qt/toolbar.py b/glue/viewers/matplotlib/qt/toolbar.py index 89e1425c4..c2ec8c2be 100644 --- a/glue/viewers/matplotlib/qt/toolbar.py +++ b/glue/viewers/matplotlib/qt/toolbar.py @@ -1,109 +1,4 @@ -from matplotlib.backends.backend_qt5 import NavigationToolbar2QT - -from glue.config import viewer_tool -from glue.viewers.common.tool import CheckableTool, Tool - - -__all__ = ['MatplotlibTool', 'MatplotlibCheckableTool', 'HomeTool', 'SaveTool', - 'PanTool', 'ZoomTool'] - - -def _ensure_mpl_nav(viewer): - # Set up virtual Matplotlib navigation toolbar (don't show it) - if not hasattr(viewer, '_mpl_nav'): - viewer._mpl_nav = NavigationToolbar2QT(viewer.central_widget.canvas, viewer) - viewer._mpl_nav.hide() - - -def _cleanup_mpl_nav(viewer): - if getattr(viewer, '_mpl_nav', None) is not None: - viewer._mpl_nav.setParent(None) - try: - viewer._mpl_nav.parent = None - except AttributeError: - pass - viewer._mpl_nav = None - - -class MatplotlibTool(Tool): - - def __init__(self, viewer=None): - super(MatplotlibTool, self).__init__(viewer=viewer) - _ensure_mpl_nav(viewer) - - def close(self): - _cleanup_mpl_nav(self.viewer) - super(MatplotlibTool, self).close() - - -class MatplotlibCheckableTool(CheckableTool): - - def __init__(self, viewer=None): - super(MatplotlibCheckableTool, self).__init__(viewer=viewer) - _ensure_mpl_nav(viewer) - - def close(self): - _cleanup_mpl_nav(self.viewer) - super(MatplotlibCheckableTool, self).close() - - -@viewer_tool -class HomeTool(MatplotlibTool): - - tool_id = 'mpl:home' - icon = 'glue_home' - action_text = 'Home' - tool_tip = 'Reset original zoom' - shortcut = 'H' - - def activate(self): - if hasattr(self.viewer, 'state') and hasattr(self.viewer.state, 'reset_limits'): - self.viewer.state.reset_limits() - else: - self.viewer._mpl_nav.home() - - -@viewer_tool -class SaveTool(MatplotlibTool): - - tool_id = 'mpl:save' - icon = 'glue_filesave' - action_text = 'Save plot to file' - tool_tip = 'Save the figure' - - def activate(self): - self.viewer._mpl_nav.save_figure() - - -@viewer_tool -class PanTool(MatplotlibCheckableTool): - - tool_id = 'mpl:pan' - icon = 'glue_move' - action_text = 'Pan' - tool_tip = 'Pan axes with left mouse, zoom with right' - shortcut = 'M' - - def activate(self): - self.viewer._mpl_nav.pan() - - def deactivate(self): - if hasattr(self.viewer, '_mpl_nav'): - self.viewer._mpl_nav.pan() - - -@viewer_tool -class ZoomTool(MatplotlibCheckableTool): - - tool_id = 'mpl:zoom' - icon = 'glue_zoom_to_rect' - action_text = 'Zoom' - tool_tip = 'Zoom to rectangle' - shortcut = 'Z' - - def activate(self): - self.viewer._mpl_nav.zoom() - - def deactivate(self): - if hasattr(self.viewer, '_mpl_nav'): - self.viewer._mpl_nav.zoom() +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.viewers.matplotlib.qt.toolbar is deprecated, use glue_qt.viewers.matplotlib.toolbar instead', GlueDeprecationWarning) +from glue_qt.viewers.matplotlib.toolbar import * # noqa diff --git a/glue/viewers/matplotlib/qt/toolbar_mode.py b/glue/viewers/matplotlib/qt/toolbar_mode.py index c7c650149..69af6a89e 100644 --- a/glue/viewers/matplotlib/qt/toolbar_mode.py +++ b/glue/viewers/matplotlib/qt/toolbar_mode.py @@ -1,198 +1,4 @@ -import os - -from qtpy import QtGui, QtWidgets -from glue.utils import nonpartial -from glue.utils.qt import load_ui, cmap2pixmap -from glue.viewers.common.tool import Tool -from glue.config import viewer_tool -from glue.viewers.matplotlib.toolbar_mode import ToolbarModeBase - -__all__ = ['ContrastMode', 'ColormapMode'] - - -@viewer_tool -class ContrastMode(ToolbarModeBase): - """ - Uses right mouse button drags to set bias and contrast, DS9-style. - - The horizontal position of the mouse sets the bias, the vertical position - sets the contrast. - - The move_callback defaults to calling _set_norm on the viewer with the - instance of ConstrastMode as the sole argument. - """ - - icon = 'glue_contrast' - tool_id = 'image:contrast' - action_text = 'Contrast' - tool_tip = 'Adjust the bias/contrast' - shortcut = 'B' - - def __init__(self, viewer, **kwargs): - - super(ContrastMode, self).__init__(viewer, **kwargs) - - self.bias = 0.5 - self.contrast = 1.0 - - self._last = None - self._result = None - self._percent_lo = 1. - self._percent_hi = 99. - self.stretch = 'linear' - self._vmin = None - self._vmax = None - - def set_clip_percentile(self, lo, hi): - """Percentiles at which to clip the data at black/white""" - if lo == self._percent_lo and hi == self._percent_hi: - return - self._percent_lo = lo - self._percent_hi = hi - self._vmin = None - self._vmax = None - - def get_clip_percentile(self): - if self._vmin is None and self._vmax is None: - return self._percent_lo, self._percent_hi - return None, None - - def get_vmin_vmax(self): - if self._percent_lo is None or self._percent_hi is None: - return self._vmin, self._vmax - return None, None - - def set_vmin_vmax(self, vmin, vmax): - if vmin == self._vmin and vmax == self._vmax: - return - self._percent_hi = self._percent_lo = None - self._vmin = vmin - self._vmax = vmax - - def choose_vmin_vmax(self): - dialog = load_ui('contrastlimits.ui', None, - directory=os.path.dirname(__file__)) - v = QtGui.QDoubleValidator() - dialog.vmin.setValidator(v) - dialog.vmax.setValidator(v) - - vmin, vmax = self.get_vmin_vmax() - if vmin is not None: - dialog.vmin.setText(str(vmin)) - if vmax is not None: - dialog.vmax.setText(str(vmax)) - - def _apply(): - try: - vmin = float(dialog.vmin.text()) - vmax = float(dialog.vmax.text()) - self.set_vmin_vmax(vmin, vmax) - if self._move_callback is not None: - self._move_callback(self) - except ValueError: - pass - - bb = dialog.buttonBox - bb.button(bb.Apply).clicked.connect(_apply) - dialog.accepted.connect(_apply) - dialog.show() - dialog.raise_() - dialog.exec_() - - def move(self, event): - """ MoveEvent. Update bias and contrast on Right Mouse button drag """ - if event.button != 3: # RMB drag only - return - x, y = event.x, event.y - dx, dy = self._axes.figure.canvas.get_width_height() - x = 1.0 * x / dx - y = 1.0 * y / dy - - self.bias = x - self.contrast = (1 - y) * 10 - - super(ContrastMode, self).move(event) - - def menu_actions(self): - result = [] - - a = QtWidgets.QAction("minmax", None) - a.triggered.connect(nonpartial(self.set_clip_percentile, 0, 100)) - result.append(a) - - a = QtWidgets.QAction("99%", None) - a.triggered.connect(nonpartial(self.set_clip_percentile, 1, 99)) - result.append(a) - - a = QtWidgets.QAction("95%", None) - a.triggered.connect(nonpartial(self.set_clip_percentile, 5, 95)) - result.append(a) - - a = QtWidgets.QAction("90%", None) - a.triggered.connect(nonpartial(self.set_clip_percentile, 10, 90)) - result.append(a) - - rng = QtWidgets.QAction("Set range...", None) - rng.triggered.connect(nonpartial(self.choose_vmin_vmax)) - result.append(rng) - - a = QtWidgets.QAction("", None) - a.setSeparator(True) - result.append(a) - - a = QtWidgets.QAction("linear", None) - a.triggered.connect(nonpartial(setattr, self, 'stretch', 'linear')) - result.append(a) - - a = QtWidgets.QAction("log", None) - a.triggered.connect(nonpartial(setattr, self, 'stretch', 'log')) - result.append(a) - - a = QtWidgets.QAction("sqrt", None) - a.triggered.connect(nonpartial(setattr, self, 'stretch', 'sqrt')) - result.append(a) - - a = QtWidgets.QAction("asinh", None) - a.triggered.connect(nonpartial(setattr, self, 'stretch', 'arcsinh')) - result.append(a) - - for r in result: - if r is rng: - continue - if self._move_callback is not None: - r.triggered.connect(nonpartial(self._move_callback, self)) - - return result - - -class ColormapAction(QtWidgets.QAction): - - def __init__(self, label, cmap, parent): - super(ColormapAction, self).__init__(label, parent) - self.cmap = cmap - pm = cmap2pixmap(cmap) - self.setIcon(QtGui.QIcon(pm)) - - -@viewer_tool -class ColormapMode(Tool): - """ - A tool to change the colormap used in a viewer. - - This calls a ``set_cmap`` method on the viewer, which should take the name - of the colormap as the sole argument. - """ - - icon = 'glue_rainbow' - tool_id = 'image:colormap' - action_text = 'Set color scale' - tool_tip = 'Set color scale' - - def menu_actions(self): - from glue import config - acts = [] - for label, cmap in config.colormaps: - a = ColormapAction(label, cmap, self.viewer) - a.triggered.connect(nonpartial(self.viewer.set_cmap, cmap)) - acts.append(a) - return acts +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.viewers.matplotlib.qt.toolbar_mode is deprecated, use glue_qt.viewers.matplotlib.toolbar_mode instead', GlueDeprecationWarning) +from glue_qt.viewers.matplotlib.toolbar_mode import * # noqa diff --git a/glue/viewers/matplotlib/qt/widget.py b/glue/viewers/matplotlib/qt/widget.py old mode 100755 new mode 100644 index 5010b13bf..d9b1a89ac --- a/glue/viewers/matplotlib/qt/widget.py +++ b/glue/viewers/matplotlib/qt/widget.py @@ -1,181 +1,4 @@ -#!/usr/bin/env python - import warnings -import matplotlib -from matplotlib.backend_bases import KeyEvent, MouseEvent, ResizeEvent -from matplotlib.figure import Figure - -from qtpy import QtCore, QtGui, QtWidgets -from qtpy.QtCore import Qt, QRectF -from glue.config import settings -from glue.utils.matplotlib import DEFER_DRAW_BACKENDS, MATPLOTLIB_GE_36 - -from matplotlib.backends.backend_qt5 import FigureManagerQT -from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg - -__all__ = ['MplCanvas', 'MplWidget'] - -# Register the Qt backend with defer_draw -DEFER_DRAW_BACKENDS.append(FigureCanvasQTAgg) - -# We want to ignore warnings about left==right and bottom==top since these are -# not critical and the default behavior makes sense. -warnings.filterwarnings('ignore', '.*Attempting to set identical left==right', UserWarning) -warnings.filterwarnings('ignore', '.*Attempting to set identical bottom==top', UserWarning) - - -class MplCanvas(FigureCanvasQTAgg): - - """Class to represent the FigureCanvas widget""" - - rightDrag = QtCore.Signal(float, float) - leftDrag = QtCore.Signal(float, float) - homeButton = QtCore.Signal() - resize_begin = QtCore.Signal() - resize_end = QtCore.Signal() - - def __init__(self): - - self._draw_count = 0 - interactive = matplotlib.is_interactive() - matplotlib.interactive(False) - self.roi_callback = None - - self._draw_zoom_rect = None - - self.fig = Figure(facecolor=settings.BACKGROUND_COLOR) - - FigureCanvasQTAgg.__init__(self, self.fig) - FigureCanvasQTAgg.setSizePolicy(self, - QtWidgets.QSizePolicy.Expanding, - QtWidgets.QSizePolicy.Expanding) - - FigureCanvasQTAgg.updateGeometry(self) - self.manager = FigureManagerQT(self, 0) - matplotlib.interactive(interactive) - - self._resize_timer = QtCore.QTimer() - self._resize_timer.setInterval(250) - self._resize_timer.setSingleShot(True) - self._resize_timer.timeout.connect(self._on_timeout) - - self.renderer = None - - def _on_timeout(self): - buttons = QtWidgets.QApplication.instance().mouseButtons() - if buttons != Qt.NoButton: - self._resize_timer.start() - else: - self.resize_end.emit() - - def paintEvent(self, event): - - # super needs this - if self.renderer is None: - self.renderer = self.get_renderer() - - super(MplCanvas, self).paintEvent(event) - - # See drawRectangle for what _draw_zoom_rect is - if self._draw_zoom_rect is not None: - painter = QtGui.QPainter(self) - self._draw_zoom_rect(painter) - painter.end() - - if self.roi_callback is not None: - self.roi_callback(self) - - def drawRectangle(self, rect): - - # The default zoom rectangle in Matplotlib is quite faint, and there is - # no easy mechanism for changing the default appearance. However, the - # drawing of the zoom rectangle is done in the public method - # drawRectangle on the canvas. This method sets up a callback function - # that is then called during paintEvent. However, we shouldn't use the - # same private attribute since this might break, so we use a private - # attribute with a different name, which means the one matplotlib uses - # will remain empty and not plot anything. - - if rect is None: - _draw_zoom_rect = None - else: - def _draw_zoom_rect(painter): - pen = QtGui.QPen(QtGui.QPen(Qt.red, 2, Qt.DotLine)) - painter.setPen(pen) - try: - dpi_ratio = self.devicePixelRatio() or 1 - except AttributeError: # Matplotlib <2 - dpi_ratio = 1 - painter.drawRect(QRectF(*(pt / dpi_ratio for pt in rect))) - - # This function will be called at the end of the paintEvent - self._draw_zoom_rect = _draw_zoom_rect - - # We need to call update to force the canvas to be painted again - self.update() - - def resizeEvent(self, event): - if not self._resize_timer.isActive(): - self.resize_begin.emit() - self._resize_timer.start() - super(MplCanvas, self).resizeEvent(event) - - def draw(self, *args, **kwargs): - self._draw_count += 1 - return super(MplCanvas, self).draw(*args, **kwargs) - - def keyPressEvent(self, event): - event.setAccepted(False) - super(MplCanvas, self).keyPressEvent(event) - - # FigureCanvasBase methods deprecated in 3.6 - see - # https://github.com/matplotlib/matplotlib/pull/16931 - # adding replacements here for tests convenience. - - if MATPLOTLIB_GE_36: - def resize_event(self): - self.callbacks.process('resize_event', ResizeEvent('resize_event', self)) - - def key_press_event(self, event): - self.callbacks.process('key_press_event', KeyEvent('key_press_event', self, event)) - - def key_release_event(self, event): - self.callbacks.process('key_release_event', KeyEvent('key_release_event', self, event)) - - def button_press_event(self, *event): - self.callbacks.process('button_press_event', - MouseEvent('button_press_event', self, *event)) - - def button_release_event(self, *event): - self.callbacks.process('button_release_event', - MouseEvent('button_release_event', self, *event)) - - def motion_notify_event(self, *event): - self.callbacks.process('motion_notify_event', - MouseEvent('motion_notify_event', self, *event)) - - -class MplWidget(QtWidgets.QWidget): - - """Widget defined in Qt Designer""" - - # signals - rightDrag = QtCore.Signal(float, float) - leftDrag = QtCore.Signal(float, float) - - def __init__(self, parent=None): - # initialization of Qt MainWindow widget - QtWidgets.QWidget.__init__(self, parent) - # set the canvas to the Matplotlib widget - self.canvas = MplCanvas() - # create a vertical box layout - self.vbl = QtWidgets.QVBoxLayout() - self.vbl.setContentsMargins(0, 0, 0, 0) - self.vbl.setSpacing(0) - # add mpl widget to the vertical box - self.vbl.addWidget(self.canvas) - # set the layout to the vertical box - self.setLayout(self.vbl) - - self.canvas.rightDrag.connect(self.rightDrag) - self.canvas.leftDrag.connect(self.leftDrag) +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.viewers.matplotlib.qt.widget is deprecated, use glue_qt.viewers.matplotlib.widget instead', GlueDeprecationWarning) +from glue_qt.viewers.matplotlib.widget import * # noqa diff --git a/glue/viewers/profile/qt/__init__.py b/glue/viewers/profile/qt/__init__.py index b1a0a4df1..2b9e5fa54 100644 --- a/glue/viewers/profile/qt/__init__.py +++ b/glue/viewers/profile/qt/__init__.py @@ -1,6 +1,4 @@ -from .data_viewer import ProfileViewer # noqa - - -def setup(): - from glue.config import qt_client - qt_client.add(ProfileViewer) +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.viewers.profile.qt is deprecated, use glue_qt.viewers.profile instead', GlueDeprecationWarning) +from glue_qt.viewers.profile import * # noqa diff --git a/glue/viewers/profile/qt/data_viewer.py b/glue/viewers/profile/qt/data_viewer.py index 38def7c94..77ed871f7 100644 --- a/glue/viewers/profile/qt/data_viewer.py +++ b/glue/viewers/profile/qt/data_viewer.py @@ -1,33 +1,4 @@ -from glue.utils import defer_draw, decorate_all_methods - -from glue.viewers.matplotlib.qt.data_viewer import MatplotlibDataViewer -from glue.viewers.profile.qt.layer_style_editor import ProfileLayerStyleEditor -from glue.viewers.profile.qt.layer_artist import QThreadedProfileLayerArtist -from glue.viewers.profile.qt.options_widget import ProfileOptionsWidget -from glue.viewers.profile.state import ProfileViewerState - -from glue.viewers.profile.qt.profile_tools import ProfileAnalysisTool # noqa -from glue.viewers.profile.viewer import MatplotlibProfileMixin - -__all__ = ['ProfileViewer'] - - -@decorate_all_methods(defer_draw) -class ProfileViewer(MatplotlibProfileMixin, MatplotlibDataViewer): - - LABEL = '1D Profile' - _layer_style_widget_cls = ProfileLayerStyleEditor - _state_cls = ProfileViewerState - _options_cls = ProfileOptionsWidget - _data_artist_cls = QThreadedProfileLayerArtist - _subset_artist_cls = QThreadedProfileLayerArtist - - large_data_size = 1e8 - - allow_duplicate_data = True - - tools = ['select:xrange', 'profile-analysis'] - - def __init__(self, session, parent=None, state=None): - MatplotlibDataViewer.__init__(self, session, parent=parent, state=state) - MatplotlibProfileMixin.setup_callbacks(self) +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.viewers.profile.qt.data_viewer is deprecated, use glue_qt.viewers.profile.data_viewer instead', GlueDeprecationWarning) +from glue_qt.viewers.profile.data_viewer import * # noqa diff --git a/glue/viewers/profile/qt/layer_artist.py b/glue/viewers/profile/qt/layer_artist.py index bcb5e7a97..f584c22fd 100644 --- a/glue/viewers/profile/qt/layer_artist.py +++ b/glue/viewers/profile/qt/layer_artist.py @@ -1,62 +1,4 @@ -import time - -from glue.viewers.profile.layer_artist import ProfileLayerArtist -from glue.viewers.matplotlib.qt.compute_worker import ComputeWorker -from glue.utils import defer_draw - -__all__ = ['QThreadedProfileLayerArtist'] - - -class QThreadedProfileLayerArtist(ProfileLayerArtist): - - def __init__(self, axes, viewer_state, layer_state=None, layer=None): - - super(QThreadedProfileLayerArtist, self).__init__(axes, viewer_state, - layer_state=layer_state, layer=layer) - - self.setup_thread() - - def wait(self): - # Wait 0.5 seconds to make sure that the computation has properly started - time.sleep(0.5) - while self._worker.running: - time.sleep(1 / 25) - from glue.utils.qt import process_events - process_events() - - def remove(self): - super(QThreadedProfileLayerArtist, self).remove() - if self._worker is not None: - self._worker.work_queue.put('stop') - self._worker.exit() - # Need to wait otherwise the thread will be destroyed while still - # running, causing a segmentation fault - self._worker.wait() - self._worker = None - - @property - def is_computing(self): - return self._worker is not None and self._worker.running - - def setup_thread(self): - self._worker = ComputeWorker(self._calculate_profile_thread) - self._worker.compute_end.connect(self._calculate_profile_postthread) - self._worker.compute_error.connect(self._calculate_profile_error) - self._worker.compute_start.connect(self.notify_start_computation) - self._worker.start() - - @defer_draw - def _calculate_profile(self, reset=False): - if self.state.layer is not None and self.state.layer.size > 1e7: - self._worker.work_queue.put(reset) - else: - super(QThreadedProfileLayerArtist, self)._calculate_profile(reset=reset) - - def _calculate_profile_postthread(self): - - # If the worker has started running again, we should stop at this point - # since this function will get called again. - if self._worker is not None and self._worker.running: - return - - super(QThreadedProfileLayerArtist, self)._calculate_profile_postthread() +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.viewers.profile.qt.layer_artist is deprecated, use glue_qt.viewers.profile.layer_artist instead', GlueDeprecationWarning) +from glue_qt.viewers.profile.layer_artist import * # noqa diff --git a/glue/viewers/profile/qt/layer_style_editor.py b/glue/viewers/profile/qt/layer_style_editor.py index 1d85c11ea..8ff0884bb 100644 --- a/glue/viewers/profile/qt/layer_style_editor.py +++ b/glue/viewers/profile/qt/layer_style_editor.py @@ -1,30 +1,4 @@ -import os - -from qtpy import QtWidgets - -from echo.qt import autoconnect_callbacks_to_qt -from glue.utils.qt import load_ui - - -class ProfileLayerStyleEditor(QtWidgets.QWidget): - - def __init__(self, layer, parent=None): - - super(ProfileLayerStyleEditor, self).__init__(parent=parent) - - self.ui = load_ui('layer_style_editor.ui', self, - directory=os.path.dirname(__file__)) - - connect_kwargs = {'alpha': dict(value_range=(0, 1))} - - self._connections = autoconnect_callbacks_to_qt(layer.state, self.ui, connect_kwargs) - - self.viewer_state = layer.state.viewer_state - self.viewer_state.add_callback('normalize', self._on_normalize_change) - self._on_normalize_change() - - def _on_normalize_change(self, *event): - self.ui.label_limits.setVisible(self.viewer_state.normalize) - self.ui.valuetext_v_min.setVisible(self.viewer_state.normalize) - self.ui.valuetext_v_max.setVisible(self.viewer_state.normalize) - self.ui.button_flip_limits.setVisible(self.viewer_state.normalize) +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.viewers.profile.qt.layer_style_editor is deprecated, use glue_qt.viewers.profile.layer_style_editor instead', GlueDeprecationWarning) +from glue_qt.viewers.profile.layer_style_editor import * # noqa diff --git a/glue/viewers/profile/qt/layer_style_editor.ui b/glue/viewers/profile/qt/layer_style_editor.ui deleted file mode 100644 index b55fd505e..000000000 --- a/glue/viewers/profile/qt/layer_style_editor.ui +++ /dev/null @@ -1,208 +0,0 @@ - - - Form - - - - 0 - 0 - 292 - 150 - - - - Form - - - - 5 - - - 5 - - - 5 - - - 5 - - - 10 - - - 5 - - - - - - 75 - true - - - - attribute - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - 2 - - - - - - - - padding: 0px - - - ⇄ - - - - - - - - - - - - Qt::Horizontal - - - - 40 - 5 - - - - - - - - - 0 - 0 - - - - - - - - - - - - 75 - true - - - - color - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - 100 - - - Qt::Horizontal - - - - - - - - 75 - true - - - - opacity - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - 75 - true - - - - linewidth - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - 75 - true - - - - limits - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - QComboBox::AdjustToMinimumContentsLengthWithIcon - - - - - - - - QColorBox - QLabel -
glue.utils.qt.colors
-
-
- - -
diff --git a/glue/viewers/profile/qt/mouse_mode.py b/glue/viewers/profile/qt/mouse_mode.py index 223f4aa35..7c14a0143 100644 --- a/glue/viewers/profile/qt/mouse_mode.py +++ b/glue/viewers/profile/qt/mouse_mode.py @@ -1,194 +1,4 @@ -from echo import CallbackProperty, delay_callback -from glue.core.state_objects import State -from glue.viewers.matplotlib.mouse_mode import MouseMode - -__all__ = ['NavigateMouseMode', 'RangeMouseMode'] - - -COLOR = (0.0, 0.25, 0.7) - - -class NavigationModeState(State): - x = CallbackProperty(None) - - -class NavigateMouseMode(MouseMode): - - def __init__(self, viewer, press_callback=None): - super(NavigateMouseMode, self).__init__(viewer) - self.state = NavigationModeState() - self.state.add_callback('x', self._update_artist) - self.pressed = False - self.active = False - self._press_callback = press_callback - - def press(self, event): - if not self.active or not event.inaxes: - return - if self._press_callback is not None: - self._press_callback() - self.pressed = True - self.state.x = event.xdata - - def move(self, event): - if not self.active or not self.pressed or not event.inaxes: - return - self.state.x = event.xdata - - def release(self, event): - if not self.active: - return - self.pressed = False - - def _update_artist(self, *args): - if hasattr(self, '_line'): - if self.state.x is None: - self._line.set_visible(False) - else: - self._line.set_visible(True) - self._line.set_data([self.state.x, self.state.x], [0, 1]) - else: - if self.state.x is not None: - self._line = self._axes.axvline(self.state.x, color=COLOR) - self._canvas.draw_idle() - - def deactivate(self): - if hasattr(self, '_line'): - self._line.set_visible(False) - self._canvas.draw_idle() - super(NavigateMouseMode, self).deactivate() - self.active = False - - def activate(self): - if hasattr(self, '_line'): - self._line.set_visible(True) - self._canvas.draw_idle() - super(NavigateMouseMode, self).activate() - self.active = True - - def clear(self): - self.state.x = None - - -class RangeModeState(State): - - x_min = CallbackProperty(None) - x_max = CallbackProperty(None) - - @property - def x_range(self): - return self.x_min, self.x_max - - -PICK_THRESH = 0.02 - - -class RangeMouseMode(MouseMode): - - def __init__(self, viewer): - super(RangeMouseMode, self).__init__(viewer) - self.state = RangeModeState() - self.state.add_callback('x_min', self._update_artist) - self.state.add_callback('x_max', self._update_artist) - self.pressed = False - - self.mode = None - self.move_params = None - - self.active = False - - def press(self, event): - - if not self.active or not event.inaxes: - return - - self.pressed = True - - x_min, x_max = self._axes.get_xlim() - x_range = abs(x_max - x_min) - - if self.state.x_min is None or self.state.x_max is None: - self.mode = 'move-x-max' - with delay_callback(self.state, 'x_min', 'x_max'): - self.state.x_min = event.xdata - self.state.x_max = event.xdata - elif abs(event.xdata - self.state.x_min) / x_range < PICK_THRESH: - self.mode = 'move-x-min' - elif abs(event.xdata - self.state.x_max) / x_range < PICK_THRESH: - self.mode = 'move-x-max' - elif (event.xdata > self.state.x_min) is (event.xdata < self.state.x_max): - self.mode = 'move' - self.move_params = (event.xdata, self.state.x_min, self.state.x_max) - else: - self.mode = 'move-x-max' - self.state.x_min = event.xdata - - def move(self, event): - - if not self.active or not self.pressed or not event.inaxes: - return - - if self.mode == 'move-x-min': - self.state.x_min = event.xdata - elif self.mode == 'move-x-max': - self.state.x_max = event.xdata - elif self.mode == 'move': - orig_click, orig_x_min, orig_x_max = self.move_params - with delay_callback(self.state, 'x_min', 'x_max'): - self.state.x_min = orig_x_min + (event.xdata - orig_click) - self.state.x_max = orig_x_max + (event.xdata - orig_click) - - def release(self, event): - if not self.active: - return - self.pressed = False - self.mode = None - self.move_params - - def _update_artist(self, *args): - y_min, y_max = self._axes.get_ylim() - if hasattr(self, '_lines'): - if self.state.x_min is None or self.state.x_max is None: - self._lines[0].set_visible(False) - self._lines[1].set_visible(False) - self._interval.set_visible(False) - else: - self._lines[0].set_data([self.state.x_min, self.state.x_min], [0, 1]) - self._lines[1].set_data([self.state.x_max, self.state.x_max], [0, 1]) - self._interval.set_xy([[self.state.x_min, 0], - [self.state.x_min, 1], - [self.state.x_max, 1], - [self.state.x_max, 0], - [self.state.x_min, 0]]) - else: - if self.state.x_min is not None and self.state.x_max is not None: - self._lines = (self._axes.axvline(self.state.x_min, color=COLOR), - self._axes.axvline(self.state.x_max, color=COLOR)) - self._interval = self._axes.axvspan(self.state.x_min, - self.state.x_max, - color=COLOR, alpha=0.05) - self._canvas.draw_idle() - - def deactivate(self): - if hasattr(self, '_lines'): - self._lines[0].set_visible(False) - self._lines[1].set_visible(False) - self._interval.set_visible(False) - - self._canvas.draw_idle() - super(RangeMouseMode, self).deactivate() - self.active = False - - def activate(self): - if hasattr(self, '_lines'): - self._lines[0].set_visible(True) - self._lines[1].set_visible(True) - self._interval.set_visible(True) - self._canvas.draw_idle() - super(RangeMouseMode, self).activate() - self.active = True - - def clear(self): - with delay_callback(self.state, 'x_min', 'x_max'): - self.state.x_min = None - self.state.x_max = None +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.viewers.profile.qt.mouse_mode is deprecated, use glue_qt.viewers.profile.mouse_mode instead', GlueDeprecationWarning) +from glue_qt.viewers.profile.mouse_mode import * # noqa diff --git a/glue/viewers/profile/qt/options_widget.py b/glue/viewers/profile/qt/options_widget.py index 261f16cf3..93ff9ed25 100644 --- a/glue/viewers/profile/qt/options_widget.py +++ b/glue/viewers/profile/qt/options_widget.py @@ -1,56 +1,4 @@ -import os - -from qtpy import QtWidgets - -from glue.core.coordinate_helpers import dependent_axes -from echo.qt import autoconnect_callbacks_to_qt -from glue.utils.qt import load_ui, fix_tab_widget_fontsize - -__all__ = ['ProfileOptionsWidget'] - - -WARNING_TEXT = ("Warning: the coordinate '{label}' is not aligned with pixel " - "grid, so the values shown on the x-axis are approximate.") - - -class ProfileOptionsWidget(QtWidgets.QWidget): - - def __init__(self, viewer_state, session, parent=None): - - super(ProfileOptionsWidget, self).__init__(parent=parent) - - self.ui = load_ui('options_widget.ui', self, - directory=os.path.dirname(__file__)) - - fix_tab_widget_fontsize(self.ui.tab_widget) - - self._connections = autoconnect_callbacks_to_qt(viewer_state, self.ui) - self._connections_axes = autoconnect_callbacks_to_qt(viewer_state, self.ui.axes_editor.ui) - connect_kwargs = {'alpha': dict(value_range=(0, 1))} - self._connections_legend = autoconnect_callbacks_to_qt(viewer_state.legend, self.ui.legend_editor.ui, connect_kwargs) - - self.viewer_state = viewer_state - - self.session = session - - self.viewer_state.add_callback('x_att', self._on_attribute_change) - - self.ui.text_warning.hide() - - def _on_attribute_change(self, *args): - - if (self.viewer_state.reference_data is None or - self.viewer_state.x_att_pixel is None or - self.viewer_state.x_att is self.viewer_state.x_att_pixel): - self.ui.text_warning.hide() - return - - world_warning = len(dependent_axes(self.viewer_state.reference_data.coords, - self.viewer_state.x_att_pixel.axis)) > 1 - - if world_warning: - self.ui.text_warning.show() - self.ui.text_warning.setText(WARNING_TEXT.format(label=self.viewer_state.x_att.label)) - else: - self.ui.text_warning.hide() - self.ui.text_warning.setText('') +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.viewers.profile.qt.options_widget is deprecated, use glue_qt.viewers.profile.options_widget instead', GlueDeprecationWarning) +from glue_qt.viewers.profile.options_widget import * # noqa diff --git a/glue/viewers/profile/qt/options_widget.ui b/glue/viewers/profile/qt/options_widget.ui deleted file mode 100644 index b46fa5967..000000000 --- a/glue/viewers/profile/qt/options_widget.ui +++ /dev/null @@ -1,423 +0,0 @@ - - - Widget - - - - 0 - 0 - 293 - 418 - - - - 1D Profile - - - - 5 - - - 5 - - - 5 - - - 5 - - - 5 - - - - - 0 - - - - General - - - - 10 - - - 10 - - - 10 - - - 10 - - - 10 - - - 5 - - - - - QComboBox::AdjustToMinimumContentsLengthWithIcon - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - 75 - true - - - - function - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - 75 - true - - - - x axis - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Qt::Horizontal - - - - 40 - 5 - - - - - - - - - 75 - true - - - - normalize - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - QComboBox::AdjustToMinimumContentsLengthWithIcon - - - - - - - color: rgb(255, 33, 28) - - - Warning - - - Qt::AlignCenter - - - true - - - - - - - - - - - - - - - - - - 75 - true - - - - reference - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - 75 - true - - - - x unit - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - 75 - true - - - - y_unit - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - - - - - Limits - - - - 10 - - - 10 - - - 10 - - - 10 - - - 10 - - - 5 - - - - - padding: 0px - - - ⇄ - - - - - - - - - - - - - - - - - 75 - true - - - - x axis - - - - - - - - 75 - true - - - - y axis - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - Qt::Horizontal - - - - 40 - 5 - - - - - - - - log - - - true - - - - - - - log - - - true - - - - - bool_x_log - valuetext_x_max - button_flip_x - valuetext_x_min - valuetext_y_min - valuetext_y_max - bool_y_log - label_2 - label_5 - verticalSpacer - horizontalSpacer_2 - - - - Axes - - - - 5 - - - 5 - - - 5 - - - 5 - - - - - - - - - Legend - - - - 5 - - - 5 - - - 5 - - - 5 - - - - - - - - - - - - - - AxesEditorWidget - QWidget -
glue.viewers.matplotlib.qt.axes_editor
-
- - LegendEditorWidget - QWidget -
glue.viewers.matplotlib.qt.legend_editor
-
-
- - -
diff --git a/glue/viewers/profile/qt/profile_tools.py b/glue/viewers/profile/qt/profile_tools.py index 59dd2a7bb..41eb0cf45 100644 --- a/glue/viewers/profile/qt/profile_tools.py +++ b/glue/viewers/profile/qt/profile_tools.py @@ -1,354 +1,4 @@ -import os -import weakref -import traceback -from collections import OrderedDict - -import numpy as np - -from qtpy.QtCore import Qt -from qtpy import QtWidgets, QtGui - -from glue.utils import color2hex -from glue.config import fit_plugin, viewer_tool -from glue.utils.qt import load_ui, fix_tab_widget_fontsize -from glue.viewers.profile.qt.mouse_mode import NavigateMouseMode, RangeMouseMode -from glue.core.qt.fitters import FitSettingsWidget -from glue.utils.qt import Worker -from glue.viewers.common.tool import Tool -from glue.viewers.image.state import AggregateSlice -from glue.core.aggregate import mom1, mom2 -from glue.core import BaseData, Subset -from glue.viewers.image.qt import ImageViewer -from glue.core.link_manager import is_convertible_to_single_pixel_cid -from echo import SelectionCallbackProperty -from echo.qt import connect_combo_selection - -__all__ = ['ProfileTools'] - - -MODES = ['navigate', 'fit', 'collapse'] - -COLLAPSE_FUNCS = OrderedDict([(np.nanmean, 'Mean'), - (np.nanmedian, 'Median'), - (np.nanmin, 'Minimum'), - (np.nanmax, 'Maximum'), - (np.nansum, 'Sum'), - (mom1, 'Moment 1'), - (mom2, 'Moment 2')]) - - -@viewer_tool -class ProfileAnalysisTool(Tool): - - action_text = 'Options' - tool_id = 'profile-analysis' - - def __init__(self, viewer): - super(ProfileAnalysisTool, self).__init__(viewer) - self._profile_tools = ProfileTools(viewer) - container_widget = QtWidgets.QSplitter(Qt.Horizontal) - plot_widget = viewer.centralWidget() - container_widget.addWidget(plot_widget) - container_widget.addWidget(self._profile_tools) - viewer.setCentralWidget(container_widget) - self._profile_tools.enable() - self._profile_tools.hide() - - def activate(self): - if self._profile_tools.isVisible(): - self._profile_tools.hide() - else: - self._profile_tools.show() - - -class ProfileTools(QtWidgets.QWidget): - - fit_function = SelectionCallbackProperty() - collapse_function = SelectionCallbackProperty() - - def __init__(self, parent=None): - - super(ProfileTools, self).__init__(parent=parent) - - self._viewer = weakref.ref(parent) - - self.ui = load_ui('profile_tools.ui', self, - directory=os.path.dirname(__file__)) - - fix_tab_widget_fontsize(self.ui.tabs) - - self.image_viewer = None - - @property - def viewer(self): - return self._viewer() - - def show(self, *args): - super(ProfileTools, self).show(*args) - self._on_tab_change() - - def hide(self, *args): - super(ProfileTools, self).hide(*args) - self.rng_mode.deactivate() - self.nav_mode.deactivate() - - def enable(self): - - self.nav_mode = NavigateMouseMode(self.viewer, - press_callback=self._on_nav_activate) - self.rng_mode = RangeMouseMode(self.viewer) - - self.nav_mode.state.add_callback('x', self._on_slider_change) - - self.ui.tabs.setCurrentIndex(0) - - self.ui.tabs.currentChanged.connect(self._on_tab_change) - - self.ui.button_settings.clicked.connect(self._on_settings) - self.ui.button_fit.clicked.connect(self._on_fit) - self.ui.button_clear.clicked.connect(self._on_clear) - self.ui.button_collapse.clicked.connect(self._on_collapse) - - font = QtGui.QFont("Courier") - font.setStyleHint(font.Monospace) - self.ui.text_log.document().setDefaultFont(font) - self.ui.text_log.setLineWrapMode(self.ui.text_log.NoWrap) - - self.axes = self.viewer.axes - self.canvas = self.axes.figure.canvas - - self._fit_artists = [] - - ProfileTools.fit_function.set_choices(self, list(fit_plugin)) - ProfileTools.fit_function.set_display_func(self, lambda fitter: fitter.label) - self._connection_fit = connect_combo_selection(self, 'fit_function', self.ui.combosel_fit_function) - - ProfileTools.collapse_function.set_choices(self, list(COLLAPSE_FUNCS)) - ProfileTools.collapse_function.set_display_func(self, COLLAPSE_FUNCS.get) - self._connection_collapse = connect_combo_selection(self, 'collapse_function', self.ui.combosel_collapse_function) - - self._toolbar_connected = False - - self.viewer.toolbar_added.connect(self._on_toolbar_added) - - self.viewer.state.add_callback('x_att', self._on_x_att_change) - - def _on_x_att_change(self, *event): - self.nav_mode.clear() - self.rng_mode.clear() - - def _on_nav_activate(self, *args): - self._nav_data = self._visible_data() - self._nav_viewers = {} - for data in self._nav_data: - pix_cid = is_convertible_to_single_pixel_cid(data, self.viewer.state.x_att_pixel) - self._nav_viewers[data] = self._viewers_with_data_slice(data, pix_cid) - - def _on_slider_change(self, *args): - - x = self.nav_mode.state.x - - if x is None: - return - - for data in self._nav_data: - - axis, slc = self._get_axis_and_pixel_slice(data, x) - - for viewer in self._nav_viewers[data]: - slices = list(viewer.state.slices) - slices[axis] = slc - viewer.state.slices = tuple(slices) - - def _get_axis_and_pixel_slice(self, data, x): - - if self.viewer.state.x_att in data.pixel_component_ids: - axis = self.viewer.state.x_att.axis - slc = int(round(x)) - else: - pix_cid = is_convertible_to_single_pixel_cid(data, self.viewer.state.x_att_pixel) - axis = pix_cid.axis - axis_view = [0] * data.ndim - axis_view[pix_cid.axis] = slice(None) - axis_values = data[self.viewer.state.x_att, axis_view] - slc = int(np.argmin(np.abs(axis_values - x))) - - return axis, slc - - def _on_settings(self): - d = FitSettingsWidget(self.fit_function()) - d.exec_() - - def _on_fit(self): - """ - Fit a model to the data - - The fitting happens on a dedicated thread, to keep the UI - responsive - """ - - if self.rng_mode.state.x_min is None or self.rng_mode.state.x_max is None: - return - - x_range = self.rng_mode.state.x_range - fitter = self.fit_function() - - def on_success(result): - fit_results, x, y = result - report = "" - normalize = {} - for layer_artist in fit_results: - report += ("{1}" - "".format(color2hex(layer_artist.state.color), - layer_artist.layer.label)) - report += "
" + fitter.summarize(fit_results[layer_artist], x, y) + "
" - if self.viewer.state.normalize: - normalize[layer_artist] = layer_artist.state.normalize_values - self._report_fit(report) - self._plot_fit(fitter, fit_results, x, y, normalize) - - def on_fail(exc_info): - exc = '\n'.join(traceback.format_exception(*exc_info)) - self._report_fit("Error during fitting:\n%s" % exc) - - def on_done(): - self.ui.button_fit.setText("Fit") - self.ui.button_fit.setEnabled(True) - self.canvas.draw_idle() - - self.ui.button_fit.setText("Running...") - self.ui.button_fit.setEnabled(False) - - w = Worker(self._fit, fitter, xlim=x_range) - w.result.connect(on_success) - w.error.connect(on_fail) - w.finished.connect(on_done) - - self._fit_worker = w # hold onto a reference - w.start() - - def wait_for_fit(self): - self._fit_worker.wait() - - def _report_fit(self, report): - self.ui.text_log.document().setHtml(report) - - def _on_clear(self): - self.ui.text_log.document().setPlainText('') - self._clear_fit() - self.canvas.draw_idle() - - def _fit(self, fitter, xlim=None): - - # We cycle through all the visible layers and get the plotted data - # for each one of them. - - results = {} - for layer in self.viewer.layers: - if layer.enabled and layer.visible: - x, y = layer.state.profile - x = np.asarray(x) - y = np.asarray(y) - keep = (x >= min(xlim)) & (x <= max(xlim)) - if len(x) > 0: - results[layer] = fitter.build_and_fit(x[keep], y[keep]) - - return results, x, y - - def _clear_fit(self): - for artist in self._fit_artists[:]: - artist.remove() - self._fit_artists.remove(artist) - - def _plot_fit(self, fitter, fit_result, x, y, normalize): - - self._clear_fit() - - for layer in fit_result: - # y_model = fitter.predict(fit_result[layer], x) - self._fit_artists.append(fitter.plot(fit_result[layer], self.axes, x, - alpha=layer.state.alpha, - linewidth=layer.state.linewidth * 0.5, - color=layer.state.color, - normalize=normalize.get(layer, None))[0]) - - self.canvas.draw_idle() - - def _visible_data(self): - datasets = set() - for layer_artist in self.viewer.layers: - if layer_artist.enabled and layer_artist.visible: - if isinstance(layer_artist.state.layer, BaseData): - datasets.add(layer_artist.state.layer) - elif isinstance(layer_artist.state.layer, Subset): - datasets.add(layer_artist.state.layer.data) - return list(datasets) - - def _viewers_with_data_slice(self, data, xatt): - - if self.viewer.session.application is None: - return [] - - viewers = [] - for tab in self.viewer.session.application.viewers: - for viewer in tab: - if isinstance(viewer, ImageViewer): - for layer_artist in viewer._layer_artist_container[data]: - if layer_artist.enabled and layer_artist.visible: - if len(viewer.state.slices) >= xatt.axis: - viewers.append(viewer) - return viewers - - def _on_collapse(self): - - if self.rng_mode.state.x_min is None or self.rng_mode.state.x_max is None: - return - - func = self.collapse_function - x_range = self.rng_mode.state.x_range - - for data in self._visible_data(): - - pix_cid = is_convertible_to_single_pixel_cid(data, self.viewer.state.x_att_pixel) - - for viewer in self._viewers_with_data_slice(data, pix_cid): - - slices = list(viewer.state.slices) - - # TODO: don't need to fetch axis twice - axis, imin = self._get_axis_and_pixel_slice(data, x_range[0]) - axis, imax = self._get_axis_and_pixel_slice(data, x_range[1]) - - current_slice = slices[axis] - - if isinstance(current_slice, AggregateSlice): - current_slice = current_slice.center - - imin, imax = min(imin, imax), max(imin, imax) - - slices[axis] = AggregateSlice(slice(imin, imax), - current_slice, - func) - - viewer.state.slices = tuple(slices) - - @property - def mode(self): - return MODES[self.tabs.currentIndex()] - - def _on_toolbar_added(self, *event): - self.viewer.toolbar.tool_activated.connect(self._on_toolbar_activate) - self.viewer.toolbar.tool_deactivated.connect(self._on_tab_change) - - def _on_toolbar_activate(self, *event): - self.rng_mode.deactivate() - self.nav_mode.deactivate() - - def _on_tab_change(self, *event): - mode = self.mode - if mode == 'navigate': - self.rng_mode.deactivate() - self.nav_mode.activate() - else: - self.rng_mode.activate() - self.nav_mode.deactivate() +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.viewers.profile.qt.profile_tools is deprecated, use glue_qt.viewers.profile.profile_tools instead', GlueDeprecationWarning) +from glue_qt.viewers.profile.profile_tools import * # noqa diff --git a/glue/viewers/profile/qt/profile_tools.ui b/glue/viewers/profile/qt/profile_tools.ui deleted file mode 100644 index 3e3b2a65b..000000000 --- a/glue/viewers/profile/qt/profile_tools.ui +++ /dev/null @@ -1,240 +0,0 @@ - - - Form - - - - 0 - 0 - 293 - 254 - - - - Form - - - - - - 1 - - - - Navigate - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - <html><head/><body><p>To <span style=" font-weight:600;">slide </span>through any cubes in image viewers that show the same data as the one here, drag the handle from side to side.</p><p>Note that modifying the sliders in the image viewers will not change the slider here or in other viewers.</p></body></html> - - - Qt::AlignJustify|Qt::AlignVCenter - - - true - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - Fit - - - - - - Click and drag in the profile to define the range over which to fit models to the data. - - - true - - - - - - - - - Function: - - - - - - - - - - - - - - Settings - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Fit - - - - - - - Clear - - - - - - - - - - - - - Collapse - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - Click and drag in the profile to define the range over which to fit models to the data. - - - true - - - - - - - - - Function: - - - - - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Collapse - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - - diff --git a/glue/viewers/profile/qt/tests/__init__.py b/glue/viewers/profile/qt/tests/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/glue/viewers/profile/qt/tests/data/profile_v1.glu b/glue/viewers/profile/qt/tests/data/profile_v1.glu deleted file mode 100644 index ea2f33317..000000000 --- a/glue/viewers/profile/qt/tests/data/profile_v1.glu +++ /dev/null @@ -1,703 +0,0 @@ -{ - "CallbackList": { - "_type": "glue.external.echo.list.CallbackList", - "values": [ - "ProfileLayerState", - "ProfileLayerState_0", - "ProfileLayerState_1" - ] - }, - "CallbackList_0": { - "_type": "glue.external.echo.list.CallbackList", - "values": [ - "ProfileLayerState_2", - "ProfileLayerState_3", - "ProfileLayerState_4" - ] - }, - "CallbackList_1": { - "_type": "glue.external.echo.list.CallbackList", - "values": [ - "ProfileLayerState_5", - "ProfileLayerState_6", - "ProfileLayerState_7" - ] - }, - "CallbackList_2": { - "_type": "glue.external.echo.list.CallbackList", - "values": [ - "ProfileLayerState_8", - "ProfileLayerState_9", - "ProfileLayerState_10" - ] - }, - "Component": { - "_type": "glue.core.component.Component", - "data": { - "_type": "numpy.ndarray", - "data": "k05VTVBZAQB2AHsnZGVzY3InOiAnPGY4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDMsIDQsIDUpLCB9ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAoAAAAAAAAAAAAAAAAAAPA/AAAAAAAAAEAAAAAAAAAIQAAAAAAAABBAAAAAAAAAFEAAAAAAAAAYQAAAAAAAABxAAAAAAAAAIEAAAAAAAAAiQAAAAAAAACRAAAAAAAAAJkAAAAAAAAAoQAAAAAAAACpAAAAAAAAALEAAAAAAAAAuQAAAAAAAADBAAAAAAAAAMUAAAAAAAAAyQAAAAAAAADNAAAAAAAAANEAAAAAAAAA1QAAAAAAAADZAAAAAAAAAN0AAAAAAAAA4QAAAAAAAADlAAAAAAAAAOkAAAAAAAAA7QAAAAAAAADxAAAAAAAAAPUAAAAAAAAA+QAAAAAAAAD9AAAAAAAAAQEAAAAAAAIBAQAAAAAAAAEFAAAAAAACAQUAAAAAAAABCQAAAAAAAgEJAAAAAAAAAQ0AAAAAAAIBDQAAAAAAAAERAAAAAAACAREAAAAAAAABFQAAAAAAAgEVAAAAAAAAARkAAAAAAAIBGQAAAAAAAAEdAAAAAAACAR0AAAAAAAABIQAAAAAAAgEhAAAAAAAAASUAAAAAAAIBJQAAAAAAAAEpAAAAAAACASkAAAAAAAABLQAAAAAAAgEtAAAAAAAAATEAAAAAAAIBMQAAAAAAAAE1AAAAAAACATUA=" - }, - "units": "" - }, - "CoordinateComponent": { - "_type": "glue.core.component.CoordinateComponent", - "axis": 0, - "world": false - }, - "CoordinateComponent_0": { - "_type": "glue.core.component.CoordinateComponent", - "axis": 1, - "world": false - }, - "CoordinateComponent_1": { - "_type": "glue.core.component.CoordinateComponent", - "axis": 2, - "world": false - }, - "CoordinateComponent_2": { - "_type": "glue.core.component.CoordinateComponent", - "axis": 0, - "world": true - }, - "CoordinateComponent_3": { - "_type": "glue.core.component.CoordinateComponent", - "axis": 1, - "world": true - }, - "CoordinateComponent_4": { - "_type": "glue.core.component.CoordinateComponent", - "axis": 2, - "world": true - }, - "Coordinates": { - "_type": "glue.core.coordinates.Coordinates" - }, - "DataCollection": { - "_protocol": 4, - "_type": "glue.core.data_collection.DataCollection", - "cids": [ - "Pixel Axis 0 [z]", - "Pixel Axis 1 [y]", - "Pixel Axis 2 [x]", - "World 0", - "World 1", - "World 2", - "array_0" - ], - "components": [ - "CoordinateComponent", - "CoordinateComponent_0", - "CoordinateComponent_1", - "CoordinateComponent_2", - "CoordinateComponent_3", - "CoordinateComponent_4", - "Component" - ], - "data": [ - "array" - ], - "groups": [ - "Subset 1", - "Subset 2" - ], - "links": [], - "subset_group_count": 2 - }, - "Pixel Axis 0 [z]": { - "_type": "glue.core.component_id.PixelComponentID", - "axis": 0, - "label": "Pixel Axis 0 [z]" - }, - "Pixel Axis 1 [y]": { - "_type": "glue.core.component_id.PixelComponentID", - "axis": 1, - "label": "Pixel Axis 1 [y]" - }, - "Pixel Axis 2 [x]": { - "_type": "glue.core.component_id.PixelComponentID", - "axis": 2, - "label": "Pixel Axis 2 [x]" - }, - "ProfileLayerState": { - "_type": "glue.viewers.profile.state.ProfileLayerState", - "values": { - "alpha": 0.8, - "attribute": "array_0", - "color": "st__#919191", - "layer": "array", - "linewidth": 1, - "percentile": 100, - "v_max": 59.0, - "v_min": 19.0, - "visible": true, - "zorder": 1 - } - }, - "ProfileLayerState_0": { - "_type": "glue.viewers.profile.state.ProfileLayerState", - "values": { - "alpha": 0.5, - "attribute": "array_0", - "color": "st__#e31a1c", - "layer": "Subset 1_0", - "linewidth": 1, - "percentile": 100, - "v_max": 57.0, - "v_min": 17.0, - "visible": true, - "zorder": 2 - } - }, - "ProfileLayerState_1": { - "_type": "glue.viewers.profile.state.ProfileLayerState", - "values": { - "alpha": 0.5, - "attribute": "array_0", - "color": "st__#33a02c", - "layer": "Subset 2_0", - "linewidth": 1, - "percentile": 100, - "v_max": 58.0, - "v_min": 18.0, - "visible": true, - "zorder": 3 - } - }, - "ProfileLayerState_10": { - "_type": "glue.viewers.profile.state.ProfileLayerState", - "values": { - "alpha": 0.5, - "attribute": "array_0", - "color": "st__#33a02c", - "layer": "Subset 2_0", - "linewidth": 1, - "percentile": 100, - "v_max": 30.5, - "v_min": 30.5, - "visible": false, - "zorder": 3 - } - }, - "ProfileLayerState_2": { - "_type": "glue.viewers.profile.state.ProfileLayerState", - "values": { - "alpha": 0.8, - "attribute": "array_0", - "color": "st__#919191", - "layer": "array", - "linewidth": 1, - "percentile": 100, - "v_max": 59.0, - "v_min": 44.0, - "visible": true, - "zorder": 1 - } - }, - "ProfileLayerState_3": { - "_type": "glue.viewers.profile.state.ProfileLayerState", - "values": { - "alpha": 0.5, - "attribute": "array_0", - "color": "st__#e31a1c", - "layer": "Subset 1_0", - "linewidth": 1, - "percentile": 100, - "v_max": 57.0, - "v_min": 42.0, - "visible": false, - "zorder": 2 - } - }, - "ProfileLayerState_4": { - "_type": "glue.viewers.profile.state.ProfileLayerState", - "values": { - "alpha": 0.5, - "attribute": "array_0", - "color": "st__#33a02c", - "layer": "Subset 2_0", - "linewidth": 1, - "percentile": 100, - "v_max": 58.0, - "v_min": 43.0, - "visible": true, - "zorder": 3 - } - }, - "ProfileLayerState_5": { - "_type": "glue.viewers.profile.state.ProfileLayerState", - "values": { - "alpha": 0.8, - "attribute": "array_0", - "color": "st__#919191", - "layer": "array", - "linewidth": 1, - "percentile": 100, - "v_max": 4.0, - "v_min": 0.0, - "visible": true, - "zorder": 1 - } - }, - "ProfileLayerState_6": { - "_type": "glue.viewers.profile.state.ProfileLayerState", - "values": { - "alpha": 0.5, - "attribute": "array_0", - "color": "st__#e31a1c", - "layer": "Subset 1_0", - "linewidth": 1, - "percentile": 100, - "v_max": 2.0, - "v_min": 1.0, - "visible": true, - "zorder": 2 - } - }, - "ProfileLayerState_7": { - "_type": "glue.viewers.profile.state.ProfileLayerState", - "values": { - "alpha": 0.5, - "attribute": "array_0", - "color": "st__#33a02c", - "layer": "Subset 2_0", - "linewidth": 1, - "percentile": 100, - "v_max": 3.0, - "v_min": 3.0, - "visible": false, - "zorder": 3 - } - }, - "ProfileLayerState_8": { - "_type": "glue.viewers.profile.state.ProfileLayerState", - "values": { - "alpha": 0.8, - "attribute": "array_0", - "color": "st__#919191", - "layer": "array", - "linewidth": 1, - "percentile": 100, - "v_max": 31.5, - "v_min": 27.5, - "visible": true, - "zorder": 1 - } - }, - "ProfileLayerState_9": { - "_type": "glue.viewers.profile.state.ProfileLayerState", - "values": { - "alpha": 0.5, - "attribute": "array_0", - "color": "st__#e31a1c", - "layer": "Subset 1_0", - "linewidth": 1, - "percentile": 100, - "v_max": 29.5, - "v_min": 28.5, - "visible": false, - "zorder": 2 - } - }, - "ProfileViewer": { - "_protocol": 1, - "_type": "glue.viewers.profile.qt.data_viewer.ProfileViewer", - "layers": [ - { - "_type": "glue.viewers.profile.layer_artist.ProfileLayerArtist", - "state": "ProfileLayerState" - }, - { - "_type": "glue.viewers.profile.layer_artist.ProfileLayerArtist", - "state": "ProfileLayerState_0" - }, - { - "_type": "glue.viewers.profile.layer_artist.ProfileLayerArtist", - "state": "ProfileLayerState_1" - } - ], - "pos": [ - 0, - 0 - ], - "session": "Session", - "size": [ - 600, - 400 - ], - "state": { - "values": { - "aspect": "st__auto", - "function": "st__maximum", - "layers": "CallbackList", - "normalize": false, - "reference_data": "array", - "show_axes": true, - "x_att": "Pixel Axis 0 [z]", - "x_att_pixel": "Pixel Axis 0 [z]", - "x_axislabel": "st__Pixel Axis 0 [z]", - "x_axislabel_size": 10, - "x_axislabel_weight": "st__normal", - "x_log": false, - "x_max": 2.5, - "x_min": -0.5, - "x_ticklabel_size": 8, - "y_axislabel": "st__Data values", - "y_axislabel_size": 10, - "y_axislabel_weight": "st__normal", - "y_log": false, - "y_max": 63.0, - "y_min": 13.0, - "y_ticklabel_size": 8 - } - } - }, - "ProfileViewer_0": { - "_protocol": 1, - "_type": "glue.viewers.profile.qt.data_viewer.ProfileViewer", - "layers": [ - { - "_type": "glue.viewers.profile.layer_artist.ProfileLayerArtist", - "state": "ProfileLayerState_2" - }, - { - "_type": "glue.viewers.profile.layer_artist.ProfileLayerArtist", - "state": "ProfileLayerState_3" - }, - { - "_type": "glue.viewers.profile.layer_artist.ProfileLayerArtist", - "state": "ProfileLayerState_4" - } - ], - "pos": [ - 600, - 1 - ], - "session": "Session", - "size": [ - 600, - 400 - ], - "state": { - "values": { - "aspect": "st__auto", - "function": "st__maximum", - "layers": "CallbackList_0", - "normalize": true, - "reference_data": "array", - "show_axes": true, - "x_att": "Pixel Axis 1 [y]", - "x_att_pixel": "Pixel Axis 1 [y]", - "x_axislabel": "st__Pixel Axis 1 [y]", - "x_axislabel_size": 10, - "x_axislabel_weight": "st__normal", - "x_log": false, - "x_max": 3.5, - "x_min": -0.5, - "x_ticklabel_size": 8, - "y_axislabel": "st__Normalized data values", - "y_axislabel_size": 10, - "y_axislabel_weight": "st__normal", - "y_log": false, - "y_max": 1.1, - "y_min": -0.1, - "y_ticklabel_size": 8 - } - } - }, - "ProfileViewer_1": { - "_protocol": 1, - "_type": "glue.viewers.profile.qt.data_viewer.ProfileViewer", - "layers": [ - { - "_type": "glue.viewers.profile.layer_artist.ProfileLayerArtist", - "state": "ProfileLayerState_5" - }, - { - "_type": "glue.viewers.profile.layer_artist.ProfileLayerArtist", - "state": "ProfileLayerState_6" - }, - { - "_type": "glue.viewers.profile.layer_artist.ProfileLayerArtist", - "state": "ProfileLayerState_7" - } - ], - "pos": [ - 0, - 400 - ], - "session": "Session", - "size": [ - 600, - 400 - ], - "state": { - "values": { - "aspect": "st__auto", - "function": "st__minimum", - "layers": "CallbackList_1", - "normalize": false, - "reference_data": "array", - "show_axes": true, - "x_att": "Pixel Axis 2 [x]", - "x_att_pixel": "Pixel Axis 2 [x]", - "x_axislabel": "st__Pixel Axis 2 [x]", - "x_axislabel_size": 10, - "x_axislabel_weight": "st__normal", - "x_log": false, - "x_max": 4.5, - "x_min": -0.5, - "x_ticklabel_size": 8, - "y_axislabel": "st__Data values", - "y_axislabel_size": 10, - "y_axislabel_weight": "st__normal", - "y_log": false, - "y_max": 4.4, - "y_min": -0.4, - "y_ticklabel_size": 8 - } - } - }, - "ProfileViewer_2": { - "_protocol": 1, - "_type": "glue.viewers.profile.qt.data_viewer.ProfileViewer", - "layers": [ - { - "_type": "glue.viewers.profile.layer_artist.ProfileLayerArtist", - "state": "ProfileLayerState_8" - }, - { - "_type": "glue.viewers.profile.layer_artist.ProfileLayerArtist", - "state": "ProfileLayerState_9" - }, - { - "_type": "glue.viewers.profile.layer_artist.ProfileLayerArtist", - "state": "ProfileLayerState_10" - } - ], - "pos": [ - 600, - 400 - ], - "session": "Session", - "size": [ - 600, - 400 - ], - "state": { - "values": { - "aspect": "st__auto", - "function": "st__mean", - "layers": "CallbackList_2", - "normalize": false, - "reference_data": "array", - "show_axes": true, - "x_att": "Pixel Axis 2 [x]", - "x_att_pixel": "Pixel Axis 2 [x]", - "x_axislabel": "st__Pixel Axis 2 [x]", - "x_axislabel_size": 10, - "x_axislabel_weight": "st__normal", - "x_log": false, - "x_max": 9.5, - "x_min": -5.5, - "x_ticklabel_size": 8, - "y_axislabel": "st__Data values", - "y_axislabel_size": 10, - "y_axislabel_weight": "st__normal", - "y_log": false, - "y_max": 31.9, - "y_min": 27.1, - "y_ticklabel_size": 8 - } - } - }, - "RangeSubsetState": { - "_type": "glue.core.subset.RangeSubsetState", - "att": "Pixel Axis 2 [x]", - "hi": 2.2825159914712154, - "lo": 0.9925373134328361 - }, - "RangeSubsetState_0": { - "_type": "glue.core.subset.RangeSubsetState", - "att": "Pixel Axis 2 [x]", - "hi": 3.2633262260127935, - "lo": 2.932835820895522 - }, - "Session": { - "_type": "glue.core.session.Session" - }, - "Subset 1": { - "_type": "glue.core.subset_group.SubsetGroup", - "label": "Subset 1", - "state": "RangeSubsetState", - "style": { - "_type": "glue.core.visual.VisualAttributes", - "alpha": 0.5, - "color": "#e31a1c", - "linestyle": "solid", - "linewidth": 2.5, - "marker": "o", - "markersize": 7 - }, - "subsets": [ - "Subset 1_0" - ] - }, - "Subset 1_0": { - "_type": "glue.core.subset_group.GroupedSubset", - "group": "Subset 1", - "style": { - "_type": "glue.core.visual.VisualAttributes", - "alpha": 0.5, - "color": "#e31a1c", - "linestyle": "solid", - "linewidth": 2.5, - "marker": "o", - "markersize": 7 - } - }, - "Subset 2": { - "_type": "glue.core.subset_group.SubsetGroup", - "label": "Subset 2", - "state": "RangeSubsetState_0", - "style": { - "_type": "glue.core.visual.VisualAttributes", - "alpha": 0.5, - "color": "#33a02c", - "linestyle": "solid", - "linewidth": 2.5, - "marker": "o", - "markersize": 7 - }, - "subsets": [ - "Subset 2_0" - ] - }, - "Subset 2_0": { - "_type": "glue.core.subset_group.GroupedSubset", - "group": "Subset 2", - "style": { - "_type": "glue.core.visual.VisualAttributes", - "alpha": 0.5, - "color": "#33a02c", - "linestyle": "solid", - "linewidth": 2.5, - "marker": "o", - "markersize": 7 - } - }, - "World 0": { - "_type": "glue.core.component_id.ComponentID", - "label": "World 0" - }, - "World 1": { - "_type": "glue.core.component_id.ComponentID", - "label": "World 1" - }, - "World 2": { - "_type": "glue.core.component_id.ComponentID", - "label": "World 2" - }, - "__main__": { - "_type": "glue.app.qt.application.GlueApplication", - "data": "DataCollection", - "plugins": [ - "glue_plotly", - "glue.plugins.export_d3po", - "glue.plugins.tools", - "glue_vispy_viewers.scatter", - "glue.plugins.wcs_autolinking", - "glue.viewers.image.qt", - "glue.viewers.scatter.qt", - "glue.viewers.profile.qt", - "glue.io.formats.fits", - "glue.plugins.tools.pv_slicer", - "glue.plugins.dendro_viewer.qt", - "glue.core.data_exporters", - "glue.viewers.table.qt", - "glue.viewers.histogram.qt", - "glue.plugins.coordinate_helpers", - "glue_vispy_viewers.volume", - "glue.plugins.data_factories.spectral_cube" - ], - "session": "Session", - "tab_names": [ - "Tab 1" - ], - "viewers": [ - [ - "ProfileViewer", - "ProfileViewer_0", - "ProfileViewer_1", - "ProfileViewer_2" - ] - ] - }, - "array": { - "_key_joins": [], - "_protocol": 5, - "_type": "glue.core.data.Data", - "components": [ - [ - "Pixel Axis 0 [z]", - "CoordinateComponent" - ], - [ - "Pixel Axis 1 [y]", - "CoordinateComponent_0" - ], - [ - "Pixel Axis 2 [x]", - "CoordinateComponent_1" - ], - [ - "World 0", - "CoordinateComponent_2" - ], - [ - "World 1", - "CoordinateComponent_3" - ], - [ - "World 2", - "CoordinateComponent_4" - ], - [ - "array_0", - "Component" - ] - ], - "coords": "Coordinates", - "label": "array", - "meta": { - "_type": "collections.OrderedDict", - "contents": {} - }, - "primary_owner": [ - "Pixel Axis 0 [z]", - "Pixel Axis 1 [y]", - "Pixel Axis 2 [x]", - "World 0", - "World 1", - "World 2", - "array_0" - ], - "style": { - "_type": "glue.core.visual.VisualAttributes", - "alpha": 0.8, - "color": "#919191", - "linestyle": "solid", - "linewidth": 1, - "marker": "o", - "markersize": 3 - }, - "subsets": [ - "Subset 1_0", - "Subset 2_0" - ], - "uuid": "bcc66633-1537-48a3-8710-96e303bf6a6e" - }, - "array_0": { - "_type": "glue.core.component_id.ComponentID", - "label": "array" - } -} \ No newline at end of file diff --git a/glue/viewers/profile/qt/tests/test_data_viewer.py b/glue/viewers/profile/qt/tests/test_data_viewer.py deleted file mode 100644 index c17446a99..000000000 --- a/glue/viewers/profile/qt/tests/test_data_viewer.py +++ /dev/null @@ -1,554 +0,0 @@ -# pylint: disable=I0011,W0613,W0201,W0212,E1101,E1103 - -import os - -import pytest -import numpy as np - -from astropy import units as u -from astropy.wcs import WCS - -from numpy.testing import assert_equal, assert_allclose - -from glue.core import Data -from glue.core.roi import XRangeROI -from glue.core.subset import SliceSubsetState -from glue.app.qt import GlueApplication -from glue.core.component_link import ComponentLink -from glue.viewers.matplotlib.qt.tests.test_data_viewer import BaseTestMatplotlibDataViewer -from glue.core.coordinates import IdentityCoordinates -from glue.viewers.profile.tests.test_state import SimpleCoordinates -from glue.core.tests.test_state import clone -from glue.core.state import GlueUnSerializer -from glue.plugins.wcs_autolinking.wcs_autolinking import WCSLink -from glue.config import settings, unit_converter - -from ..data_viewer import ProfileViewer - -DATA = os.path.join(os.path.dirname(__file__), 'data') - - -def setup_function(func): - func.ORIGINAL_UNIT_CONVERTER = settings.UNIT_CONVERTER - - -def teardown_function(func): - settings.UNIT_CONVERTER = func.ORIGINAL_UNIT_CONVERTER - - -def teardown_module(): - unit_converter._members.pop('test-spectral2') - - -class TestProfileCommon(BaseTestMatplotlibDataViewer): - - def init_data(self): - return Data(label='d1', - x=np.random.random(24).reshape((3, 4, 2))) - - viewer_cls = ProfileViewer - - @pytest.mark.skip() - def test_double_add_ignored(self): - pass - - -class TestProfileViewer(object): - - def setup_method(self, method): - - self.data = Data(label='d1') - self.data.coords = SimpleCoordinates() - self.data['x'] = np.arange(24).reshape((3, 4, 2)) - - self.data2 = Data(label='d2') - self.data2['y'] = np.arange(24).reshape((3, 4, 2)) - - self.app = GlueApplication() - self.session = self.app.session - self.hub = self.session.hub - - self.data_collection = self.session.data_collection - self.data_collection.append(self.data) - self.data_collection.append(self.data2) - - self.viewer = self.app.new_data_viewer(ProfileViewer) - - def teardown_method(self, method): - self.viewer.close() - self.viewer = None - self.app.close() - self.app = None - - def test_functions(self): - self.viewer.add_data(self.data) - self.viewer.state.function = 'mean' - assert len(self.viewer.layers) == 1 - layer_artist = self.viewer.layers[0] - assert_allclose(layer_artist.state.profile[0], [0, 2, 4]) - assert_allclose(layer_artist.state.profile[1], [3.5, 11.5, 19.5]) - - def test_incompatible(self): - self.viewer.add_data(self.data) - data2 = Data(y=np.random.random((3, 4, 2))) - self.data_collection.append(data2) - self.viewer.add_data(data2) - assert len(self.viewer.layers) == 2 - assert self.viewer.layers[0].enabled - assert not self.viewer.layers[1].enabled - - def test_selection(self): - - self.viewer.add_data(self.data) - - self.viewer.state.x_att = self.data.pixel_component_ids[0] - - roi = XRangeROI(0.9, 2.1) - - self.viewer.apply_roi(roi) - - assert len(self.data.subsets) == 1 - assert_equal(self.data.subsets[0].to_mask()[:, 0, 0], [0, 1, 1]) - - self.viewer.state.x_att = self.data.world_component_ids[0] - - roi = XRangeROI(1.9, 3.1) - - self.viewer.apply_roi(roi) - - assert len(self.data.subsets) == 1 - assert_equal(self.data.subsets[0].to_mask()[:, 0, 0], [0, 1, 0]) - - def test_enabled_layers(self): - - data2 = Data(label='d1', y=np.arange(24).reshape((3, 4, 2)), - coords=IdentityCoordinates(n_dim=3)) - self.data_collection.append(data2) - - self.viewer.add_data(self.data) - self.viewer.add_data(data2) - - assert self.viewer.layers[0].enabled - assert not self.viewer.layers[1].enabled - - self.data_collection.add_link(ComponentLink([data2.world_component_ids[1]], self.data.world_component_ids[0], using=lambda x: 2 * x)) - - assert self.viewer.layers[0].enabled - assert self.viewer.layers[1].enabled - - def test_slice_subset_state(self): - - self.viewer.add_data(self.data) - - subset = self.data.new_subset() - subset.subset_state = SliceSubsetState(self.data, [slice(1, 2), slice(None)]) - - assert self.viewer.layers[0].enabled - assert self.viewer.layers[1].enabled - - def test_clone(self): - - # Regression test for a bug that meant that deserializing a profile - # viewer resulted in disabled layers - - self.viewer.add_data(self.data) - - subset = self.data.new_subset() - subset.subset_state = SliceSubsetState(self.data, [slice(1, 2), slice(None)]) - - app = clone(self.app) - - assert app.viewers[0][0].layers[0].enabled - assert app.viewers[0][0].layers[1].enabled - - app.close() - - def test_incompatible_on_add(self): - - # Regression test for a bug when adding a dataset to a profile viewer - # with a single incompatible subset. - - subset_state = SliceSubsetState(self.data, [slice(1, 2), slice(None)]) - self.data_collection.new_subset_group(subset_state=subset_state, label='s1') - - data2 = Data(x=[[2, 3], [4, 3]], label='d2') - self.data_collection.append(data2) - self.viewer.add_data(data2) - - def test_dependent_axes(self): - - # Make sure that if we pick a world component that has correlations with - # others and is not lined up with the pixel grid, a warning is shown. - - self.viewer.add_data(self.data) - - self.viewer.state.x_att = self.data.pixel_component_ids[0] - assert self.viewer.options_widget().ui.text_warning.text() == '' - - self.viewer.state.x_att = self.data.pixel_component_ids[1] - assert self.viewer.options_widget().ui.text_warning.text() == '' - - self.viewer.state.x_att = self.data.pixel_component_ids[2] - assert self.viewer.options_widget().ui.text_warning.text() == '' - - self.viewer.state.x_att = self.data.world_component_ids[0] - assert self.viewer.options_widget().ui.text_warning.text() == '' - - self.viewer.state.x_att = self.data.world_component_ids[1] - assert self.viewer.options_widget().ui.text_warning.text() != '' - - self.viewer.state.x_att = self.data.world_component_ids[2] - assert self.viewer.options_widget().ui.text_warning.text() != '' - - def test_multiple_data(self, tmpdir): - - # Regression test for issues when multiple datasets are present - # and the reference data is not the default one. - - self.viewer.add_data(self.data) - self.viewer.add_data(self.data2) - assert self.viewer.layers[0].enabled - assert not self.viewer.layers[1].enabled - - # Make sure that when changing the reference data, which layer - # is enabled changes. - self.viewer.state.reference_data = self.data2 - assert not self.viewer.layers[0].enabled - assert self.viewer.layers[1].enabled - - # Make sure that everything works fine after saving/reloading - filename = tmpdir.join('test_multiple_data.glu').strpath - self.session.application.save_session(filename) - with open(filename, 'r') as f: - session = f.read() - state = GlueUnSerializer.loads(session) - ga = state.object('__main__') - viewer = ga.viewers[0][0] - assert not viewer.layers[0].enabled - assert viewer.layers[1].enabled - ga.close() - - @pytest.mark.parametrize('protocol', [1]) - def test_session_back_compat(self, protocol): - - filename = os.path.join(DATA, 'profile_v{0}.glu'.format(protocol)) - - with open(filename, 'r') as f: - session = f.read() - - state = GlueUnSerializer.loads(session) - - ga = state.object('__main__') - - dc = ga.session.data_collection - - assert len(dc) == 1 - - assert dc[0].label == 'array' - - viewer1 = ga.viewers[0][0] - assert len(viewer1.state.layers) == 3 - assert viewer1.state.x_att_pixel is dc[0].pixel_component_ids[0] - assert_allclose(viewer1.state.x_min, -0.5) - assert_allclose(viewer1.state.x_max, 2.5) - assert_allclose(viewer1.state.y_min, 13) - assert_allclose(viewer1.state.y_max, 63) - assert viewer1.state.function == 'maximum' - assert not viewer1.state.normalize - assert viewer1.state.layers[0].visible - assert viewer1.state.layers[1].visible - assert viewer1.state.layers[2].visible - - viewer2 = ga.viewers[0][1] - assert viewer2.state.x_att_pixel is dc[0].pixel_component_ids[1] - assert_allclose(viewer2.state.x_min, -0.5) - assert_allclose(viewer2.state.x_max, 3.5) - assert_allclose(viewer2.state.y_min, -0.1) - assert_allclose(viewer2.state.y_max, 1.1) - assert viewer2.state.function == 'maximum' - assert viewer2.state.normalize - assert viewer2.state.layers[0].visible - assert not viewer2.state.layers[1].visible - assert viewer2.state.layers[2].visible - - viewer3 = ga.viewers[0][2] - assert viewer3.state.x_att_pixel is dc[0].pixel_component_ids[2] - assert_allclose(viewer3.state.x_min, -0.5) - assert_allclose(viewer3.state.x_max, 4.5) - assert_allclose(viewer3.state.y_min, -0.4) - assert_allclose(viewer3.state.y_max, 4.4) - assert viewer3.state.function == 'minimum' - assert not viewer3.state.normalize - assert viewer3.state.layers[0].visible - assert viewer3.state.layers[1].visible - assert not viewer3.state.layers[2].visible - - viewer4 = ga.viewers[0][3] - assert viewer4.state.x_att_pixel is dc[0].pixel_component_ids[2] - assert_allclose(viewer4.state.x_min, -5.5) - assert_allclose(viewer4.state.x_max, 9.5) - assert_allclose(viewer4.state.y_min, 27.1) - assert_allclose(viewer4.state.y_max, 31.9) - assert viewer4.state.function == 'mean' - assert not viewer4.state.normalize - assert viewer4.state.layers[0].visible - assert not viewer4.state.layers[1].visible - assert not viewer4.state.layers[2].visible - - ga.close() - - def test_reset_limits(self): - self.viewer.add_data(self.data) - self.viewer.add_data(self.data2) - self.viewer.state.x_min = 0.2 - self.viewer.state.x_max = 0.4 - self.viewer.state.y_min = 0.3 - self.viewer.state.y_max = 0.5 - self.viewer.state.reset_limits() - assert self.viewer.state.x_min == 0 - assert self.viewer.state.x_max == 4 - assert self.viewer.state.y_min == 7 - assert self.viewer.state.y_max == 23 - - def test_limits_unchanged(self): - # Make sure the limits don't change if a subset is created or another - # dataset added - they should only change if the reference data is changed - self.viewer.add_data(self.data) - self.viewer.state.x_min = 0.2 - self.viewer.state.x_max = 0.4 - self.viewer.state.y_min = 0.3 - self.viewer.state.y_max = 0.5 - self.viewer.add_data(self.data2) - assert self.viewer.state.x_min == 0.2 - assert self.viewer.state.x_max == 0.4 - assert self.viewer.state.y_min == 0.3 - assert self.viewer.state.y_max == 0.5 - roi = XRangeROI(0.9, 2.1) - self.viewer.apply_roi(roi) - assert self.viewer.state.x_min == 0.2 - assert self.viewer.state.x_max == 0.4 - assert self.viewer.state.y_min == 0.3 - assert self.viewer.state.y_max == 0.5 - - def test_layer_visibility(self): - self.viewer.add_data(self.data) - assert self.viewer.layers[0].mpl_artists[0].get_visible() is True - self.viewer.state.layers[0].visible = False - assert self.viewer.layers[0].mpl_artists[0].get_visible() is False - - -@unit_converter('test-spectral2') -class SpectralUnitConverter: - - def equivalent_units(self, data, cid, units): - return map(str, u.Unit(units).find_equivalent_units(include_prefix_units=True, equivalencies=u.spectral())) - - def to_unit(self, data, cid, values, original_units, target_units): - return (values * u.Unit(original_units)).to_value(target_units, equivalencies=u.spectral()) - - -def test_unit_conversion(): - - settings.UNIT_CONVERTER = 'test-spectral2' - - wcs1 = WCS(naxis=1) - wcs1.wcs.ctype = ['FREQ'] - wcs1.wcs.crval = [1] - wcs1.wcs.cdelt = [1] - wcs1.wcs.crpix = [1] - wcs1.wcs.cunit = ['GHz'] - - d1 = Data(f1=[1, 2, 3]) - d1.get_component('f1').units = 'Jy' - d1.coords = wcs1 - - wcs2 = WCS(naxis=1) - wcs2.wcs.ctype = ['WAVE'] - wcs2.wcs.crval = [10] - wcs2.wcs.cdelt = [10] - wcs2.wcs.crpix = [1] - wcs2.wcs.cunit = ['cm'] - - d2 = Data(f2=[2000, 1000, 3000]) - d2.get_component('f2').units = 'mJy' - d2.coords = wcs2 - - app = GlueApplication() - session = app.session - - data_collection = session.data_collection - data_collection.append(d1) - data_collection.append(d2) - - data_collection.add_link(WCSLink(d1, d2)) - - viewer = app.new_data_viewer(ProfileViewer) - viewer.add_data(d1) - viewer.add_data(d2) - - assert viewer.layers[0].enabled - assert viewer.layers[1].enabled - - x, y = viewer.state.layers[0].profile - assert_allclose(x, [1.e9, 2.e9, 3.e9]) - assert_allclose(y, [1, 2, 3]) - - x, y = viewer.state.layers[1].profile - assert_allclose(x, 299792458 / np.array([0.1, 0.2, 0.3])) - assert_allclose(y, [2000, 1000, 3000]) - - assert viewer.state.x_min == 1.e9 - assert viewer.state.x_max == 3.e9 - assert viewer.state.y_min == 1. - assert viewer.state.y_max == 3. - - # Change the limits to make sure they are always converted - viewer.state.x_min = 5e8 - viewer.state.x_max = 4e9 - viewer.state.y_min = 0.5 - viewer.state.y_max = 3.5 - - roi = XRangeROI(1.4e9, 2.1e9) - viewer.apply_roi(roi) - - assert len(d1.subsets) == 1 - assert_equal(d1.subsets[0].to_mask(), [0, 1, 0]) - - assert len(d2.subsets) == 1 - assert_equal(d2.subsets[0].to_mask(), [0, 1, 0]) - - viewer.state.x_display_unit = 'GHz' - viewer.state.y_display_unit = 'mJy' - - x, y = viewer.state.layers[0].profile - assert_allclose(x, [1, 2, 3]) - assert_allclose(y, [1000, 2000, 3000]) - - x, y = viewer.state.layers[1].profile - assert_allclose(x, 2.99792458 / np.array([1, 2, 3])) - assert_allclose(y, [2000, 1000, 3000]) - - assert viewer.state.x_min == 0.5 - assert viewer.state.x_max == 4. - - # Units get reset because they were originally 'native' and 'native' to a - # specific unit always trigger resetting the limits since different datasets - # might be converted in different ways. - assert viewer.state.y_min == 1000. - assert viewer.state.y_max == 3000. - - # Now set the limits explicitly again and make sure in future they are converted - viewer.state.y_min = 500. - viewer.state.y_max = 3500. - - roi = XRangeROI(0.5, 1.2) - viewer.apply_roi(roi) - - assert len(d1.subsets) == 1 - assert_equal(d1.subsets[0].to_mask(), [1, 0, 0]) - - assert len(d2.subsets) == 1 - assert_equal(d2.subsets[0].to_mask(), [0, 0, 1]) - - viewer.state.x_display_unit = 'cm' - viewer.state.y_display_unit = 'Jy' - - roi = XRangeROI(15, 35) - viewer.apply_roi(roi) - - assert len(d1.subsets) == 1 - assert_equal(d1.subsets[0].to_mask(), [1, 0, 0]) - - assert len(d2.subsets) == 1 - assert_equal(d2.subsets[0].to_mask(), [0, 1, 1]) - - assert_allclose(viewer.state.x_min, (4 * u.GHz).to_value(u.cm, equivalencies=u.spectral())) - assert_allclose(viewer.state.x_max, (0.5 * u.GHz).to_value(u.cm, equivalencies=u.spectral())) - assert_allclose(viewer.state.y_min, 0.5) - assert_allclose(viewer.state.y_max, 3.5) - - -def test_unit_conversion_limits(): - - # Regression test for issues that happened when changing attributes with - # different units. - - wcs1 = WCS(naxis=2) - wcs1.wcs.ctype = ['A', 'B'] - wcs1.wcs.crval = [1, 3] - wcs1.wcs.cdelt = [1, 2] - wcs1.wcs.crpix = [1, 1] - wcs1.wcs.cunit = ['deg', 'm'] - - d1 = Data(f1=[[1, 2, 3]], f2=[[10, 20, 30]]) - d1.get_component('f1').units = 'Jy' - d1.get_component('f2').units = 's' - d1.coords = wcs1 - - app = GlueApplication() - session = app.session - - data_collection = session.data_collection - data_collection.append(d1) - - viewer = app.new_data_viewer(ProfileViewer) - viewer.add_data(d1) - - assert viewer.state.x_att is d1.id['B'] - - assert viewer.state.x_min == 3.0 - assert viewer.state.x_max == 3.0 - assert viewer.state.y_min == 0. - assert viewer.state.y_max == 1. - - # Explicitly set unit on y axis to enable unit conversion - viewer.state.y_display_unit = 'Jy' - - assert_allclose(viewer.state.layers[0].profile[0], [3]) - assert_allclose(viewer.state.layers[0].profile[1], [3]) - - # Change the limits to see if they are updated or reset - viewer.state.x_min = 0. - viewer.state.x_max = 10 - viewer.state.y_min = 0.0 - viewer.state.y_max = 4.0 - - viewer.state.x_display_unit = 'cm' - - assert_allclose(viewer.state.layers[0].profile[0], [300]) - assert_allclose(viewer.state.layers[0].profile[1], [3]) - - assert_allclose(viewer.state.x_min, 0) - assert_allclose(viewer.state.x_max, 1000) - assert_allclose(viewer.state.y_min, 0) - assert_allclose(viewer.state.y_max, 4) - - viewer.state.y_display_unit = 'mJy' - - assert_allclose(viewer.state.layers[0].profile[0], [300]) - assert_allclose(viewer.state.layers[0].profile[1], [3000]) - - assert_allclose(viewer.state.x_min, 0) - assert_allclose(viewer.state.x_max, 1000) - assert_allclose(viewer.state.y_min, 0) - assert_allclose(viewer.state.y_max, 4000) - - viewer.state.x_att = d1.id['A'] - - assert_allclose(viewer.state.layers[0].profile[0], [1, 2, 3]) - assert_allclose(viewer.state.layers[0].profile[1], [1000, 2000, 3000]) - - assert_allclose(viewer.state.x_min, 1) - assert_allclose(viewer.state.x_max, 3) - assert_allclose(viewer.state.y_min, 0) - assert_allclose(viewer.state.y_max, 4000) - - viewer.state.layers[0].attribute = d1.id['f2'] - - assert_allclose(viewer.state.layers[0].profile[0], [1, 2, 3]) - assert_allclose(viewer.state.layers[0].profile[1], [10, 20, 30]) - - assert_allclose(viewer.state.x_min, 1) - assert_allclose(viewer.state.x_max, 3) - assert_allclose(viewer.state.y_min, 10) - assert_allclose(viewer.state.y_max, 30) diff --git a/glue/viewers/profile/qt/tests/test_mouse_mode.py b/glue/viewers/profile/qt/tests/test_mouse_mode.py deleted file mode 100644 index e12c873d6..000000000 --- a/glue/viewers/profile/qt/tests/test_mouse_mode.py +++ /dev/null @@ -1,173 +0,0 @@ -from unittest.mock import MagicMock - -from matplotlib import pyplot as plt - -from ..mouse_mode import NavigateMouseMode, RangeMouseMode - - -def test_navigate_mouse_mode(): - - callback = MagicMock() - - fig = plt.figure() - ax = fig.add_subplot(1, 1, 1) - ax.set_xlim(0, 10) - viewer = MagicMock() - viewer.axes = ax - mode = NavigateMouseMode(viewer, press_callback=callback) - - event = MagicMock() - event.xdata = 1.5 - event.inaxes = True - mode.press(event) - assert mode.state.x is None - mode.move(event) - assert mode.state.x is None - mode.release(event) - assert mode.state.x is None - mode.activate() - mode.press(event) - assert callback.call_count == 1 - assert mode.state.x == 1.5 - event.xdata = 2.5 - mode.move(event) - assert mode.state.x == 2.5 - mode.release(event) - event.xdata = 3.5 - mode.move(event) - assert mode.state.x == 2.5 - mode.deactivate() - event.xdata = 1.5 - mode.press(event) - assert callback.call_count == 1 - assert mode.state.x == 2.5 - mode.activate() - event.xdata = 3.5 - mode.press(event) - assert callback.call_count == 2 - assert mode.state.x == 3.5 - event.inaxes = False - event.xdata = 4.5 - mode.press(event) - assert callback.call_count == 2 - assert mode.state.x == 3.5 - - -def test_range_mouse_mode(): - - fig = plt.figure() - ax = fig.add_subplot(1, 1, 1) - ax.set_xlim(0, 10) - viewer = MagicMock() - viewer.axes = ax - mode = RangeMouseMode(viewer) - - event = MagicMock() - event.xdata = 1.5 - event.inaxes = True - - # Pressing, moving, and releasing doesn't do anything until mode is active - mode.press(event) - assert mode.state.x_min is None - assert mode.state.x_max is None - mode.move(event) - assert mode.state.x_min is None - assert mode.state.x_max is None - mode.release(event) - assert mode.state.x_min is None - assert mode.state.x_max is None - - mode.activate() - - # Click and drag then creates an interval where x_min is the first value - # that was clicked and x_max is set to the position of the mouse while - # dragging and at the point of releasing. - - mode.press(event) - assert mode.state.x_min == 1.5 - assert mode.state.x_max == 1.5 - - event.xdata = 2.5 - mode.move(event) - assert mode.state.x_min == 1.5 - assert mode.state.x_max == 2.5 - - event.xdata = 3.5 - mode.move(event) - assert mode.state.x_min == 1.5 - assert mode.state.x_max == 3.5 - - mode.release(event) - event.xdata = 4.5 - mode.move(event) - assert mode.state.x_min == 1.5 - assert mode.state.x_max == 3.5 - - # Test that we can drag the existing edges by clicking on then - - event.xdata = 1.49 - mode.press(event) - event.xdata = 1.25 - mode.move(event) - assert mode.state.x_min == 1.25 - assert mode.state.x_max == 3.5 - mode.release(event) - - event.xdata = 3.51 - mode.press(event) - event.xdata = 4.0 - mode.move(event) - assert mode.state.x_min == 1.25 - assert mode.state.x_max == 4.0 - mode.release(event) - - # Test that we can drag the entire interval by clicking inside - - event.xdata = 2 - mode.press(event) - event.xdata = 3 - mode.move(event) - assert mode.state.x_min == 2.25 - assert mode.state.x_max == 5.0 - mode.release(event) - - # Test that x_range works - - assert mode.state.x_range == (2.25, 5.0) - - # Clicking outside the range starts a new interval - - event.xdata = 6 - mode.press(event) - event.xdata = 7 - mode.move(event) - assert mode.state.x_min == 6 - assert mode.state.x_max == 7 - mode.release(event) - - # Deactivate and activate again to make sure that code for hiding/showing - # artists gets executed - - mode.deactivate() - - event.xdata = 8 - mode.press(event) - assert mode.state.x_min == 6 - assert mode.state.x_max == 7 - - mode.activate() - - event.xdata = 9 - mode.press(event) - event.xdata = 10 - mode.move(event) - assert mode.state.x_min == 9 - assert mode.state.x_max == 10 - - # Check that events outside the axes get ignored - - event.inaxes = False - event.xdata = 11 - mode.press(event) - assert mode.state.x_min == 9 - assert mode.state.x_max == 10 diff --git a/glue/viewers/profile/qt/tests/test_profile_tools.py b/glue/viewers/profile/qt/tests/test_profile_tools.py deleted file mode 100644 index 3e3e1a5fe..000000000 --- a/glue/viewers/profile/qt/tests/test_profile_tools.py +++ /dev/null @@ -1,215 +0,0 @@ -import pytest -import numpy as np - -from numpy.testing import assert_allclose - -from glue.core import Data -from glue.tests.helpers import PYSIDE2_INSTALLED # noqa -from glue.app.qt import GlueApplication -from glue.utils.qt import process_events -from glue.viewers.image.state import AggregateSlice - -from glue.viewers.image.qt import ImageViewer -from glue.viewers.profile.tests.test_state import SimpleCoordinates -from ..data_viewer import ProfileViewer - - -class TestProfileTools(object): - - def setup_method(self, method): - - self.data = Data(label='d1') - self.data.coords = SimpleCoordinates() - self.data['x'] = np.arange(240).reshape((30, 4, 2)).astype(float) - - self.app = GlueApplication() - self.session = self.app.session - self.hub = self.session.hub - - self.data_collection = self.session.data_collection - self.data_collection.append(self.data) - - self.viewer = self.app.new_data_viewer(ProfileViewer) - self.viewer.state.function = 'mean' - - self.viewer.toolbar.active_tool = 'profile-analysis' - - self.profile_tools = self.viewer.toolbar.tools['profile-analysis']._profile_tools - - def teardown_method(self, method): - self.viewer.close() - self.viewer = None - self.app.close() - self.app = None - - def test_navigate_sync_image(self): - - self.viewer.add_data(self.data) - image_viewer = self.app.new_data_viewer(ImageViewer) - image_viewer.add_data(self.data) - assert image_viewer.state.slices == (0, 0, 0) - - self.viewer.state.x_att = self.data.pixel_component_ids[0] - - # Force events to be processed to make sure that the callback functions - # for the computation thread are executed (since they rely on signals) - self.viewer.layers[0].wait() - process_events() - - x, y = self.viewer.axes.transData.transform([[1, 4]])[0] - self.viewer.axes.figure.canvas.button_press_event(x, y, 1) - self.viewer.axes.figure.canvas.button_release_event(x, y, 1) - assert image_viewer.state.slices == (1, 0, 0) - - self.viewer.state.x_att = self.data.world_component_ids[0] - - x, y = self.viewer.axes.transData.transform([[10, 4]])[0] - self.viewer.axes.figure.canvas.button_press_event(x, y, 1) - self.viewer.axes.figure.canvas.button_release_event(x, y, 1) - assert image_viewer.state.slices == (5, 0, 0) - - @pytest.mark.skipif('PYSIDE2_INSTALLED') - def test_fit_polynomial(self): - - # TODO: need to deterministically set to polynomial fitter - - self.viewer.add_data(self.data) - self.profile_tools.ui.tabs.setCurrentIndex(1) - - # First try in pixel coordinates - - self.viewer.state.x_att = self.data.pixel_component_ids[0] - - # Force events to be processed to make sure that the callback functions - # for the computation thread are executed (since they rely on signals) - self.viewer.layers[0].wait() - process_events() - - x, y = self.viewer.axes.transData.transform([[0.9, 4]])[0] - self.viewer.axes.figure.canvas.button_press_event(x, y, 1) - x, y = self.viewer.axes.transData.transform([[15.1, 4]])[0] - self.viewer.axes.figure.canvas.motion_notify_event(x, y, 1) - - assert_allclose(self.profile_tools.rng_mode.state.x_range, (0.9, 15.1)) - - self.profile_tools.ui.button_fit.click() - self.profile_tools.wait_for_fit() - - # Force events to be processed to make sure that the callback functions - # for the computation thread are executed (since they rely on signals) - process_events() - - pixel_log = self.profile_tools.text_log.toPlainText().splitlines() - assert pixel_log[0] == 'd1' - assert pixel_log[1] == 'Coefficients:' - assert pixel_log[-2] == '8.000000e+00' - assert pixel_log[-1] == '3.500000e+00' - - self.profile_tools.ui.button_clear.click() - assert self.profile_tools.text_log.toPlainText() == '' - - # Next, try in world coordinates - - self.viewer.state.x_att = self.data.world_component_ids[0] - - x, y = self.viewer.axes.transData.transform([[1.9, 4]])[0] - self.viewer.axes.figure.canvas.button_press_event(x, y, 1) - x, y = self.viewer.axes.transData.transform([[30.1, 4]])[0] - self.viewer.axes.figure.canvas.motion_notify_event(x, y, 1) - - assert_allclose(self.profile_tools.rng_mode.state.x_range, (1.9, 30.1)) - - self.profile_tools.ui.button_fit.click() - self.profile_tools.wait_for_fit() - process_events() - - world_log = self.profile_tools.text_log.toPlainText().splitlines() - assert world_log[0] == 'd1' - assert world_log[1] == 'Coefficients:' - assert world_log[-2] == '4.000000e+00' - assert world_log[-1] == '3.500000e+00' - - def test_collapse(self): - - self.viewer.add_data(self.data) - - image_viewer = self.app.new_data_viewer(ImageViewer) - image_viewer.add_data(self.data) - - self.profile_tools.ui.tabs.setCurrentIndex(2) - - # First try in pixel coordinates - - self.viewer.state.x_att = self.data.pixel_component_ids[0] - - # Force events to be processed to make sure that the callback functions - # for the computation thread are executed (since they rely on signals) - self.viewer.layers[0].wait() - process_events() - - x, y = self.viewer.axes.transData.transform([[0.9, 4]])[0] - self.viewer.axes.figure.canvas.button_press_event(x, y, 1) - x, y = self.viewer.axes.transData.transform([[15.1, 4]])[0] - self.viewer.axes.figure.canvas.motion_notify_event(x, y, 1) - - self.profile_tools.ui.button_collapse.click() - - assert isinstance(image_viewer.state.slices[0], AggregateSlice) - assert image_viewer.state.slices[0].slice.start == 1 - assert image_viewer.state.slices[0].slice.stop == 15 - assert image_viewer.state.slices[0].center == 0 - assert image_viewer.state.slices[0].function is np.nanmean - - # Next, try in world coordinates - - self.viewer.state.x_att = self.data.world_component_ids[0] - - # Force events to be processed to make sure that the callback functions - # for the computation thread are executed (since they rely on signals) - self.viewer.layers[0].wait() - process_events() - - x, y = self.viewer.axes.transData.transform([[1.9, 4]])[0] - self.viewer.axes.figure.canvas.button_press_event(x, y, 1) - x, y = self.viewer.axes.transData.transform([[30.1, 4]])[0] - self.viewer.axes.figure.canvas.motion_notify_event(x, y, 1) - - self.profile_tools.ui.button_collapse.click() - - assert isinstance(image_viewer.state.slices[0], AggregateSlice) - assert image_viewer.state.slices[0].slice.start == 1 - assert image_viewer.state.slices[0].slice.stop == 15 - assert image_viewer.state.slices[0].center == 0 - assert image_viewer.state.slices[0].function is np.nanmean - - def test_collapse_reverse(self, capsys): - - # Regression test for a bug that caused collapsing to fail if selecting - # the range in the reverse direction. - - self.viewer.add_data(self.data) - - image_viewer = self.app.new_data_viewer(ImageViewer) - image_viewer.add_data(self.data) - - self.profile_tools.ui.tabs.setCurrentIndex(2) - - self.viewer.state.x_att = self.data.pixel_component_ids[0] - - # Force events to be processed to make sure that the callback functions - # for the computation thread are executed (since they rely on signals) - self.viewer.layers[0].wait() - process_events() - - x, y = self.viewer.axes.transData.transform([[15.1, 4]])[0] - self.viewer.axes.figure.canvas.button_press_event(x, y, 1) - x, y = self.viewer.axes.transData.transform([[0.9, 4]])[0] - self.viewer.axes.figure.canvas.motion_notify_event(x, y, 1) - - self.profile_tools.ui.button_collapse.click() - process_events() - - # We use capsys here because the # error is otherwise only apparent in stderr. - out, err = capsys.readouterr() - assert out.strip() == "" - assert err.strip() == "" diff --git a/glue/viewers/profile/qt/tests/test_python_export.py b/glue/viewers/profile/qt/tests/test_python_export.py deleted file mode 100644 index fa9c3b0c0..000000000 --- a/glue/viewers/profile/qt/tests/test_python_export.py +++ /dev/null @@ -1,89 +0,0 @@ -from astropy.utils import NumpyRNGContext - -from glue.core import Data, DataCollection -from glue.app.qt.application import GlueApplication -from glue.viewers.matplotlib.qt.tests.test_python_export import BaseTestExportPython, random_with_nan -from glue.viewers.profile.qt import ProfileViewer -from glue.viewers.profile.tests.test_state import SimpleCoordinates - - -class TestExportPython(BaseTestExportPython): - - def setup_method(self, method): - - self.data = Data(label='d1') - self.data.coords = SimpleCoordinates() - with NumpyRNGContext(12345): - self.data['x'] = random_with_nan(48, 5).reshape((6, 4, 2)) - self.data['y'] = random_with_nan(48, 12).reshape((6, 4, 2)) - self.data_collection = DataCollection([self.data]) - self.app = GlueApplication(self.data_collection) - self.viewer = self.app.new_data_viewer(ProfileViewer) - self.viewer.add_data(self.data) - # Make legend location deterministic - self.viewer.state.legend.location = 'lower left' - - def teardown_method(self, method): - self.viewer.close() - self.viewer = None - self.app.close() - self.app = None - - def test_simple(self, tmpdir): - self.assert_same(tmpdir) - - def test_simple_legend(self, tmpdir): - self.viewer.state.legend.visible = True - self.assert_same(tmpdir) - - def test_color(self, tmpdir): - self.viewer.state.layers[0].color = '#ac0567' - self.assert_same(tmpdir) - - def test_linewidth(self, tmpdir): - self.viewer.state.layers[0].linewidth = 7.25 - self.assert_same(tmpdir) - - def test_max(self, tmpdir): - self.viewer.state.function = 'maximum' - self.assert_same(tmpdir) - - def test_min(self, tmpdir): - self.viewer.state.function = 'minimum' - self.assert_same(tmpdir) - - def test_mean(self, tmpdir): - self.viewer.state.function = 'mean' - self.assert_same(tmpdir) - - def test_median(self, tmpdir): - self.viewer.state.function = 'median' - self.assert_same(tmpdir) - - def test_sum(self, tmpdir): - self.viewer.state.function = 'sum' - self.assert_same(tmpdir) - - def test_normalization(self, tmpdir): - self.viewer.state.normalize = True - self.assert_same(tmpdir) - - def test_subset(self, tmpdir): - self.viewer.state.function = 'mean' - self.data_collection.new_subset_group('mysubset', self.data.id['x'] > 0.25) - self.assert_same(tmpdir) - - def test_subset_legend(self, tmpdir): - self.viewer.state.legend.visible = True - self.viewer.state.function = 'mean' - self.viewer.state.layers[0].linewidth = 7.25 - self.data_collection.new_subset_group('mysubset', self.data.id['x'] > 0.25) - self.assert_same(tmpdir) - - def test_xatt(self, tmpdir): - self.viewer.x_att = self.data.pixel_component_ids[1] - self.assert_same(tmpdir) - - def test_profile_att(self, tmpdir): - self.viewer.layers[0].state.attribute = self.data.id['y'] - self.assert_same(tmpdir) diff --git a/glue/viewers/scatter/layer_artist.py b/glue/viewers/scatter/layer_artist.py index 3c22ccf4a..bb8ac5c6e 100644 --- a/glue/viewers/scatter/layer_artist.py +++ b/glue/viewers/scatter/layer_artist.py @@ -6,9 +6,9 @@ from mpl_scatter_density.generic_density_artist import GenericDensityArtist -from astropy.visualization import (ImageNormalize, LinearStretch, SqrtStretch, - AsinhStretch, LogStretch) +from astropy.visualization import ImageNormalize +from glue.config import stretches from glue.utils import defer_draw, ensure_numerical, datetime64_to_mpl from glue.viewers.scatter.state import ScatterLayerState from glue.viewers.scatter.python_export import python_export_scatter_layer @@ -17,11 +17,9 @@ from matplotlib.lines import Line2D - -STRETCHES = {'linear': LinearStretch, - 'sqrt': SqrtStretch, - 'arcsinh': AsinhStretch, - 'log': LogStretch} +# We keep the following so that scripts exported with previous versions of glue +# continue to work, as they imported STRETCHES from here. +STRETCHES = stretches.members CMAP_PROPERTIES = set(['cmap_mode', 'cmap_att', 'cmap_vmin', 'cmap_vmax', 'cmap']) MARKER_PROPERTIES = set(['size_mode', 'size_att', 'size_vmin', 'size_vmax', 'size_scaling', 'size', 'fill']) @@ -367,7 +365,7 @@ def _update_visual_attributes(self, changed, force=False): set_mpl_artist_cmap(self.density_artist, c, self.state) if force or 'stretch' in changed: - self.density_artist.set_norm(ImageNormalize(stretch=STRETCHES[self.state.stretch]())) + self.density_artist.set_norm(ImageNormalize(stretch=stretches.members[self.state.stretch])) if force or 'dpi' in changed: self.density_artist.set_dpi(self._viewer_state.dpi) diff --git a/glue/viewers/scatter/python_export.py b/glue/viewers/scatter/python_export.py index fcd98287b..717805078 100644 --- a/glue/viewers/scatter/python_export.py +++ b/glue/viewers/scatter/python_export.py @@ -77,7 +77,8 @@ def python_export_scatter_layer(layer, *args): if layer.state.density_map: imports += ["from mpl_scatter_density import ScatterDensityArtist"] - imports += ["from glue.viewers.scatter.layer_artist import DensityMapLimits, STRETCHES"] + imports += ["from glue.config import stretches"] + imports += ["from glue.viewers.scatter.layer_artist import DensityMapLimits"] imports += ["from astropy.visualization import ImageNormalize"] script += "density_limits = DensityMapLimits()\n" @@ -91,7 +92,7 @@ def python_export_scatter_layer(layer, *args): options['color'] = layer.state.color options['vmin'] = code('density_limits.min') options['vmax'] = code('density_limits.max') - options['norm'] = code("ImageNormalize(stretch=STRETCHES['{0}']())".format(layer.state.stretch)) + options['norm'] = code("ImageNormalize(stretch=stretches.members['{0}'])".format(layer.state.stretch)) else: options['c'] = code("layer_data['{0}']".format(layer.state.cmap_att.label)) options['cmap'] = code("plt.cm.{0}".format(layer.state.cmap.name)) diff --git a/glue/viewers/scatter/qt/__init__.py b/glue/viewers/scatter/qt/__init__.py index 090ca88c5..5c9d59659 100644 --- a/glue/viewers/scatter/qt/__init__.py +++ b/glue/viewers/scatter/qt/__init__.py @@ -1,6 +1,4 @@ -from .data_viewer import ScatterViewer # noqa - - -def setup(): - from glue.config import qt_client - qt_client.add(ScatterViewer) +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.viewers.scatter.qt is deprecated, use glue_qt.viewers.scatter instead', GlueDeprecationWarning) +from glue_qt.viewers.scatter import * # noqa diff --git a/glue/viewers/scatter/qt/data_viewer.py b/glue/viewers/scatter/qt/data_viewer.py index 9e4d7f6d8..b9f1f1865 100644 --- a/glue/viewers/scatter/qt/data_viewer.py +++ b/glue/viewers/scatter/qt/data_viewer.py @@ -1,37 +1,4 @@ -from glue.utils import defer_draw, decorate_all_methods -from glue.viewers.matplotlib.qt.data_viewer import MatplotlibDataViewer -from glue.viewers.scatter.qt.layer_style_editor import ScatterLayerStyleEditor -from glue.viewers.scatter.layer_artist import ScatterLayerArtist -from glue.viewers.scatter.qt.options_widget import ScatterOptionsWidget -from glue.viewers.scatter.state import ScatterViewerState - -from glue.viewers.scatter.viewer import MatplotlibScatterMixin - - -__all__ = ['ScatterViewer'] - - -@decorate_all_methods(defer_draw) -class ScatterViewer(MatplotlibScatterMixin, MatplotlibDataViewer): - - LABEL = '2D Scatter' - _layer_style_widget_cls = ScatterLayerStyleEditor - _state_cls = ScatterViewerState - _options_cls = ScatterOptionsWidget - _data_artist_cls = ScatterLayerArtist - _subset_artist_cls = ScatterLayerArtist - - tools = ['select:rectangle', 'select:xrange', - 'select:yrange', 'select:circle', - 'select:polygon'] - - def __init__(self, session, parent=None, state=None): - proj = None if not state or not state.plot_mode else state.plot_mode - MatplotlibDataViewer.__init__(self, session, parent=parent, state=state, projection=proj) - MatplotlibScatterMixin.setup_callbacks(self) - - def limits_to_mpl(self, *args): - # These projections throw errors if we try to set the limits - if self.state.plot_mode in ['aitoff', 'hammer', 'lambert', 'mollweide']: - return - super().limits_to_mpl(*args) +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.viewers.scatter.qt.data_viewer is deprecated, use glue_qt.viewers.scatter.data_viewer instead', GlueDeprecationWarning) +from glue_qt.viewers.scatter.data_viewer import * # noqa diff --git a/glue/viewers/scatter/qt/layer_style_editor.py b/glue/viewers/scatter/qt/layer_style_editor.py index 8c60fe2a7..39c7a490c 100644 --- a/glue/viewers/scatter/qt/layer_style_editor.py +++ b/glue/viewers/scatter/qt/layer_style_editor.py @@ -1,228 +1,4 @@ -import os - -import numpy as np - -from qtpy import QtWidgets, QtGui -from qtpy.QtCore import Qt - -from glue.core import BaseData -from echo.qt import autoconnect_callbacks_to_qt, connect_value -from glue.utils.qt import load_ui, fix_tab_widget_fontsize -from glue.core.exceptions import IncompatibleAttribute - - -class ScatterLayerStyleEditor(QtWidgets.QWidget): - - def __init__(self, layer, parent=None): - - super(ScatterLayerStyleEditor, self).__init__(parent=parent) - - self.ui = load_ui('layer_style_editor.ui', self, - directory=os.path.dirname(__file__)) - - connect_kwargs = {'alpha': dict(value_range=(0, 1)), - 'size_scaling': dict(value_range=(0.1, 10), log=True), - 'density_contrast': dict(value_range=(0, 1)), - 'vector_scaling': dict(value_range=(0.1, 10), log=True)} - self._connections = autoconnect_callbacks_to_qt(layer.state, self.ui, connect_kwargs) - - self._connection_dpi = connect_value(layer.state.viewer_state, 'dpi', self.ui.value_dpi, - value_range=(12, 144), log=True) - - fix_tab_widget_fontsize(self.ui.tab_widget) - - self.layer_state = layer.state - - self.layer_state.add_callback('markers_visible', self._update_markers_visible) - self.layer_state.add_callback('line_visible', self._update_line_visible) - self.layer_state.add_callback('xerr_visible', self._update_xerr_visible) - self.layer_state.add_callback('yerr_visible', self._update_yerr_visible) - self.layer_state.add_callback('vector_visible', self._update_vectors_visible) - - self.layer_state.add_callback('cmap_mode', self._update_cmap_mode) - self.layer_state.add_callback('size_mode', self._update_size_mode) - self.layer_state.add_callback('vector_mode', self._update_vector_mode) - - self.layer_state.add_callback('density_map', self._update_size_mode) - self.layer_state.add_callback('density_map', self._update_warnings) - self.layer_state.add_callback('density_map', self._update_checkboxes) - - self.layer_state.viewer_state.add_callback('x_att', self._update_checkboxes) - self.layer_state.viewer_state.add_callback('y_att', self._update_checkboxes) - if hasattr(self.layer_state.viewer_state, 'plot_mode'): - self.layer_state.viewer_state.add_callback('plot_mode', self._update_vectors_visible) - - self.layer_state.add_callback('layer', self._update_warnings) - - self._update_markers_visible() - self._update_line_visible() - self._update_xerr_visible() - self._update_yerr_visible() - self._update_vectors_visible() - - self._update_size_mode() - self._update_vector_mode() - self._update_cmap_mode() - - self._update_checkboxes() - - self._update_warnings() - - def _update_warnings(self, *args): - - if self.layer_state.layer is None: - n_points = 0 - else: - n_points = np.prod(self.layer_state.layer.shape) - - warning = " (may be slow given data size)" - - for combo, threshold in [(self.ui.combosel_size_mode, 10000), - (self.ui.combosel_cmap_mode, 50000)]: - - if n_points > threshold and not self.layer_state.density_map: - for item in range(combo.count()): - text = combo.itemText(item) - if text != 'Fixed': - combo.setItemText(item, text + warning) - combo.setItemData(item, QtGui.QBrush(Qt.red), Qt.TextColorRole) - else: - for item in range(combo.count()): - text = combo.itemText(item) - if text != 'Fixed': - if warning in text: - combo.setItemText(item, text.replace(warning, '')) - combo.setItemData(item, QtGui.QBrush(), Qt.TextColorRole) - - if n_points > 10000: - self.ui.label_warning_errorbar.show() - else: - self.ui.label_warning_errorbar.hide() - - if n_points > 10000: - self.ui.label_warning_vector.show() - else: - self.ui.label_warning_vector.hide() - - def _update_size_mode(self, size_mode=None): - - visible = not self.layer_state.density_map and not self.layer_state.size_mode == 'Fixed' - self.ui.label_size_attribute.setVisible(visible) - self.ui.combosel_size_att.setVisible(visible) - self.ui.label_size_limits.setVisible(visible) - self.ui.valuetext_size_vmin.setVisible(visible) - self.ui.valuetext_size_vmax.setVisible(visible) - self.ui.button_flip_size.setVisible(visible) - - visible = not self.layer_state.density_map and self.layer_state.size_mode == 'Fixed' - self.ui.value_size.setVisible(visible) - - density = self.layer_state.density_map - self.ui.value_dpi.setVisible(density) - self.ui.label_dpi.setVisible(density) - self.ui.label_stretch.setVisible(density) - self.ui.combosel_stretch.setVisible(density) - self.ui.value_density_contrast.setVisible(density) - self.ui.label_contrast.setVisible(density) - self.ui.combosel_size_mode.setVisible(not density) - self.ui.value_size_scaling.setVisible(not density) - self.ui.label_size_mode.setVisible(not density) - self.ui.label_size_scaling.setVisible(not density) - self.ui.label_fill.setVisible(not density) - self.ui.bool_fill.setVisible(not density) - - def _update_markers_visible(self, *args): - self.ui.combosel_size_mode.setEnabled(self.layer_state.markers_visible) - self.ui.value_size.setEnabled(self.layer_state.markers_visible) - self.ui.combosel_size_att.setEnabled(self.layer_state.markers_visible) - self.ui.valuetext_size_vmin.setEnabled(self.layer_state.markers_visible) - self.ui.valuetext_size_vmax.setEnabled(self.layer_state.markers_visible) - self.ui.button_flip_size.setEnabled(self.layer_state.markers_visible) - self.ui.value_size_scaling.setEnabled(self.layer_state.markers_visible) - self.ui.value_dpi.setEnabled(self.layer_state.markers_visible) - self.ui.combosel_stretch.setEnabled(self.layer_state.markers_visible) - self.ui.label_size_scaling.setEnabled(self.layer_state.markers_visible) - self.ui.combosel_points_mode.setEnabled(self.layer_state.markers_visible) - self.ui.value_density_contrast.setEnabled(self.layer_state.markers_visible) - - def _update_checkboxes(self, *args): - - if isinstance(self.layer_state.layer, BaseData): - layer = self.layer_state.layer - else: - layer = self.layer_state.layer.data - - try: - x_datetime = layer.get_kind(self.layer_state.viewer_state.x_att) == 'datetime' - except IncompatibleAttribute: - x_datetime = False - - try: - y_datetime = layer.get_kind(self.layer_state.viewer_state.y_att) == 'datetime' - except IncompatibleAttribute: - y_datetime = False - - for checkbox in [self.ui.bool_line_visible, self.ui.bool_xerr_visible, - self.ui.bool_yerr_visible, self.ui.bool_vector_visible]: - if self.layer_state.density_map: - checkbox.setEnabled(False) - checkbox.setToolTip('Not available with density map') - else: - if ((x_datetime and checkbox in (self.ui.bool_xerr_visible, self.ui.bool_vector_visible)) or - (y_datetime and checkbox in (self.ui.bool_yerr_visible, self.ui.bool_vector_visible))): - checkbox.setEnabled(False) - checkbox.setToolTip('Not available when using datetime attribute') - else: - checkbox.setEnabled(True) - checkbox.setToolTip('') - - def _update_line_visible(self, *args): - self.ui.value_linewidth.setEnabled(self.layer_state.line_visible) - self.ui.combosel_linestyle.setEnabled(self.layer_state.line_visible) - - def _update_xerr_visible(self, *args): - self.ui.combosel_xerr_att.setEnabled(self.layer_state.xerr_visible) - - def _update_yerr_visible(self, *args): - self.ui.combosel_yerr_att.setEnabled(self.layer_state.yerr_visible) - - def _update_vectors_visible(self, *args): - viewer_state = self.layer_state.viewer_state - is_rect = not hasattr(viewer_state, 'plot_mode') or viewer_state.plot_mode == 'rectilinear' - self.ui.combosel_vector_mode.setEnabled(self.layer_state.vector_visible and is_rect) - self.ui.combosel_vx_att.setEnabled(self.layer_state.vector_visible) - self.ui.combosel_vy_att.setEnabled(self.layer_state.vector_visible) - self.ui.value_vector_scaling.setEnabled(self.layer_state.vector_visible) - self.ui.combosel_vector_origin.setEnabled(self.layer_state.vector_visible) - self.ui.bool_vector_arrowhead.setEnabled(self.layer_state.vector_visible) - - def _update_vector_mode(self, vector_mode=None): - if self.layer_state.vector_mode == 'Cartesian': - self.ui.label_vector_x.setText('vx') - self.ui.label_vector_y.setText('vy') - elif self.layer_state.vector_mode == 'Polar': - self.ui.label_vector_x.setText('angle (deg)') - self.ui.label_vector_y.setText('length') - - def _update_cmap_mode(self, cmap_mode=None): - - if self.layer_state.cmap_mode == 'Fixed': - self.ui.label_cmap_attribute.hide() - self.ui.combosel_cmap_att.hide() - self.ui.label_cmap_limits.hide() - self.ui.valuetext_cmap_vmin.hide() - self.ui.valuetext_cmap_vmax.hide() - self.ui.button_flip_cmap.hide() - self.ui.combodata_cmap.hide() - self.ui.label_colormap.hide() - self.ui.color_color.show() - else: - self.ui.label_cmap_attribute.show() - self.ui.combosel_cmap_att.show() - self.ui.label_cmap_limits.show() - self.ui.valuetext_cmap_vmin.show() - self.ui.valuetext_cmap_vmax.show() - self.ui.button_flip_cmap.show() - self.ui.combodata_cmap.show() - self.ui.label_colormap.show() - self.ui.color_color.hide() +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.viewers.scatter.qt.layer_style_editor is deprecated, use glue_qt.viewers.scatter.layer_style_editor instead', GlueDeprecationWarning) +from glue_qt.viewers.scatter.layer_style_editor import * # noqa diff --git a/glue/viewers/scatter/qt/layer_style_editor.ui b/glue/viewers/scatter/qt/layer_style_editor.ui deleted file mode 100644 index cad731176..000000000 --- a/glue/viewers/scatter/qt/layer_style_editor.ui +++ /dev/null @@ -1,1054 +0,0 @@ - - - Form - - - - 0 - 0 - 330 - 296 - - - - Form - - - - 5 - - - 5 - - - 5 - - - 5 - - - - - 0 - - - Qt::ElideNone - - - - Color - - - - 10 - - - 3 - - - 10 - - - 3 - - - 10 - - - 5 - - - - - 100 - - - Qt::Horizontal - - - - - - - - 75 - true - - - - opacity - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - Qt::Horizontal - - - - 40 - 1 - - - - - - - - - 0 - 0 - - - - - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - QComboBox::AdjustToMinimumContentsLengthWithIcon - - - - - - - - 80 - 16777215 - - - - - - - - padding: 0px - - - ⇄ - - - - - - - - 75 - true - - - - color - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - 75 - true - - - - limits - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - 75 - true - - - - colormap - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - 75 - true - - - - attribute - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - 0 - 0 - - - - QComboBox::AdjustToMinimumContentsLengthWithIcon - - - - - - - - Points - - - - 10 - - - 3 - - - 10 - - - 3 - - - 10 - - - 5 - - - - - - 120 - 16777215 - - - - QComboBox::AdjustToMinimumContentsLengthWithIcon - - - - - - - - 75 - true - - - - show - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - 75 - true - - - - Qt::LeftToRight - - - limits - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - padding: 0px - - - ⇄ - - - - - - - - 75 - true - - - - dpi - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - - 75 - true - - - - Qt::LeftToRight - - - attribute - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - 75 - true - - - - Qt::LeftToRight - - - scaling - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - 75 - true - - - - Qt::LeftToRight - - - size - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Qt::Horizontal - - - - 40 - 1 - - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - 100 - - - 50 - - - Qt::Horizontal - - - - - - - - 0 - 0 - - - - - 80 - 16777215 - - - - - - - - - 75 - true - - - - type - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - QComboBox::AdjustToMinimumContentsLengthWithIcon - - - - - - - - - - - - - - - 75 - true - - - - stretch - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Qt::Horizontal - - - - - - - - 16777215 - 16777215 - - - - QComboBox::AdjustToMinimumContentsLengthWithIcon - - - - - - - - 75 - true - - - - contrast - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Qt::Horizontal - - - - - - - - 75 - true - - - - fill - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - - - - - - Line - - - - 10 - - - 3 - - - 10 - - - 3 - - - 10 - - - 5 - - - - - Qt::Horizontal - - - - 40 - 1 - - - - - - - - 1 - - - - - - - - 75 - true - - - - width - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - 75 - true - - - - style - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - 75 - true - - - - show - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - - - - - - Errors - - - - 10 - - - 3 - - - 10 - - - 3 - - - 10 - - - 5 - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - Qt::Horizontal - - - - 40 - 1 - - - - - - - - QComboBox::AdjustToMinimumContentsLengthWithIcon - - - - - - - QComboBox::AdjustToMinimumContentsLengthWithIcon - - - - - - - - - - - - - - - - - - - - - - 75 - true - - - - show y - - - - - - - - 75 - true - - - - show x - - - - - - - color: #BB2200 - - - Warning: adding errorbars may be slow - - - Qt::AlignCenter - - - - - - - - Vectors - - - - 10 - - - 3 - - - 10 - - - 3 - - - 10 - - - 5 - - - - - Show arrow - - - - - - - QComboBox::AdjustToMinimumContentsLengthWithIcon - - - - - - - - 75 - true - - - - show - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - QComboBox::AdjustToMinimumContentsLengthWithIcon - - - - - - - - 75 - true - - - - vx - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - 0 - 0 - - - - - 16777215 - 16777215 - - - - - - - - - 75 - true - - - - arrow - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - - 75 - true - - - - type - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - - - - - - 75 - true - - - - vy - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - 75 - true - - - - origin - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Qt::Horizontal - - - - 40 - 1 - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - Qt::Horizontal - - - - - - - - 75 - true - - - - scaling - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - color: #BB2200 - - - Warning: adding vectors may be slow - - - Qt::AlignCenter - - - false - - - - - - - - - - - - QColorBox - QLabel -
glue.utils.qt.colors
-
- - QColormapCombo - QComboBox -
glue.utils.qt.colors
-
-
- - -
diff --git a/glue/viewers/scatter/qt/options_widget.py b/glue/viewers/scatter/qt/options_widget.py index 8d26dbdd2..fe1e5fc51 100644 --- a/glue/viewers/scatter/qt/options_widget.py +++ b/glue/viewers/scatter/qt/options_widget.py @@ -1,118 +1,4 @@ -import os - -from qtpy import QtWidgets - -from echo.qt import autoconnect_callbacks_to_qt -from glue.utils.qt import load_ui, fix_tab_widget_fontsize -from glue.viewers.matplotlib.state import MatplotlibDataViewerState - -__all__ = ['ScatterOptionsWidget'] - - -def _get_labels(proj): - if proj == 'rectilinear': - return 'x axis', 'y axis' - elif proj == 'polar': - return 'theta', 'radius' - elif proj in ['aitoff', 'hammer', 'lambert', 'mollweide']: - return 'long', 'lat' - else: - return 'axis 1', 'axis 2' - - -class ScatterOptionsWidget(QtWidgets.QWidget): - - def __init__(self, viewer_state, session, parent=None): - - super(ScatterOptionsWidget, self).__init__(parent=parent) - - self.ui = load_ui('options_widget.ui', self, - directory=os.path.dirname(__file__)) - - fix_tab_widget_fontsize(self.ui.tab_widget) - - self._connections = autoconnect_callbacks_to_qt(viewer_state, self.ui) - self._connections_axes = autoconnect_callbacks_to_qt(viewer_state, self.ui.axes_editor.ui) - connect_kwargs = {'alpha': dict(value_range=(0, 1))} - self._connections_legend = autoconnect_callbacks_to_qt(viewer_state.legend, self.ui.legend_editor.ui, connect_kwargs) - - self.viewer_state = viewer_state - - viewer_state.add_callback('x_att', self._update_x_attribute) - viewer_state.add_callback('y_att', self._update_y_attribute) - viewer_state.add_callback('plot_mode', self._update_plot_mode) - viewer_state.add_callback('angle_unit', self._update_x_attribute) - - self.session = session - self.ui.axes_editor.button_apply_all.clicked.connect(self._apply_all_viewers) - - # Without this, log buttons will be enabled when the application starts - # regardless of the current plot mode - self._update_x_attribute() - self._update_y_attribute() - - # Make sure that the UI is consistent with the plot mode that's selected - self._update_plot_mode() - - def _apply_all_viewers(self): - for tab in self.session.application.viewers: - for viewer in tab: - if isinstance(viewer.state, MatplotlibDataViewerState): - viewer.state.update_axes_settings_from(self.viewer_state) - - def _update_x_attribute(self, *args): - # If at least one of the components is categorical or a date, disable log button - log_enabled = ('categorical' not in self.viewer_state.x_kinds and self.viewer_state.plot_mode not in ['aitoff', 'hammer', 'lambert', 'mollweide', 'polar']) - self.ui.bool_x_log.setEnabled(log_enabled) - self.ui.bool_x_log_.setEnabled(log_enabled) - if not log_enabled: - self.ui.bool_x_log.setChecked(False) - self.ui.bool_x_log_.setChecked(False) - - def _update_y_attribute(self, *args): - # If at least one of the components is categorical or a date, disable log button - log_enabled = ('categorical' not in self.viewer_state.y_kinds and self.viewer_state.plot_mode not in ['aitoff', 'hammer', 'lambert', 'mollweide', 'polar']) - self.ui.bool_y_log.setEnabled(log_enabled) - self.ui.bool_y_log_.setEnabled(log_enabled) - if not log_enabled: - self.ui.bool_y_log.setChecked(False) - self.ui.bool_y_log_.setChecked(False) - - def _update_plot_mode(self, *args): - x_label, y_label = _get_labels(self.viewer_state.plot_mode) - self.ui.x_lab.setText(x_label) - self.ui.x_lab_2.setText(x_label) - self.ui.y_lab.setText(y_label) - self.ui.y_lab_2.setText(y_label) - lim_enabled = self.viewer_state.plot_mode not in ['aitoff', 'hammer', 'lambert', 'mollweide'] - is_polar = self.viewer_state.using_polar - is_rect = self.viewer_state.using_rectilinear - self.ui.valuetext_x_min.setEnabled(lim_enabled) - self.ui.valuetext_x_max.setEnabled(lim_enabled) - self.ui.valuetext_y_min.setEnabled(lim_enabled) - self.ui.valuetext_y_max.setEnabled(lim_enabled) - self.ui.button_full_circle.setVisible(False) - self.ui.angle_unit_lab.setVisible(not is_rect) - self.ui.combosel_angle_unit.setVisible(not is_rect) - self.ui.x_lab_2.setVisible(not is_polar) - self.ui.valuetext_x_min.setVisible(not is_polar) - self.ui.button_flip_x.setVisible(not is_polar) - self.ui.valuetext_x_max.setVisible(not is_polar) - self.ui.bool_x_log.setVisible(not is_polar) - - # In polar mode, the axis labels are shown in ticks rather than using the plot axis - # so we adjust the axes editor to account for this - axes_ui = self.ui.axes_editor.ui - axes_ui.label_3.setVisible(not is_polar) - axes_ui.label_4.setVisible(not is_polar) - axes_ui.value_x_axislabel_size.setVisible(not is_polar) - axes_ui.value_y_axislabel_size.setVisible(not is_polar) - axes_ui.combosel_x_axislabel_weight.setVisible(not is_polar) - axes_ui.combosel_y_axislabel_weight.setVisible(not is_polar) - axes_ui.label_2.setText("Θlabel" if is_polar else "xlabel") - axes_ui.label_6.setText("rlabel" if is_polar else "ylabel") - axes_ui.label.setText("Θ" if is_polar else "x") - axes_ui.label_10.setText("r" if is_polar else "y") - - self._update_x_attribute() - self._update_y_attribute() +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.viewers.scatter.qt.options_widget is deprecated, use glue_qt.viewers.scatter.options_widget instead', GlueDeprecationWarning) +from glue_qt.viewers.scatter.options_widget import * # noqa diff --git a/glue/viewers/scatter/qt/options_widget.ui b/glue/viewers/scatter/qt/options_widget.ui deleted file mode 100644 index 245123fcf..000000000 --- a/glue/viewers/scatter/qt/options_widget.ui +++ /dev/null @@ -1,397 +0,0 @@ - - - Widget - - - - 0 - 0 - 389 - 221 - - - - 2D Scatter - - - - 5 - - - 5 - - - 5 - - - 5 - - - 5 - - - - - QTabWidget::North - - - 0 - - - false - - - false - - - false - - - false - - - - General - - - - 10 - - - 10 - - - 10 - - - 10 - - - 10 - - - 5 - - - - - QComboBox::AdjustToMinimumContentsLengthWithIcon - - - - - - - - 75 - true - - - - type - - - - - - - - - - Qt::Horizontal - - - - 40 - 5 - - - - - - - - log - - - true - - - - - - - QComboBox::AdjustToMinimumContentsLengthWithIcon - - - - - - - - 75 - true - - - - y axis - - - - - - - log - - - true - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - 75 - true - - - - x axis - - - - - - - - - - <html><head/><body><p><span style=" font-weight:600;">unit</span></p></body></html> - - - - - - - - Limits - - - - 10 - - - 10 - - - 10 - - - 10 - - - 10 - - - 5 - - - - - - 75 - true - - - - x axis - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - log - - - true - - - - - - - - - - - 75 - true - - - - y axis - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - padding: 0px - - - ⇄ - - - - - - - padding: 0px - - - ⇄ - - - - - - - log - - - true - - - - - - - - - - - - - Qt::Horizontal - - - - 40 - 5 - - - - - - - - true - - - full circle - - - - - - - - Axes - - - - 5 - - - 5 - - - 5 - - - 5 - - - - - - - - - Legend - - - - 5 - - - 5 - - - 5 - - - 5 - - - - - - - - - - - - - - AxesEditorWidget - QWidget -
glue.viewers.matplotlib.qt.axes_editor
-
- - LegendEditorWidget - QWidget -
glue.viewers.matplotlib.qt.legend_editor
-
-
- - -
diff --git a/glue/viewers/scatter/qt/tests/__init__.py b/glue/viewers/scatter/qt/tests/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/glue/viewers/scatter/qt/tests/data/scatter_and_line_v1.glu b/glue/viewers/scatter/qt/tests/data/scatter_and_line_v1.glu deleted file mode 100644 index dcf452518..000000000 --- a/glue/viewers/scatter/qt/tests/data/scatter_and_line_v1.glu +++ /dev/null @@ -1,322 +0,0 @@ -{ - "CallbackList": { - "_type": "glue.external.echo.list.CallbackList", - "values": [ - "ScatterLayerState" - ] - }, - "CallbackList_0": { - "_type": "glue.external.echo.list.CallbackList", - "values": [ - "ScatterLayerState_0" - ] - }, - "Component": { - "_type": "glue.core.component.Component", - "data": { - "_type": "numpy.ndarray", - "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGk4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDMsKSwgfSAgICAgICAgICAgIAoBAAAAAAAAAAIAAAAAAAAAAwAAAAAAAAA=" - }, - "units": "" - }, - "Component_0": { - "_type": "glue.core.component.Component", - "data": { - "_type": "numpy.ndarray", - "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGk4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDMsKSwgfSAgICAgICAgICAgIAoCAAAAAAAAAAMAAAAAAAAABAAAAAAAAAA=" - }, - "units": "" - }, - "CoordinateComponent": { - "_type": "glue.core.component.CoordinateComponent", - "axis": 0, - "world": false - }, - "CoordinateComponentLink": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "World 0" - ], - "index": 0, - "pix2world": false, - "to": [ - "Pixel Axis 0 [x]" - ] - }, - "CoordinateComponentLink_0": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "Pixel Axis 0 [x]" - ], - "index": 0, - "pix2world": true, - "to": [ - "World 0" - ] - }, - "CoordinateComponent_0": { - "_type": "glue.core.component.CoordinateComponent", - "axis": 0, - "world": true - }, - "Coordinates": { - "_type": "glue.core.coordinates.Coordinates" - }, - "DataCollection": { - "_protocol": 3, - "_type": "glue.core.data_collection.DataCollection", - "cids": [ - "a", - "Pixel Axis 0 [x]", - "World 0", - "b" - ], - "components": [ - "Component", - "CoordinateComponent", - "CoordinateComponent_0", - "Component_0" - ], - "data": [ - "table" - ], - "groups": [], - "links": [ - "CoordinateComponentLink", - "CoordinateComponentLink_0" - ], - "subset_group_count": 0 - }, - "LinearSegmentedColormap": { - "_type": "matplotlib.colors.LinearSegmentedColormap", - "cmap": "gray" - }, - "Pixel Axis 0 [x]": { - "_type": "glue.core.component_id.PixelComponentID", - "axis": 0, - "hidden": true, - "label": "Pixel Axis 0 [x]" - }, - "ScatterLayerState": { - "_type": "glue.viewers.scatter.state.ScatterLayerState", - "values": { - "alpha": 0.7777777777777778, - "cmap": "LinearSegmentedColormap", - "cmap_att": "a", - "cmap_mode": "st__Fixed", - "cmap_vmax": 3.0, - "cmap_vmin": 1.0, - "color": "st__#404040", - "layer": "table", - "linestyle": "st__solid", - "linewidth": 1, - "size": 3, - "size_att": "a", - "size_mode": "st__Fixed", - "size_scaling": 1, - "size_vmax": 3.0, - "size_vmin": 1.0, - "style": "st__Scatter", - "visible": true, - "xerr_att": "a", - "xerr_visible": false, - "yerr_att": "a", - "yerr_visible": false, - "zorder": 1 - } - }, - "ScatterLayerState_0": { - "_type": "glue.viewers.scatter.state.ScatterLayerState", - "values": { - "alpha": 0.7777777777777778, - "cmap": "LinearSegmentedColormap", - "cmap_att": "a", - "cmap_mode": "st__Fixed", - "cmap_vmax": 3.0, - "cmap_vmin": 1.0, - "color": "st__#404040", - "layer": "table", - "linestyle": "st__solid", - "linewidth": 1, - "size": 3, - "size_att": "a", - "size_mode": "st__Fixed", - "size_scaling": 1, - "size_vmax": 3.0, - "size_vmin": 1.0, - "style": "st__Line", - "visible": true, - "xerr_att": "a", - "xerr_visible": false, - "yerr_att": "a", - "yerr_visible": false, - "zorder": 1 - } - }, - "ScatterViewer": { - "_protocol": 1, - "_type": "glue.viewers.scatter.qt.data_viewer.ScatterViewer", - "layers": [ - { - "_type": "glue.viewers.scatter.layer_artist.ScatterLayerArtist", - "state": "ScatterLayerState" - } - ], - "pos": [ - 0, - 0 - ], - "session": "Session", - "size": [ - 605, - 444 - ], - "state": { - "values": { - "aspect": "st__auto", - "layers": "CallbackList", - "x_att": "a", - "x_log": false, - "x_max": 3.0, - "x_min": 1.0, - "y_att": "b", - "y_log": false, - "y_max": 4.0, - "y_min": 2.0 - } - } - }, - "ScatterViewer_0": { - "_protocol": 1, - "_type": "glue.viewers.scatter.qt.data_viewer.ScatterViewer", - "layers": [ - { - "_type": "glue.viewers.scatter.layer_artist.ScatterLayerArtist", - "state": "ScatterLayerState_0" - } - ], - "pos": [ - 605, - 0 - ], - "session": "Session", - "size": [ - 600, - 400 - ], - "state": { - "values": { - "aspect": "st__auto", - "layers": "CallbackList_0", - "x_att": "a", - "x_log": false, - "x_max": 3.0, - "x_min": 1.0, - "y_att": "b", - "y_log": false, - "y_max": 4.0, - "y_min": 2.0 - } - } - }, - "Session": { - "_type": "glue.core.session.Session" - }, - "World 0": { - "_type": "glue.core.component_id.ComponentID", - "hidden": true, - "label": "World 0" - }, - "__main__": { - "_type": "glue.app.qt.application.GlueApplication", - "data": "DataCollection", - "plugins": [ - "glue.core.data_exporters", - "glue.viewers.histogram", - "glue.io.formats.fits", - "glue_exp.tools.zoom_buttons", - "glue.viewers.scatter", - "glue.plugins.export_d3po", - "glue.plugins.exporters.plotly", - "glue_samp", - "glue_exp.importers.webcam", - "glue.viewers.table", - "glue_aladin", - "glue.plugins.tools.spectrum_tool", - "glue_vispy_viewers.volume", - "glue.plugins.dendro_viewer", - "glue.viewers.image", - "glue_exp.tools.contour_selection", - "glue_wwt", - "glue.plugins.tools.pv_slicer", - "glue_exp.tools.floodfill_selection", - "glue_exp.importers.vizier", - "glue_vispy_viewers.scatter", - "glue.plugins.coordinate_helpers" - ], - "session": "Session", - "tab_names": [ - "Tab 1" - ], - "viewers": [ - [ - "ScatterViewer", - "ScatterViewer_0" - ] - ] - }, - "a": { - "_type": "glue.core.component_id.ComponentID", - "hidden": false, - "label": "a" - }, - "b": { - "_type": "glue.core.component_id.ComponentID", - "hidden": false, - "label": "b" - }, - "table": { - "_key_joins": [], - "_protocol": 5, - "_type": "glue.core.data.Data", - "components": [ - [ - "a", - "Component" - ], - [ - "Pixel Axis 0 [x]", - "CoordinateComponent" - ], - [ - "World 0", - "CoordinateComponent_0" - ], - [ - "b", - "Component_0" - ] - ], - "coords": "Coordinates", - "label": "table", - "primary_owner": [ - "a", - "Pixel Axis 0 [x]", - "World 0", - "b" - ], - "style": { - "_type": "glue.core.visual.VisualAttributes", - "alpha": 0.7777777777777778, - "color": "#404040", - "linestyle": "solid", - "linewidth": 1, - "marker": "o", - "markersize": 3 - }, - "subsets": [], - "uuid": "3755b390-c67e-44a0-b5b5-ebfc709c859e" - } -} \ No newline at end of file diff --git a/glue/viewers/scatter/qt/tests/data/scatter_v0.glu b/glue/viewers/scatter/qt/tests/data/scatter_v0.glu deleted file mode 100644 index 74e520951..000000000 --- a/glue/viewers/scatter/qt/tests/data/scatter_v0.glu +++ /dev/null @@ -1,449 +0,0 @@ -{ - "Component": { - "_type": "glue.core.component.Component", - "data": { - "_type": "numpy.ndarray", - "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGY4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDMsKSwgfSAgICAgICAgICAgIAoAAAAAAADwP5qZmZmZmck/AAAAAAAA8L8=" - }, - "units": "" - }, - "Component_0": { - "_type": "glue.core.component.Component", - "data": { - "_type": "numpy.ndarray", - "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGk4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDMsKSwgfSAgICAgICAgICAgIAoDAAAAAAAAAAIAAAAAAAAAAgAAAAAAAAA=" - }, - "units": "" - }, - "Component_1": { - "_type": "glue.core.component.Component", - "data": { - "_type": "numpy.ndarray", - "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGY4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDMsKSwgfSAgICAgICAgICAgIAoAAAAAAAAQQJqZmZmZmdk/AAAAAAAAFEA=" - }, - "units": "" - }, - "CoordinateComponent": { - "_type": "glue.core.component.CoordinateComponent", - "axis": 0, - "world": false - }, - "CoordinateComponentLink": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "World 0" - ], - "index": 0, - "pix2world": false, - "to": [ - "Pixel Axis 0 [x]" - ] - }, - "CoordinateComponentLink_0": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "Pixel Axis 0 [x]" - ], - "index": 0, - "pix2world": true, - "to": [ - "World 0" - ] - }, - "CoordinateComponent_0": { - "_type": "glue.core.component.CoordinateComponent", - "axis": 0, - "world": true - }, - "Coordinates": { - "_type": "glue.core.coordinates.Coordinates" - }, - "DataCollection": { - "_protocol": 3, - "_type": "glue.core.data_collection.DataCollection", - "cids": [ - "a", - "Pixel Axis 0 [x]", - "World 0", - "b", - "c" - ], - "components": [ - "Component", - "CoordinateComponent", - "CoordinateComponent_0", - "Component_0", - "Component_1" - ], - "data": [ - "basic" - ], - "groups": [ - "Subset 1_0", - "Subset 2_0" - ], - "links": [ - "CoordinateComponentLink", - "CoordinateComponentLink_0" - ], - "subset_group_count": 2 - }, - "Pixel Axis 0 [x]": { - "_type": "glue.core.component_id.PixelComponentID", - "axis": 0, - "hidden": true, - "label": "Pixel Axis 0 [x]" - }, - "PolygonalROI": { - "_type": "glue.core.roi.PolygonalROI", - "vx": [ - 0.010048309178743997, - 0.22106280193236705, - 0.22106280193236705, - 0.010048309178743997, - 0.010048309178743997 - ], - "vy": [ - 1.9849289099526066, - 1.9849289099526066, - 2.167298578199052, - 2.167298578199052, - 1.9849289099526066 - ] - }, - "PolygonalROI_0": { - "_type": "glue.core.roi.PolygonalROI", - "vx": [ - -1.04, - 0.4521739130434783, - 0.4521739130434783, - -1.04, - -1.04 - ], - "vy": [ - 1.9997156398104265, - 1.9997156398104265, - 2.2806635071090047, - 2.2806635071090047, - 1.9997156398104265 - ] - }, - "RoiSubsetState": { - "_type": "glue.core.subset.RoiSubsetState", - "roi": "PolygonalROI", - "xatt": "a", - "yatt": "b" - }, - "RoiSubsetState_0": { - "_type": "glue.core.subset.RoiSubsetState", - "roi": "PolygonalROI_0", - "xatt": "a", - "yatt": "b" - }, - "ScatterWidget": { - "_type": "glue.viewers.scatter.qt.viewer_widget.ScatterWidget", - "layers": [ - { - "_type": "glue.viewers.scatter.layer_artist.ScatterLayerArtist", - "layer": "basic", - "visible": true, - "xatt": "a", - "yatt": "b", - "zorder": 1 - }, - { - "_type": "glue.viewers.scatter.layer_artist.ScatterLayerArtist", - "layer": "Subset 1", - "visible": true, - "xatt": "a", - "yatt": "b", - "zorder": 2 - }, - { - "_type": "glue.viewers.scatter.layer_artist.ScatterLayerArtist", - "layer": "Subset 2", - "visible": true, - "xatt": "a", - "yatt": "b", - "zorder": 3 - } - ], - "pos": [ - 0, - 0 - ], - "properties": { - "hidden": false, - "xatt": "a", - "xflip": false, - "xlog": false, - "xmax": 1.04, - "xmin": -1.04, - "yatt": "b", - "yflip": false, - "ylog": false, - "ymax": 3.02, - "ymin": 1.98 - }, - "session": "Session", - "size": [ - 600, - 400 - ] - }, - "ScatterWidget_0": { - "_type": "glue.viewers.scatter.qt.viewer_widget.ScatterWidget", - "layers": [ - { - "_type": "glue.viewers.scatter.layer_artist.ScatterLayerArtist", - "layer": "basic", - "visible": true, - "xatt": "a", - "yatt": "c", - "zorder": 1 - }, - { - "_type": "glue.viewers.scatter.layer_artist.ScatterLayerArtist", - "layer": "Subset 1", - "visible": false, - "xatt": "a", - "yatt": "c", - "zorder": 2 - }, - { - "_type": "glue.viewers.scatter.layer_artist.ScatterLayerArtist", - "layer": "Subset 2", - "visible": true, - "xatt": "a", - "yatt": "c", - "zorder": 3 - } - ], - "pos": [ - 600, - 0 - ], - "properties": { - "hidden": false, - "xatt": "a", - "xflip": false, - "xlog": true, - "xmax": 1.05, - "xmin": 9.5e-06, - "yatt": "c", - "yflip": false, - "ylog": true, - "ymax": 5.25, - "ymin": 0.38 - }, - "session": "Session", - "size": [ - 600, - 400 - ] - }, - "ScatterWidget_1": { - "_type": "glue.viewers.scatter.qt.viewer_widget.ScatterWidget", - "layers": [ - { - "_type": "glue.viewers.scatter.layer_artist.ScatterLayerArtist", - "layer": "basic", - "visible": true, - "xatt": "b", - "yatt": "a", - "zorder": 1 - }, - { - "_type": "glue.viewers.scatter.layer_artist.ScatterLayerArtist", - "layer": "Subset 1", - "visible": true, - "xatt": "b", - "yatt": "a", - "zorder": 2 - }, - { - "_type": "glue.viewers.scatter.layer_artist.ScatterLayerArtist", - "layer": "Subset 2", - "visible": false, - "xatt": "b", - "yatt": "a", - "zorder": 3 - } - ], - "pos": [ - 0, - 400 - ], - "properties": { - "hidden": false, - "xatt": "b", - "xflip": false, - "xlog": false, - "xmax": 5.0, - "xmin": 0.0, - "yatt": "a", - "yflip": true, - "ylog": false, - "ymax": 5.0, - "ymin": -5.0 - }, - "session": "Session", - "size": [ - 600, - 400 - ] - }, - "Session": { - "_type": "glue.core.session.Session" - }, - "Subset 1": { - "_type": "glue.core.subset_group.GroupedSubset", - "group": "Subset 1_0", - "style": { - "_type": "glue.core.visual.VisualAttributes", - "alpha": 0.5, - "color": "#e31a1c", - "linestyle": "solid", - "linewidth": 1, - "marker": "o", - "markersize": 7 - } - }, - "Subset 1_0": { - "_type": "glue.core.subset_group.SubsetGroup", - "label": "Subset 1", - "state": "RoiSubsetState", - "style": { - "_type": "glue.core.visual.VisualAttributes", - "alpha": 0.5, - "color": "#e31a1c", - "linestyle": "solid", - "linewidth": 1, - "marker": "o", - "markersize": 7 - }, - "subsets": [ - "Subset 1" - ] - }, - "Subset 2": { - "_type": "glue.core.subset_group.GroupedSubset", - "group": "Subset 2_0", - "style": { - "_type": "glue.core.visual.VisualAttributes", - "alpha": 0.5, - "color": "#33a02c", - "linestyle": "solid", - "linewidth": 1, - "marker": "o", - "markersize": 7 - } - }, - "Subset 2_0": { - "_type": "glue.core.subset_group.SubsetGroup", - "label": "Subset 2", - "state": "RoiSubsetState_0", - "style": { - "_type": "glue.core.visual.VisualAttributes", - "alpha": 0.5, - "color": "#33a02c", - "linestyle": "solid", - "linewidth": 1, - "marker": "o", - "markersize": 7 - }, - "subsets": [ - "Subset 2" - ] - }, - "World 0": { - "_type": "glue.core.component_id.ComponentID", - "hidden": true, - "label": "World 0" - }, - "__main__": { - "_type": "glue.app.qt.application.GlueApplication", - "data": "DataCollection", - "plugins": [ - "glue.viewers.scatter" - ], - "session": "Session", - "tab_names": [ - "Tab 1" - ], - "viewers": [ - [ - "ScatterWidget", - "ScatterWidget_0", - "ScatterWidget_1" - ] - ] - }, - "a": { - "_type": "glue.core.component_id.ComponentID", - "hidden": false, - "label": "a" - }, - "b": { - "_type": "glue.core.component_id.ComponentID", - "hidden": false, - "label": "b" - }, - "basic": { - "_key_joins": [], - "_protocol": 5, - "_type": "glue.core.data.Data", - "components": [ - [ - "a", - "Component" - ], - [ - "Pixel Axis 0 [x]", - "CoordinateComponent" - ], - [ - "World 0", - "CoordinateComponent_0" - ], - [ - "b", - "Component_0" - ], - [ - "c", - "Component_1" - ] - ], - "coords": "Coordinates", - "label": "basic", - "primary_owner": [ - "a", - "Pixel Axis 0 [x]", - "World 0", - "b", - "c" - ], - "style": { - "_type": "glue.core.visual.VisualAttributes", - "alpha": 0.8, - "color": "0.35", - "linestyle": "solid", - "linewidth": 1, - "marker": "o", - "markersize": 3 - }, - "subsets": [ - "Subset 1", - "Subset 2" - ], - "uuid": "dde080dc-a65a-4988-bed9-c0cb90ff91a8" - }, - "c": { - "_type": "glue.core.component_id.ComponentID", - "hidden": false, - "label": "c" - } -} \ No newline at end of file diff --git a/glue/viewers/scatter/qt/tests/data/scatter_v1.glu b/glue/viewers/scatter/qt/tests/data/scatter_v1.glu deleted file mode 100644 index c1f224894..000000000 --- a/glue/viewers/scatter/qt/tests/data/scatter_v1.glu +++ /dev/null @@ -1,578 +0,0 @@ -{ - "CallbackList": { - "_type": "glue.external.echo.list.CallbackList", - "values": [ - "ScatterLayerState", - "ScatterLayerState_0", - "ScatterLayerState_1" - ] - }, - "CallbackList_0": { - "_type": "glue.external.echo.list.CallbackList", - "values": [ - "ScatterLayerState_2", - "ScatterLayerState_3", - "ScatterLayerState_4" - ] - }, - "CallbackList_1": { - "_type": "glue.external.echo.list.CallbackList", - "values": [ - "ScatterLayerState_5", - "ScatterLayerState_6", - "ScatterLayerState_7" - ] - }, - "Component": { - "_type": "glue.core.component.Component", - "data": { - "_type": "numpy.ndarray", - "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGY4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDMsKSwgfSAgICAgICAgICAgIAoAAAAAAADwP5qZmZmZmck/AAAAAAAA8L8=" - }, - "units": "" - }, - "Component_0": { - "_type": "glue.core.component.Component", - "data": { - "_type": "numpy.ndarray", - "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGk4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDMsKSwgfSAgICAgICAgICAgIAoDAAAAAAAAAAIAAAAAAAAAAgAAAAAAAAA=" - }, - "units": "" - }, - "Component_1": { - "_type": "glue.core.component.Component", - "data": { - "_type": "numpy.ndarray", - "data": "k05VTVBZAQBGAHsnZGVzY3InOiAnPGY4JywgJ2ZvcnRyYW5fb3JkZXInOiBGYWxzZSwgJ3NoYXBlJzogKDMsKSwgfSAgICAgICAgICAgIAoAAAAAAAAQQJqZmZmZmdk/AAAAAAAAFEA=" - }, - "units": "" - }, - "CoordinateComponent": { - "_type": "glue.core.component.CoordinateComponent", - "axis": 0, - "world": false - }, - "CoordinateComponentLink": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "World 0" - ], - "index": 0, - "pix2world": false, - "to": [ - "Pixel Axis 0 [x]" - ] - }, - "CoordinateComponentLink_0": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "World 0" - ], - "index": 0, - "pix2world": false, - "to": [ - "Pixel Axis 0 [x]" - ] - }, - "CoordinateComponentLink_1": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "Pixel Axis 0 [x]" - ], - "index": 0, - "pix2world": true, - "to": [ - "World 0" - ] - }, - "CoordinateComponentLink_2": { - "_type": "glue.core.component_link.CoordinateComponentLink", - "coords": "Coordinates", - "frm": [ - "Pixel Axis 0 [x]" - ], - "index": 0, - "pix2world": true, - "to": [ - "World 0" - ] - }, - "CoordinateComponent_0": { - "_type": "glue.core.component.CoordinateComponent", - "axis": 0, - "world": true - }, - "Coordinates": { - "_type": "glue.core.coordinates.Coordinates" - }, - "DataCollection": { - "_protocol": 3, - "_type": "glue.core.data_collection.DataCollection", - "cids": [ - "a", - "Pixel Axis 0 [x]", - "World 0", - "b", - "c" - ], - "components": [ - "Component", - "CoordinateComponent", - "CoordinateComponent_0", - "Component_0", - "Component_1" - ], - "data": [ - "basic" - ], - "groups": [ - "Subset 1", - "Subset 2" - ], - "links": [ - "CoordinateComponentLink", - "CoordinateComponentLink_0", - "CoordinateComponentLink_1", - "CoordinateComponentLink_2" - ], - "subset_group_count": 2 - }, - "Pixel Axis 0 [x]": { - "_type": "glue.core.component_id.PixelComponentID", - "axis": 0, - "hidden": true, - "label": "Pixel Axis 0 [x]" - }, - "PolygonalROI": { - "_type": "glue.core.roi.PolygonalROI", - "vx": [ - 0.010048309178743997, - 0.22106280193236705, - 0.22106280193236705, - 0.010048309178743997, - 0.010048309178743997 - ], - "vy": [ - 1.9849289099526066, - 1.9849289099526066, - 2.167298578199052, - 2.167298578199052, - 1.9849289099526066 - ] - }, - "PolygonalROI_0": { - "_type": "glue.core.roi.PolygonalROI", - "vx": [ - -1.04, - 0.4521739130434783, - 0.4521739130434783, - -1.04, - -1.04 - ], - "vy": [ - 1.9997156398104265, - 1.9997156398104265, - 2.2806635071090047, - 2.2806635071090047, - 1.9997156398104265 - ] - }, - "RoiSubsetState": { - "_type": "glue.core.subset.RoiSubsetState", - "roi": "PolygonalROI", - "xatt": "a", - "yatt": "b" - }, - "RoiSubsetState_0": { - "_type": "glue.core.subset.RoiSubsetState", - "roi": "PolygonalROI_0", - "xatt": "a", - "yatt": "b" - }, - "ScatterLayerState": { - "_type": "glue.viewers.scatter.state.ScatterLayerState", - "values": { - "alpha": 0.8, - "color": "st__0.35", - "layer": "basic", - "size": 3, - "visible": true, - "zorder": 2 - } - }, - "ScatterLayerState_0": { - "_type": "glue.viewers.scatter.state.ScatterLayerState", - "values": { - "alpha": 0.5, - "color": "st__#e31a1c", - "layer": "Subset 1_0", - "size": 7, - "visible": true, - "zorder": 3 - } - }, - "ScatterLayerState_1": { - "_type": "glue.viewers.scatter.state.ScatterLayerState", - "values": { - "alpha": 0.5, - "color": "st__#33a02c", - "layer": "Subset 2_0", - "size": 7, - "visible": true, - "zorder": 4 - } - }, - "ScatterLayerState_2": { - "_type": "glue.viewers.scatter.state.ScatterLayerState", - "values": { - "alpha": 0.8, - "color": "st__0.35", - "layer": "basic", - "size": 3, - "visible": true, - "zorder": 2 - } - }, - "ScatterLayerState_3": { - "_type": "glue.viewers.scatter.state.ScatterLayerState", - "values": { - "alpha": 0.5, - "color": "st__#e31a1c", - "layer": "Subset 1_0", - "size": 7, - "visible": false, - "zorder": 3 - } - }, - "ScatterLayerState_4": { - "_type": "glue.viewers.scatter.state.ScatterLayerState", - "values": { - "alpha": 0.5, - "color": "st__#33a02c", - "layer": "Subset 2_0", - "size": 7, - "visible": true, - "zorder": 4 - } - }, - "ScatterLayerState_5": { - "_type": "glue.viewers.scatter.state.ScatterLayerState", - "values": { - "alpha": 0.8, - "color": "st__0.35", - "layer": "basic", - "size": 3, - "visible": true, - "zorder": 2 - } - }, - "ScatterLayerState_6": { - "_type": "glue.viewers.scatter.state.ScatterLayerState", - "values": { - "alpha": 0.5, - "color": "st__#e31a1c", - "layer": "Subset 1_0", - "size": 7, - "visible": true, - "zorder": 3 - } - }, - "ScatterLayerState_7": { - "_type": "glue.viewers.scatter.state.ScatterLayerState", - "values": { - "alpha": 0.5, - "color": "st__#33a02c", - "layer": "Subset 2_0", - "size": 7, - "visible": false, - "zorder": 4 - } - }, - "ScatterViewer": { - "_protocol": 1, - "_type": "glue.viewers.scatter.qt.data_viewer.ScatterViewer", - "layers": [ - { - "_type": "glue.viewers.scatter.layer_artist.ScatterLayerArtist", - "state": "ScatterLayerState" - }, - { - "_type": "glue.viewers.scatter.layer_artist.ScatterLayerArtist", - "state": "ScatterLayerState_0" - }, - { - "_type": "glue.viewers.scatter.layer_artist.ScatterLayerArtist", - "state": "ScatterLayerState_1" - } - ], - "pos": [ - 0, - 0 - ], - "session": "Session", - "size": [ - 600, - 400 - ], - "state": { - "values": { - "layers": "CallbackList", - "x_att": "a", - "x_log": false, - "x_max": 1.04, - "x_min": -1.04, - "y_att": "b", - "y_log": false, - "y_max": 3.02, - "y_min": 1.98 - } - } - }, - "ScatterViewer_0": { - "_protocol": 1, - "_type": "glue.viewers.scatter.qt.data_viewer.ScatterViewer", - "layers": [ - { - "_type": "glue.viewers.scatter.layer_artist.ScatterLayerArtist", - "state": "ScatterLayerState_2" - }, - { - "_type": "glue.viewers.scatter.layer_artist.ScatterLayerArtist", - "state": "ScatterLayerState_3" - }, - { - "_type": "glue.viewers.scatter.layer_artist.ScatterLayerArtist", - "state": "ScatterLayerState_4" - } - ], - "pos": [ - 600, - 0 - ], - "session": "Session", - "size": [ - 600, - 400 - ], - "state": { - "values": { - "layers": "CallbackList_0", - "x_att": "a", - "x_log": true, - "x_max": 1.05, - "x_min": 9.5e-06, - "y_att": "c", - "y_log": true, - "y_max": 5.25, - "y_min": 0.38 - } - } - }, - "ScatterViewer_1": { - "_protocol": 1, - "_type": "glue.viewers.scatter.qt.data_viewer.ScatterViewer", - "layers": [ - { - "_type": "glue.viewers.scatter.layer_artist.ScatterLayerArtist", - "state": "ScatterLayerState_5" - }, - { - "_type": "glue.viewers.scatter.layer_artist.ScatterLayerArtist", - "state": "ScatterLayerState_6" - }, - { - "_type": "glue.viewers.scatter.layer_artist.ScatterLayerArtist", - "state": "ScatterLayerState_7" - } - ], - "pos": [ - 0, - 400 - ], - "session": "Session", - "size": [ - 600, - 400 - ], - "state": { - "values": { - "layers": "CallbackList_1", - "x_att": "b", - "x_log": false, - "x_max": 5.0, - "x_min": 0.0, - "y_att": "a", - "y_log": false, - "y_max": 5.0, - "y_min": -5.0 - } - } - }, - "Session": { - "_type": "glue.core.session.Session" - }, - "Subset 1": { - "_type": "glue.core.subset_group.SubsetGroup", - "label": "Subset 1", - "state": "RoiSubsetState", - "style": { - "_type": "glue.core.visual.VisualAttributes", - "alpha": 0.5, - "color": "#e31a1c", - "linestyle": "solid", - "linewidth": 1, - "marker": "o", - "markersize": 7 - }, - "subsets": [ - "Subset 1_0" - ] - }, - "Subset 1_0": { - "_type": "glue.core.subset_group.GroupedSubset", - "group": "Subset 1", - "style": { - "_type": "glue.core.visual.VisualAttributes", - "alpha": 0.5, - "color": "#e31a1c", - "linestyle": "solid", - "linewidth": 1, - "marker": "o", - "markersize": 7 - } - }, - "Subset 2": { - "_type": "glue.core.subset_group.SubsetGroup", - "label": "Subset 2", - "state": "RoiSubsetState_0", - "style": { - "_type": "glue.core.visual.VisualAttributes", - "alpha": 0.5, - "color": "#33a02c", - "linestyle": "solid", - "linewidth": 1, - "marker": "o", - "markersize": 7 - }, - "subsets": [ - "Subset 2_0" - ] - }, - "Subset 2_0": { - "_type": "glue.core.subset_group.GroupedSubset", - "group": "Subset 2", - "style": { - "_type": "glue.core.visual.VisualAttributes", - "alpha": 0.5, - "color": "#33a02c", - "linestyle": "solid", - "linewidth": 1, - "marker": "o", - "markersize": 7 - } - }, - "World 0": { - "_type": "glue.core.component_id.ComponentID", - "hidden": true, - "label": "World 0" - }, - "__main__": { - "_type": "glue.app.qt.application.GlueApplication", - "data": "DataCollection", - "plugins": [ - "glue.plugins.exporters.plotly", - "specviz.app", - "glue.plugins.coordinate_helpers", - "glue.viewers.histogram", - "glue.core.data_exporters", - "glue.plugins.export_d3po", - "glue_vispy_viewers.volume", - "glue_vispy_viewers.scatter", - "glue.viewers.table", - "glue.viewers.image", - "glue_medical", - "glue.plugins.tools.spectrum_tool", - "glue.plugins.tools.pv_slicer", - "glue.viewers.scatter" - ], - "session": "Session", - "tab_names": [ - "Tab 1" - ], - "viewers": [ - [ - "ScatterViewer", - "ScatterViewer_0", - "ScatterViewer_1" - ] - ] - }, - "a": { - "_type": "glue.core.component_id.ComponentID", - "hidden": false, - "label": "a" - }, - "b": { - "_type": "glue.core.component_id.ComponentID", - "hidden": false, - "label": "b" - }, - "basic": { - "_key_joins": [], - "_protocol": 5, - "_type": "glue.core.data.Data", - "components": [ - [ - "a", - "Component" - ], - [ - "Pixel Axis 0 [x]", - "CoordinateComponent" - ], - [ - "World 0", - "CoordinateComponent_0" - ], - [ - "b", - "Component_0" - ], - [ - "c", - "Component_1" - ] - ], - "coords": "Coordinates", - "label": "basic", - "primary_owner": [ - "a", - "Pixel Axis 0 [x]", - "World 0", - "b", - "c" - ], - "style": { - "_type": "glue.core.visual.VisualAttributes", - "alpha": 0.8, - "color": "0.35", - "linestyle": "solid", - "linewidth": 1, - "marker": "o", - "markersize": 3 - }, - "subsets": [ - "Subset 1_0", - "Subset 2_0" - ], - "uuid": "dde080dc-a65a-4988-bed9-c0cb90ff91a8" - }, - "c": { - "_type": "glue.core.component_id.ComponentID", - "hidden": false, - "label": "c" - } -} \ No newline at end of file diff --git a/glue/viewers/scatter/qt/tests/test_data_viewer.py b/glue/viewers/scatter/qt/tests/test_data_viewer.py deleted file mode 100644 index c0452d098..000000000 --- a/glue/viewers/scatter/qt/tests/test_data_viewer.py +++ /dev/null @@ -1,1004 +0,0 @@ -# pylint: disable=I0011,W0613,W0201,W0212,E1101,E1103 - -import os -from collections import Counter - -import pytest -import numpy as np - -from numpy.testing import assert_allclose, assert_equal - -from glue.config import colormaps -from glue.core.message import SubsetUpdateMessage -from glue.core import HubListener, Data -from glue.core.roi import XRangeROI, RectangularROI, CircularROI -from glue.core.roi_pretransforms import FullSphereLongitudeTransform, ProjectionMplTransform, RadianTransform -from glue.core.subset import RoiSubsetState, AndState -from glue import core -from glue.core.component_id import ComponentID -from glue.utils.qt import combo_as_string, process_events -from glue.viewers.matplotlib.qt.tests.test_data_viewer import BaseTestMatplotlibDataViewer -from glue.core.state import GlueUnSerializer -from glue.app.qt.layer_tree_widget import LayerTreeWidget -from glue.app.qt import GlueApplication - -from ..data_viewer import ScatterViewer - -DATA = os.path.join(os.path.dirname(__file__), 'data') - -fullsphere_projections = ['aitoff', 'hammer', 'lambert', 'mollweide'] - - -class TestScatterCommon(BaseTestMatplotlibDataViewer): - def init_data(self): - return Data(label='d1', x=[3.4, 2.3, -1.1, 0.3], y=['a', 'b', 'c', 'a']) - viewer_cls = ScatterViewer - - -class TestScatterViewer(object): - - def setup_method(self, method): - - self.data = Data(label='d1', x=[3.4, 2.3, -1.1, 0.3], - y=[3.2, 3.3, 3.4, 3.5], z=['a', 'b', 'c', 'a']) - self.data_2d = Data(label='d2', a=[[1, 2], [3, 4]], b=[[5, 6], [7, 8]], - x=[[3, 5], [5.4, 1]], y=[[1.2, 4], [7, 8]]) - self.data_fullsphere = Data(label='d3', x=[6.9, -1.1, 1.2, -3.7], - y=[-0.2, 1.0, 0.5, -1.1]) - - self.app = GlueApplication() - self.session = self.app.session - self.hub = self.session.hub - - self.data_collection = self.session.data_collection - self.data_collection.append(self.data) - self.data_collection.append(self.data_2d) - self.data_collection.append(self.data_fullsphere) - - self.viewer = self.app.new_data_viewer(ScatterViewer) - - def teardown_method(self, method): - self.viewer.close() - self.viewer = None - self.app.close() - self.app = None - - def test_basic(self): - - viewer_state = self.viewer.state - - # Check defaults when we add data - self.viewer.add_data(self.data) - - assert combo_as_string(self.viewer.options_widget().ui.combosel_x_att) == 'Main components:x:y:z:Coordinate components:Pixel Axis 0 [x]' - assert combo_as_string(self.viewer.options_widget().ui.combosel_y_att) == 'Main components:x:y:z:Coordinate components:Pixel Axis 0 [x]' - - assert viewer_state.x_att is self.data.id['x'] - assert_allclose(viewer_state.x_min, -1.1 - 0.18) - assert_allclose(viewer_state.x_max, 3.4 + 0.18) - - assert viewer_state.y_att is self.data.id['y'] - assert_allclose(viewer_state.y_min, 3.2 - 0.012) - assert_allclose(viewer_state.y_max, 3.5 + 0.012) - - assert not viewer_state.x_log - assert not viewer_state.y_log - - assert len(viewer_state.layers) == 1 - - # Change to categorical component and check new values - - viewer_state.y_att = self.data.id['z'] - - assert viewer_state.x_att is self.data.id['x'] - assert_allclose(viewer_state.x_min, -1.1 - 0.18) - assert_allclose(viewer_state.x_max, 3.4 + 0.18) - - assert viewer_state.y_att is self.data.id['z'] - assert_allclose(viewer_state.y_min, -0.5 - 0.12) - assert_allclose(viewer_state.y_max, 2.5 + 0.12) - - assert not viewer_state.x_log - assert not viewer_state.y_log - - def test_flip(self): - - viewer_state = self.viewer.state - - self.viewer.add_data(self.data) - - assert_allclose(viewer_state.x_min, -1.1 - 0.18) - assert_allclose(viewer_state.x_max, 3.4 + 0.18) - - self.viewer.options_widget().button_flip_x.click() - - assert_allclose(viewer_state.x_max, -1.1 - 0.18) - assert_allclose(viewer_state.x_min, 3.4 + 0.18) - - assert_allclose(viewer_state.y_min, 3.2 - 0.012) - assert_allclose(viewer_state.y_max, 3.5 + 0.012) - - self.viewer.options_widget().button_flip_y.click() - - assert_allclose(viewer_state.y_max, 3.2 - 0.012) - assert_allclose(viewer_state.y_min, 3.5 + 0.012) - - def test_remove_data(self): - self.viewer.add_data(self.data) - assert combo_as_string(self.viewer.options_widget().ui.combosel_x_att) == 'Main components:x:y:z:Coordinate components:Pixel Axis 0 [x]' - assert combo_as_string(self.viewer.options_widget().ui.combosel_y_att) == 'Main components:x:y:z:Coordinate components:Pixel Axis 0 [x]' - self.data_collection.remove(self.data) - assert combo_as_string(self.viewer.options_widget().ui.combosel_x_att) == '' - assert combo_as_string(self.viewer.options_widget().ui.combosel_y_att) == '' - - def test_update_component_updates_title(self): - self.viewer.add_data(self.data) - assert self.viewer.windowTitle() == '2D Scatter' - self.viewer.state.x_att = self.data.id['y'] - assert self.viewer.windowTitle() == '2D Scatter' - - def test_combo_updates_with_component_add(self): - self.viewer.add_data(self.data) - self.data.add_component([3, 4, 1, 2], 'a') - assert self.viewer.state.x_att is self.data.id['x'] - assert self.viewer.state.y_att is self.data.id['y'] - assert combo_as_string(self.viewer.options_widget().ui.combosel_x_att) == 'Main components:x:y:z:a:Coordinate components:Pixel Axis 0 [x]' - assert combo_as_string(self.viewer.options_widget().ui.combosel_y_att) == 'Main components:x:y:z:a:Coordinate components:Pixel Axis 0 [x]' - - def test_nonnumeric_first_component(self): - # regression test for #208. Shouldn't complain if - # first component is non-numerical - data = core.Data() - data.add_component(['a', 'b', 'c'], label='c1') - data.add_component([1, 2, 3], label='c2') - self.data_collection.append(data) - self.viewer.add_data(data) - - def test_apply_roi(self): - - self.viewer.add_data(self.data) - - roi = RectangularROI(0, 3, 3.25, 3.45) - - assert len(self.viewer.layers) == 1 - - self.viewer.apply_roi(roi) - - assert len(self.viewer.layers) == 2 - assert len(self.data.subsets) == 1 - - assert_allclose(self.data.subsets[0].to_mask(), [0, 1, 0, 0]) - - state = self.data.subsets[0].subset_state - assert isinstance(state, RoiSubsetState) - - def test_apply_roi_categorical(self): - - viewer_state = self.viewer.state - - self.viewer.add_data(self.data) - - viewer_state.y_att = self.data.id['z'] - - roi = RectangularROI(0, 3, -0.4, 0.3) - - assert len(self.viewer.layers) == 1 - - self.viewer.apply_roi(roi) - - assert len(self.viewer.layers) == 2 - assert len(self.data.subsets) == 1 - - assert_allclose(self.data.subsets[0].to_mask(), [0, 0, 0, 1]) - - state = self.data.subsets[0].subset_state - assert isinstance(state, AndState) - - def test_apply_roi_empty(self): - # Make sure that doing an ROI selection on an empty viewer doesn't - # produce error messsages - roi = XRangeROI(-0.2, 0.1) - self.viewer.apply_roi(roi) - - def test_axes_labels(self): - - viewer_state = self.viewer.state - - self.viewer.add_data(self.data) - - assert self.viewer.axes.get_xlabel() == 'x' - assert self.viewer.axes.get_ylabel() == 'y' - - viewer_state.x_log = True - - assert self.viewer.axes.get_xlabel() == 'x' - assert self.viewer.axes.get_ylabel() == 'y' - - viewer_state.x_att = self.data.id['y'] - - assert self.viewer.axes.get_xlabel() == 'y' - assert self.viewer.axes.get_ylabel() == 'y' - - viewer_state.y_log = True - - assert self.viewer.axes.get_xlabel() == 'y' - assert self.viewer.axes.get_ylabel() == 'y' - - def test_component_replaced(self): - - # regression test for 508 - if a component ID is replaced, we should - # make sure that the component ID is selected if the old component ID - # was selected - - self.viewer.add_data(self.data) - self.viewer.state.x_att = self.data.id['x'] - test = ComponentID('test') - self.data.update_id(self.viewer.state.x_att, test) - assert self.viewer.state.x_att is test - assert combo_as_string(self.viewer.options_widget().ui.combosel_x_att) == 'Main components:test:y:z:Coordinate components:Pixel Axis 0 [x]' - - def test_nan_component(self): - # regression test for case when all values are NaN in a component - data = core.Data() - data.add_component([np.nan, np.nan, np.nan], label='c1') - self.data_collection.append(data) - self.viewer.add_data(data) - - def test_density_map(self): - - kwargs = dict(range=[(-5, 5), (-5, 5)], bins=(2, 2)) - - self.viewer.add_data(self.data) - self.viewer.state.layers[0].points_mode = 'auto' - assert self.viewer.layers[0].state.compute_density_map(**kwargs).sum() == 0 - self.viewer.state.layers[0].points_mode = 'density' - assert self.viewer.layers[0].state.compute_density_map(**kwargs).sum() == 4 - self.viewer.state.layers[0].points_mode = 'markers' - assert self.viewer.layers[0].state.compute_density_map(**kwargs).sum() == 0 - - def test_density_map_color(self): - - # Regression test to make sure things don't crash when changing - # back to markers if the color mode is cmap - - self.viewer.add_data(self.data) - self.viewer.state.layers[0].points_mode = 'density' - self.viewer.state.layers[0].cmap_mode = 'Linear' - self.viewer.state.layers[0].size_mode = 'Linear' - self.viewer.state.layers[0].points_mode = 'markers' - self.viewer.state.layers[0].points_mode = 'density' - - @pytest.mark.parametrize('protocol', [0, 1]) - def test_session_back_compat(self, protocol): - - filename = os.path.join(DATA, 'scatter_v{0}.glu'.format(protocol)) - - with open(filename, 'r') as f: - session = f.read() - - state = GlueUnSerializer.loads(session) - - ga = state.object('__main__') - - dc = ga.session.data_collection - - assert len(dc) == 1 - - assert dc[0].label == 'basic' - - viewer1 = ga.viewers[0][0] - assert len(viewer1.state.layers) == 3 - assert viewer1.state.x_att is dc[0].id['a'] - assert viewer1.state.y_att is dc[0].id['b'] - assert_allclose(viewer1.state.x_min, -1.04) - assert_allclose(viewer1.state.x_max, 1.04) - assert_allclose(viewer1.state.y_min, 1.98) - assert_allclose(viewer1.state.y_max, 3.02) - assert not viewer1.state.x_log - assert not viewer1.state.y_log - assert viewer1.state.layers[0].visible - assert viewer1.state.layers[1].visible - assert viewer1.state.layers[2].visible - - viewer2 = ga.viewers[0][1] - assert len(viewer2.state.layers) == 3 - assert viewer2.state.x_att is dc[0].id['a'] - assert viewer2.state.y_att is dc[0].id['c'] - assert_allclose(viewer2.state.x_min, 9.5e-6) - assert_allclose(viewer2.state.x_max, 1.05) - assert_allclose(viewer2.state.y_min, 0.38) - assert_allclose(viewer2.state.y_max, 5.25) - assert viewer2.state.x_log - assert viewer2.state.y_log - assert viewer2.state.layers[0].visible - assert not viewer2.state.layers[1].visible - assert viewer2.state.layers[2].visible - - viewer3 = ga.viewers[0][2] - assert len(viewer3.state.layers) == 3 - assert viewer3.state.x_att is dc[0].id['b'] - assert viewer3.state.y_att is dc[0].id['a'] - assert_allclose(viewer3.state.x_min, 0) - assert_allclose(viewer3.state.x_max, 5) - assert_allclose(viewer3.state.y_min, -5) - assert_allclose(viewer3.state.y_max, 5) - assert not viewer3.state.x_log - assert not viewer3.state.y_log - assert viewer3.state.layers[0].visible - assert viewer3.state.layers[1].visible - assert not viewer3.state.layers[2].visible - - ga.close() - - def test_session_line_back_compat(self): - - # Backward-compatibility for v0.11 files in which the line and scatter - # plots were defined as separate styles. - - filename = os.path.join(DATA, 'scatter_and_line_v1.glu') - - with open(filename, 'r') as f: - session = f.read() - - state = GlueUnSerializer.loads(session) - - ga = state.object('__main__') - - dc = ga.session.data_collection - - assert len(dc) == 1 - - assert dc[0].label == 'table' - - viewer1 = ga.viewers[0][0] - assert len(viewer1.state.layers) == 1 - assert viewer1.state.x_att is dc[0].id['a'] - assert viewer1.state.y_att is dc[0].id['b'] - assert viewer1.state.layers[0].markers_visible - assert not viewer1.state.layers[0].line_visible - - viewer1 = ga.viewers[0][1] - assert len(viewer1.state.layers) == 1 - assert viewer1.state.x_att is dc[0].id['a'] - assert viewer1.state.y_att is dc[0].id['b'] - assert not viewer1.state.layers[0].markers_visible - assert viewer1.state.layers[0].line_visible - - ga.close() - - def test_save_svg(self, tmpdir): - # Regression test for a bug in AxesCache that caused SVG saving to - # fail (because renderer.buffer_rgba did not exist) - self.viewer.add_data(self.data) - filename = tmpdir.join('test.svg').strpath - self.viewer.axes.figure.savefig(filename) - - def test_2d(self): - - viewer_state = self.viewer.state - - self.viewer.add_data(self.data_2d) - - assert viewer_state.x_att is self.data_2d.id['a'] - assert_allclose(viewer_state.x_min, 1 - 0.12) - assert_allclose(viewer_state.x_max, 4 + 0.12) - - assert viewer_state.y_att is self.data_2d.id['b'] - assert_allclose(viewer_state.y_min, 5 - 0.12) - assert_allclose(viewer_state.y_max, 8 + 0.12) - - assert self.viewer.layers[0].plot_artist.get_xdata().shape == (4,) - - def test_apply_roi_single(self): - - # Regression test for a bug that caused mode.update to be called - # multiple times and resulted in all other viewers receiving many - # messages regarding subset updates (this occurred when multiple) - # datasets were present. - - layer_tree = LayerTreeWidget(session=self.session) - layer_tree.set_checkable(False) - layer_tree.setup(self.data_collection) - layer_tree.bind_selection_to_edit_subset() - - class Client(HubListener): - - def __init__(self, *args, **kwargs): - super(Client, self).__init__(*args, **kwargs) - self.count = Counter() - - def ping(self, message): - self.count[message.sender] += 1 - - def register_to_hub(self, hub): - hub.subscribe(self, SubsetUpdateMessage, handler=self.ping) - - d1 = Data(a=[1, 2, 3], label='d3') - d2 = Data(b=[1, 2, 3], label='d4') - d3 = Data(c=[1, 2, 3], label='d5') - d4 = Data(d=[1, 2, 3], label='d6') - - self.data_collection.append(d1) - self.data_collection.append(d2) - self.data_collection.append(d3) - self.data_collection.append(d4) - - client = Client() - client.register_to_hub(self.hub) - - self.viewer.add_data(d1) - self.viewer.add_data(d3) - - roi = XRangeROI(2.5, 3.5) - self.viewer.apply_roi(roi) - - for subset in client.count: - assert client.count[subset] == 1 - - @pytest.mark.parametrize('ndim', [1, 2]) - def test_all_options(self, ndim): - - # This test makes sure that all the code for the different scatter modes - # gets run, though does not check the result. - - viewer_state = self.viewer.state - - if ndim == 1: - data = self.data - elif ndim == 2: - data = self.data_2d - - self.viewer.add_data(data) - - layer_state = viewer_state.layers[0] - - layer_state.style = 'Scatter' - - layer_state.size_mode = 'Linear' - layer_state.size_att = data.id['y'] - layer_state.size_vmin = 1.2 - layer_state.size_vmax = 4. - layer_state.size_scaling = 2 - - layer_state.cmap_mode = 'Linear' - layer_state.cmap_att = data.id['x'] - layer_state.cmap_vmin = -1 - layer_state.cmap_vmax = 2. - layer_state.cmap = colormaps.members[3][1] - - # Check inverting works - layer_state.cmap_vmin = 3. - - layer_state.size_mode = 'Fixed' - - layer_state.xerr_visible = True - layer_state.xerr_att = data.id['x'] - layer_state.yerr_visible = True - layer_state.yerr_att = data.id['y'] - - layer_state.style = 'Line' - layer_state.linewidth = 3 - layer_state.linestyle = 'dashed' - - def test_session_categorical(self, tmpdir): - - def visible_xaxis_labels(ax): - # Due to a bug in Matplotlib the labels returned outside the field - # of view may be incorrect: https://github.com/matplotlib/matplotlib/issues/9397 - pos = ax.xaxis.get_ticklocs() - labels = [tick.get_text() for tick in ax.xaxis.get_ticklabels()] - xmin, xmax = ax.get_xlim() - return [labels[i] for i in range(len(pos)) if pos[i] >= xmin and pos[i] <= xmax] - - # Regression test for a bug that caused a restored scatter viewer - # with a categorical component to not show the categorical labels - # as tick labels. - - filename = tmpdir.join('test_session_categorical.glu').strpath - - self.viewer.add_data(self.data) - self.viewer.state.x_att = self.data.id['z'] - - process_events() - - assert visible_xaxis_labels(self.viewer.axes) == ['a', 'b', 'c'] - - self.session.application.save_session(filename) - - with open(filename, 'r') as f: - session = f.read() - - state = GlueUnSerializer.loads(session) - - ga = state.object('__main__') - - dc = ga.session.data_collection - - viewer = ga.viewers[0][0] - assert viewer.state.x_att is dc[0].id['z'] - assert visible_xaxis_labels(self.viewer.axes) == ['a', 'b', 'c'] - - ga.close() - - def test_enable_disable_components_combo(self): - - # Regression test for a bug that caused an error when turning off pixel - # components from combo boxes. - - self.viewer.add_data(self.data) - - self.data['a'] = self.data.id['x'] + 5 - - self.viewer.state.x_att_helper.pixel_coord = True - - self.viewer.state.x_att = self.data.pixel_component_ids[0] - - self.viewer.state.x_att_helper.pixel_coord = False - - def test_datetime64_support(self, tmpdir): - - self.data.add_component(np.array([100, 200, 300, 400], dtype='M8[D]'), 't1') - self.data.add_component(np.array([200, 300, 400, 500], dtype='M8[D]'), 't2') - self.viewer.add_data(self.data) - self.viewer.state.x_att = self.data.id['t1'] - self.viewer.state.y_att = self.data.id['y'] - - # Matplotlib deals with dates by converting them to the number of days - # since 01-01-0001, so we can check that the limits are correctly - # converted (and not 100 to 400) - assert self.viewer.axes.get_xlim() == (719251.0, 719575.0) - assert self.viewer.axes.get_ylim() == (3.2 - 0.012, 3.5 + 0.012) - - # Apply an ROI selection in plotting coordinates - roi = RectangularROI(xmin=719313, xmax=719513, ymin=3, ymax=4) - self.viewer.apply_roi(roi) - - # Check that the two middle elements are selected - assert_equal(self.data.subsets[0].to_mask(), [0, 1, 1, 0]) - - # Now do the same with the y axis - self.viewer.state.y_att = self.data.id['t2'] - - assert self.viewer.axes.get_xlim() == (719251.0, 719575.0) - assert self.viewer.axes.get_ylim() == (719351.0, 719675.0) - - # Apply an ROI selection in plotting coordinates - edit = self.session.edit_subset_mode - edit.edit_subset = [] - roi = CircularROI(xc=719463, yc=719563, radius=200) - self.viewer.apply_roi(roi) - assert_equal(self.data.subsets[1].to_mask(), [0, 1, 1, 1]) - - # Make sure that the Qt labels look ok - self.viewer.state.y_att = self.data.id['y'] - options = self.viewer.options_widget().ui - assert options.valuetext_x_min.text() == '1970-03-30' - assert options.valuetext_x_max.text() == '1971-02-17' - assert options.valuetext_y_min.text() == '3.188' - assert options.valuetext_y_max.text() == '3.512' - - # Make sure that we can set the xmin/xmax to a string date - assert_equal(self.viewer.state.x_min, np.datetime64('1970-03-30', 'D')) - options.valuetext_x_min.setText('1970-04-14') - options.valuetext_x_min.editingFinished.emit() - assert self.viewer.axes.get_xlim() == (719266.0, 719575.0) - assert_equal(self.viewer.state.x_min, np.datetime64('1970-04-14', 'D')) - - # Make sure that everything works fine after saving/reloading - filename = tmpdir.join('test_datetime64.glu').strpath - self.session.application.save_session(filename) - with open(filename, 'r') as f: - session = f.read() - state = GlueUnSerializer.loads(session) - ga = state.object('__main__') - viewer = ga.viewers[0][0] - options = viewer.options_widget().ui - - assert_equal(self.viewer.state.x_min, np.datetime64('1970-04-14', 'D')) - - assert options.valuetext_x_min.text() == '1970-04-14' - assert options.valuetext_x_max.text() == '1971-02-17' - assert options.valuetext_y_min.text() == '3.188' - assert options.valuetext_y_max.text() == '3.512' - - ga.close() - - def test_datetime64_disabled(self, capsys): - - # Make sure that datetime components aren't options for the vector and - # error markers. - - data = Data(label='test') - data.add_component(np.array([100, 200, 300, 400], dtype='M8[D]'), 't1') - data.add_component(np.array([200, 300, 400, 500], dtype='M8[D]'), 't2') - data.add_component(np.array([200., 300., 400., 500.]), 'x') - data.add_component(np.array([200., 300., 400., 500.]), 'y') - self.data_collection.append(data) - - self.viewer.add_data(data) - self.viewer.state.x_att = data.id['x'] - self.viewer.state.y_att = data.id['y'] - self.viewer.state.layers[0].cmap_mode = 'Linear' - self.viewer.state.layers[0].cmap_att = data.id['x'] - self.viewer.state.layers[0].size_mode = 'Linear' - self.viewer.state.layers[0].size_att = data.id['y'] - self.viewer.state.layers[0].vector_visible = True - self.viewer.state.layers[0].xerr_visible = True - self.viewer.state.layers[0].yerr_visible = True - - process_events() - - self.viewer.state.x_att = data.id['t1'] - self.viewer.state.y_att = data.id['t2'] - - process_events() - - # We use capsys here because the # error is otherwise only apparent in stderr. - out, err = capsys.readouterr() - assert out.strip() == "" - assert err.strip() == "" - - def test_density_map_incompatible_subset(self, capsys): - - # Regression test for a bug that caused the scatter viewer to crash - # if subset for density map was incompatible. - - data2 = Data(label='d1', x=[3.4, 2.3, -1.1, 0.3], y=[3.2, 3.3, 3.4, 3.5], z=['a', 'b', 'c', 'a']) - - self.data_collection.append(data2) - - self.viewer.add_data(self.data) - self.viewer.add_data(data2) - - self.data_collection.new_subset_group('test', self.data.id['x'] > 1) - - for layer in self.viewer.state.layers: - layer.density_map = True - - self.viewer.figure.canvas.draw() - process_events() - - assert self.viewer.layers[0].enabled - assert not self.viewer.layers[1].enabled - assert self.viewer.layers[2].enabled - assert not self.viewer.layers[3].enabled - - def test_density_map_line_error_vector(self, capsys): - - # Make sure that we don't allow/show lines/errors/vectors - # if in density map mode. - - self.viewer.add_data(self.data) - - self.viewer.state.layers[0].line_visible = True - self.viewer.state.layers[0].xerr_visible = True - self.viewer.state.layers[0].yerr_visible = True - self.viewer.state.layers[0].vector_visible = True - - # Setting density_map to True resets the visibility of - # lines/errors/vectors. - self.viewer.state.layers[0].density_map = True - assert not self.viewer.state.layers[0].line_visible - assert not self.viewer.state.layers[0].xerr_visible - assert not self.viewer.state.layers[0].yerr_visible - assert not self.viewer.state.layers[0].vector_visible - - def test_legend(self): - viewer_state = self.viewer.state - - self.viewer.add_data(self.data) - viewer_state.legend.visible = True - - handles, labels, handler_dict = self.viewer.get_handles_legend() - assert len(handles) == 1 - assert labels[0] == 'd1' - - self.data_collection.new_subset_group('test', self.data.id['x'] > 1) - assert len(viewer_state.layers) == 2 - handles, labels, handler_dict = self.viewer.get_handles_legend() - - assert len(handles) == 2 - assert labels[1] == 'test' - print(handles[1][0]) - # assert handles[1][0].get_color() == viewer_state.layers[1].state.color - - # Add a non visible layer - data2 = Data(label='d2', x=[3.4, 2.3, -1.1, 0.3], y=[3.2, 3.3, 3.4, 3.5]) - self.data_collection.append(data2) - - self.viewer.add_data(data2) - assert len(viewer_state.layers) == 4 - - # 'd2' is not enabled (no linked component) - handles, labels, handler_dict = self.viewer.get_handles_legend() - assert len(handles) == 2 - - def test_changing_plot_modes(self): - viewer_state = self.viewer.state - viewer_state.plot_mode = 'polar' - assert 'polar' in str(type(self.viewer.axes)).lower() - viewer_state.plot_mode = 'aitoff' - assert 'aitoff' in str(type(self.viewer.axes)).lower() - viewer_state.plot_mode = 'hammer' - assert 'hammer' in str(type(self.viewer.axes)).lower() - viewer_state.plot_mode = 'lambert' - assert 'lambert' in str(type(self.viewer.axes)).lower() - viewer_state.plot_mode = 'mollweide' - assert 'mollweide' in str(type(self.viewer.axes)).lower() - - # For the time being, the polar plots don't support log scaling - # def test_limit_log_set_polar(self): - # self.viewer.add_data(self.data) - # viewer_state = self.viewer.state - # viewer_state.plot_mode = "polar" - # axes = self.viewer.axes - # - # viewer_state.x_min = 0.5 - # viewer_state.x_max = 1.5 - # assert_allclose(axes.get_xlim(), [0.5, 1.5]) - # - # viewer_state.y_min = -2.5 - # viewer_state.y_max = 2.5 - # assert_allclose(axes.get_ylim(), [-2.5, 2.5]) - # - # viewer_state.y_log = True - # assert axes.get_yscale() == 'log' - - def test_limit_set_fullsphere(self): - # Make sure that the full-sphere projections ignore instead of throwing exceptions - self.viewer.add_data(self.data) - viewer_state = self.viewer.state - - for proj in fullsphere_projections: - viewer_state.plot_mode = proj - error_msg = 'Issue with {} projection'.format(proj) - axes = self.viewer.axes - viewer_state.x_min = 0.5 - viewer_state.x_max = 1.5 - viewer_state.y_min = -2.5 - viewer_state.y_max = 2.5 - assert_allclose(axes.get_xlim(), [-np.pi, np.pi], err_msg=error_msg) - assert_allclose(axes.get_ylim(), [-np.pi / 2, np.pi / 2], err_msg=error_msg) - - def test_changing_mode_limits(self): - self.viewer.add_data(self.data) - viewer_state = self.viewer.state - old_xmin = viewer_state.x_min - old_xmax = viewer_state.x_max - old_ymin = viewer_state.y_min - old_ymax = viewer_state.y_max - # Make sure limits are reset first - viewer_state.x_max += 3 - - # Currently, when we change to polar mode, the x-limits are changed to 0, 2pi - viewer_state.plot_mode = 'polar' - assert_allclose(viewer_state.x_min, 0) - assert_allclose(viewer_state.x_max, 2 * np.pi) - assert_allclose(self.viewer.axes.get_xlim(), [0, 2 * np.pi]) - assert_allclose(viewer_state.y_min, old_ymin) - assert_allclose(viewer_state.y_max, old_ymax) - assert_allclose(self.viewer.axes.get_ylim(), [old_ymin, old_ymax]) - - viewer_state.plot_mode = 'rectilinear' - assert_allclose(viewer_state.x_min, old_xmin) - assert_allclose(viewer_state.x_max, old_xmax) - assert_allclose(self.viewer.axes.get_xlim(), [old_xmin, old_xmax]) - assert_allclose(viewer_state.y_min, old_ymin) - assert_allclose(viewer_state.y_max, old_ymax) - assert_allclose(self.viewer.axes.get_ylim(), [old_ymin, old_ymax]) - - for proj in fullsphere_projections: - viewer_state.plot_mode = 'rectilinear' - viewer_state.plot_mode = proj - error_msg = 'Issue with {} projection'.format(proj) - assert_allclose(viewer_state.x_min, -np.pi) - assert_allclose(viewer_state.x_max, np.pi) - assert_allclose(self.viewer.axes.get_xlim(), [-np.pi, np.pi], err_msg=error_msg) - assert_allclose(viewer_state.y_min, -np.pi / 2) - assert_allclose(viewer_state.y_max, np.pi / 2) - assert_allclose(self.viewer.axes.get_ylim(), [-np.pi / 2, np.pi / 2], err_msg=error_msg) - - def test_changing_mode_log(self): - # Test to make sure we reset the log axes to false when changing modes to prevent problems - self.viewer.add_data(self.data) - viewer_state = self.viewer.state - viewer_state.x_log = True - viewer_state.y_log = True - - viewer_state.plot_mode = 'polar' - assert not viewer_state.x_log - assert not viewer_state.y_log - assert self.viewer.axes.get_xscale() == 'linear' - assert self.viewer.axes.get_yscale() == 'linear' - viewer_state.y_log = True - - viewer_state.plot_mode = 'rectilinear' - assert not viewer_state.x_log - assert not viewer_state.y_log - assert self.viewer.axes.get_xscale() == 'linear' - assert self.viewer.axes.get_yscale() == 'linear' - - for proj in fullsphere_projections: - viewer_state.plot_mode = 'rectilinear' - viewer_state.x_log = True - viewer_state.y_log = True - viewer_state.plot_mode = proj - error_msg = 'Issue with {} projection'.format(proj) - assert not viewer_state.x_log, error_msg - assert not viewer_state.y_log, error_msg - assert self.viewer.axes.get_xscale() == 'linear', error_msg - assert self.viewer.axes.get_yscale() == 'linear', error_msg - - def test_full_circle_utility(self): - # Make sure that the full circle function behaves well - self.viewer.add_data(self.data) - viewer_state = self.viewer.state - old_xmin = viewer_state.x_min - old_xmax = viewer_state.x_max - old_ymin = viewer_state.y_min - old_ymax = viewer_state.y_max - viewer_state.full_circle() - assert_allclose([viewer_state.x_min, viewer_state.x_max], [old_xmin, old_xmax]) - assert_allclose([viewer_state.y_min, viewer_state.y_max], [old_ymin, old_ymax]) - - viewer_state.plot_mode = 'polar' - viewer_state.full_circle() - assert_allclose([viewer_state.x_min, viewer_state.x_max], [0, 2 * np.pi]) - assert_allclose([viewer_state.y_min, viewer_state.y_max], [old_ymin, old_ymax]) - - for proj in fullsphere_projections: - error_msg = 'Issue with {} projection'.format(proj) - viewer_state.plot_mode = proj - viewer_state.full_circle() - assert_allclose([viewer_state.x_min, viewer_state.x_max], [-np.pi, np.pi], err_msg=error_msg) - assert_allclose([viewer_state.y_min, viewer_state.y_max], [-np.pi / 2, np.pi / 2], err_msg=error_msg) - - def test_limits_log_widget_polar_cartesian(self): - ui = self.viewer.options_widget().ui - viewer_state = self.viewer.state - viewer_state.plot_mode = 'polar' - assert not ui.bool_x_log.isEnabled() - assert not ui.bool_x_log_.isEnabled() - assert not ui.bool_y_log.isEnabled() - assert not ui.bool_y_log_.isEnabled() - assert ui.valuetext_x_min.isEnabled() - assert ui.button_flip_x.isEnabled() - assert ui.valuetext_x_max.isEnabled() - assert ui.valuetext_y_min.isEnabled() - assert ui.button_flip_y.isEnabled() - assert ui.valuetext_y_max.isEnabled() - assert ui.button_full_circle.isHidden() - - viewer_state.plot_mode = 'rectilinear' - assert ui.bool_x_log.isEnabled() - assert ui.bool_x_log_.isEnabled() - assert ui.bool_y_log.isEnabled() - assert ui.bool_y_log_.isEnabled() - assert ui.valuetext_x_min.isEnabled() - assert ui.button_flip_x.isEnabled() - assert ui.valuetext_x_max.isEnabled() - assert ui.valuetext_y_min.isEnabled() - assert ui.button_flip_y.isEnabled() - assert ui.valuetext_y_max.isEnabled() - assert ui.button_full_circle.isHidden() - assert ui.button_full_circle.isHidden() - - def test_limits_log_widget_fullsphere(self): - ui = self.viewer.options_widget().ui - viewer_state = self.viewer.state - for proj in fullsphere_projections: - error_msg = 'Issue with {} projection'.format(proj) - viewer_state.plot_mode = proj - not ui.bool_x_log.isEnabled() - assert not ui.bool_x_log_.isEnabled(), error_msg - assert not ui.bool_y_log.isEnabled(), error_msg - assert not ui.bool_y_log_.isEnabled(), error_msg - assert not ui.valuetext_x_min.isEnabled(), error_msg - assert ui.button_flip_x.isEnabled(), error_msg - assert not ui.valuetext_x_max.isEnabled(), error_msg - assert not ui.valuetext_y_min.isEnabled(), error_msg - assert ui.button_flip_y.isEnabled(), error_msg - assert not ui.valuetext_y_max.isEnabled(), error_msg - assert ui.button_full_circle.isHidden(), error_msg - - viewer_state.plot_mode = 'rectilinear' - assert ui.bool_x_log.isEnabled() - assert ui.bool_x_log_.isEnabled() - assert ui.bool_y_log.isEnabled() - assert ui.bool_y_log_.isEnabled() - assert ui.valuetext_x_min.isEnabled() - assert ui.button_flip_x.isEnabled() - assert ui.valuetext_x_max.isEnabled() - assert ui.valuetext_y_min.isEnabled() - assert ui.button_flip_y.isEnabled() - assert ui.valuetext_y_max.isEnabled() - assert ui.button_full_circle.isHidden() - - @pytest.mark.parametrize('angle_unit,expected_mask', [('radians', [0, 0, 0, 1]), ('degrees', [1, 1, 0, 1])]) - def test_apply_roi_polar(self, angle_unit, expected_mask): - self.viewer.add_data(self.data) - viewer_state = self.viewer.state - roi = RectangularROI(0.5, 1, 0.5, 1) - viewer_state.plot_mode = 'polar' - viewer_state.full_circle() - assert len(self.viewer.layers) == 1 - - viewer_state.angle_unit = angle_unit - - self.viewer.apply_roi(roi) - - assert len(self.viewer.layers) == 2 - assert len(self.data.subsets) == 1 - - assert_allclose(self.data.subsets[0].to_mask(), expected_mask) - - state = self.data.subsets[0].subset_state - assert isinstance(state, RoiSubsetState) - assert state.pretransform - pretrans = state.pretransform - if angle_unit == 'radians': - assert isinstance(pretrans, ProjectionMplTransform) - projtrans = pretrans - elif angle_unit == 'degrees': - assert isinstance(pretrans, RadianTransform) - projtrans = pretrans._next_transform - assert isinstance(projtrans, ProjectionMplTransform) - assert projtrans._state['projection'] == 'polar' - assert_allclose(projtrans._state['x_lim'], [viewer_state.x_min, viewer_state.x_max]) - assert_allclose(projtrans._state['y_lim'], [viewer_state.y_min, viewer_state.y_max]) - assert projtrans._state['x_scale'] == 'linear' - assert projtrans._state['y_scale'] == 'linear' - self.data.subsets[0].delete() - - viewer_state.y_log = True - self.viewer.apply_roi(roi) - state = self.data.subsets[0].subset_state - assert state.pretransform - pretrans = state.pretransform - if angle_unit == 'radians': - assert isinstance(pretrans, ProjectionMplTransform) - projtrans = pretrans - elif angle_unit == 'degrees': - assert isinstance(pretrans, RadianTransform) - projtrans = pretrans._next_transform - assert isinstance(projtrans, ProjectionMplTransform) - assert projtrans._state['y_scale'] == 'log' - viewer_state.y_log = False - - @pytest.mark.parametrize('angle_unit,expected_mask', [('radians', [1, 0, 0, 1]), ('degrees', [1, 0, 0, 0])]) - def test_apply_roi_fullsphere(self, angle_unit, expected_mask): - self.viewer.add_data(self.data_fullsphere) - viewer_state = self.viewer.state - roi = RectangularROI(0.5, 1, 0, 0.5) - - viewer_state.angle_unit = angle_unit - for proj in fullsphere_projections: - viewer_state.plot_mode = proj - assert len(self.viewer.layers) == 1 - - self.viewer.apply_roi(roi) - - assert len(self.viewer.layers) == 2 - assert len(self.data_fullsphere.subsets) == 1 - - subset = self.data_fullsphere.subsets[0] - state = subset.subset_state - assert isinstance(state, RoiSubsetState) - - assert state.pretransform - pretrans = state.pretransform - if angle_unit == 'degrees': - assert isinstance(pretrans, RadianTransform) - pretrans = pretrans._next_transform - assert isinstance(pretrans, FullSphereLongitudeTransform) - projtrans = pretrans._next_transform - assert isinstance(projtrans, ProjectionMplTransform) - - assert_allclose(subset.to_mask(), expected_mask) - - assert projtrans._state['projection'] == proj - assert_allclose(projtrans._state['x_lim'], [viewer_state.x_min, viewer_state.x_max]) - assert_allclose(projtrans._state['y_lim'], [viewer_state.y_min, viewer_state.y_max]) - assert projtrans._state['x_scale'] == 'linear' - assert projtrans._state['y_scale'] == 'linear' - subset.delete() diff --git a/glue/viewers/scatter/qt/tests/test_python_export.py b/glue/viewers/scatter/qt/tests/test_python_export.py deleted file mode 100644 index ab89994a3..000000000 --- a/glue/viewers/scatter/qt/tests/test_python_export.py +++ /dev/null @@ -1,301 +0,0 @@ -from itertools import product - -import numpy as np -import matplotlib.pyplot as plt -from astropy.utils import NumpyRNGContext - -from glue.core import Data, DataCollection -from glue.app.qt.application import GlueApplication -from glue.viewers.matplotlib.qt.tests.test_python_export import BaseTestExportPython, random_with_nan -from glue.viewers.scatter.qt import ScatterViewer - - -class TestExportPython(BaseTestExportPython): - - def setup_method(self, method): - - with NumpyRNGContext(12345): - self.data = Data(**dict((name, random_with_nan(100, nan_index=idx + 1)) for idx, name in enumerate('abcdefgh'))) - self.data['angle'] = np.random.uniform(0, 360, 100) - self.data_collection = DataCollection([self.data]) - self.app = GlueApplication(self.data_collection) - self.viewer = self.app.new_data_viewer(ScatterViewer) - self.viewer.add_data(self.data) - self.viewer.state.x_att = self.data.id['a'] - self.viewer.state.y_att = self.data.id['b'] - - def teardown_method(self, method): - self.viewer.close() - self.viewer = None - self.app.close() - self.app = None - - def test_simple(self, tmpdir): - self.assert_same(tmpdir) - - def test_simple_legend(self, tmpdir): - self.viewer.state.legend.visible = True - self.assert_same(tmpdir) - - def test_simple_nofill(self, tmpdir): - self.viewer.state.layers[0].fill = False - self.viewer.state.layers[0].size_scaling = 10 - self.assert_same(tmpdir) - - def test_simple_visual(self, tmpdir): - self.viewer.state.layers[0].color = 'blue' - self.viewer.state.layers[0].markersize = 30 - self.viewer.state.layers[0].alpha = 0.5 - self.assert_same(tmpdir) - - def test_simple_visual_legend(self, tmpdir): - self.viewer.state.legend.visible = True - self.viewer.state.layers[0].color = 'blue' - self.viewer.state.layers[0].markersize = 30 - self.viewer.state.layers[0].alpha = 0.5 - self.assert_same(tmpdir) - - def test_cmap_mode(self, tmpdir): - self.viewer.state.layers[0].cmap_mode = 'Linear' - self.viewer.state.layers[0].cmap_att = self.data.id['c'] - self.viewer.state.layers[0].cmap = plt.cm.BuGn - self.viewer.state.layers[0].cmap_vmin = 0.2 - self.viewer.state.layers[0].cmap_vmax = 0.7 - self.viewer.state.layers[0].alpha = 0.8 - self.assert_same(tmpdir) - - def test_cmap_mode_nofill(self, tmpdir): - self.viewer.state.layers[0].fill = False - self.test_cmap_mode(tmpdir) - - def test_size_mode(self, tmpdir): - self.viewer.state.layers[0].size_mode = 'Linear' - self.viewer.state.layers[0].size_att = self.data.id['d'] - self.viewer.state.layers[0].size_vmin = 0.1 - self.viewer.state.layers[0].size_vmax = 0.8 - self.viewer.state.layers[0].size_scaling = 0.4 - self.viewer.state.layers[0].alpha = 0.7 - self.assert_same(tmpdir) - - def test_size_mode_legend(self, tmpdir): - self.viewer.state.legend.visible = True - self.viewer.state.layers[0].size_mode = 'Linear' - self.viewer.state.layers[0].size_att = self.data.id['d'] - self.viewer.state.layers[0].size_vmin = 0.1 - self.viewer.state.layers[0].size_vmax = 0.8 - self.viewer.state.layers[0].size_scaling = 0.4 - self.viewer.state.layers[0].alpha = 0.7 - self.assert_same(tmpdir) - - def test_size_mode_nofill(self, tmpdir): - self.viewer.state.layers[0].fill = False - self.test_size_mode(tmpdir) - - def test_line(self, tmpdir): - self.viewer.state.layers[0].line_visible = True - self.viewer.state.layers[0].linewidth = 10 - self.viewer.state.layers[0].linestype = 'dashed' - self.viewer.state.layers[0].color = 'orange' - self.viewer.state.layers[0].alpha = 0.7 - self.viewer.state.layers[0].markersize = 100 - self.assert_same(tmpdir, tol=5) - - def test_line_cmap(self, tmpdir): - self.viewer.state.layers[0].cmap_mode = 'Linear' - self.viewer.state.layers[0].cmap_vmin = 0.2 - self.viewer.state.layers[0].cmap_vmax = 0.7 - self.viewer.state.layers[0].cmap = plt.cm.BuGn - self.test_line(tmpdir) - - def test_errorbarx(self, tmpdir): - self.viewer.state.layers[0].xerr_visible = True - self.viewer.state.layers[0].xerr_att = self.data.id['e'] - self.viewer.state.layers[0].color = 'purple' - self.viewer.state.layers[0].alpha = 0.5 - self.assert_same(tmpdir) - - def test_errorbary(self, tmpdir): - self.viewer.state.layers[0].yerr_visible = True - self.viewer.state.layers[0].yerr_att = self.data.id['f'] - self.viewer.state.layers[0].color = 'purple' - self.viewer.state.layers[0].alpha = 0.5 - self.assert_same(tmpdir) - - def test_errorbarxy(self, tmpdir): - self.viewer.state.layers[0].xerr_visible = True - self.viewer.state.layers[0].xerr_att = self.data.id['e'] - self.viewer.state.layers[0].yerr_visible = True - self.viewer.state.layers[0].yerr_att = self.data.id['f'] - self.viewer.state.layers[0].color = 'purple' - self.viewer.state.layers[0].alpha = 0.5 - self.assert_same(tmpdir) - - def test_errorbarxy_legend(self, tmpdir): - self.viewer.state.legend.visible = True - self.viewer.state.layers[0].xerr_visible = True - self.viewer.state.layers[0].xerr_att = self.data.id['e'] - self.viewer.state.layers[0].yerr_visible = True - self.viewer.state.layers[0].yerr_att = self.data.id['f'] - self.viewer.state.layers[0].color = 'purple' - self.viewer.state.layers[0].alpha = 0.5 - self.assert_same(tmpdir) - - def test_errorbarxy_cmap(self, tmpdir): - self.viewer.state.layers[0].cmap_mode = 'Linear' - self.viewer.state.layers[0].cmap_vmin = 0.2 - self.viewer.state.layers[0].cmap_vmax = 0.7 - self.viewer.state.layers[0].cmap = plt.cm.BuGn - self.test_errorbarxy(tmpdir) - - def _vector_common(self, tmpdir): - self.viewer.state.layers[0].vector_visible = True - self.viewer.state.layers[0].vy_att = self.data.id['g'] - self.viewer.state.layers[0].vector_arrowhead = True - self.viewer.state.layers[0].vector_origin = 'tail' - self.viewer.state.layers[0].vector_scaling = 1.5 - self.viewer.state.layers[0].color = 'teal' - self.viewer.state.layers[0].alpha = 0.9 - self.assert_same(tmpdir, tol=1) - - def test_vector_cartesian(self, tmpdir): - self.viewer.state.layers[0].vector_mode = 'Cartesian' - self.viewer.state.layers[0].vx_att = self.data.id['h'] - self._vector_common(tmpdir) - - def test_vector_polar(self, tmpdir): - self.viewer.state.layers[0].vector_mode = 'Polar' - self.viewer.state.layers[0].vx_att = self.data.id['angle'] - self._vector_common(tmpdir) - - def test_vector_cartesian_cmap(self, tmpdir): - self.viewer.state.layers[0].cmap_mode = 'Linear' - self.viewer.state.layers[0].cmap_vmin = 0.2 - self.viewer.state.layers[0].cmap_vmax = 0.7 - self.viewer.state.layers[0].cmap = plt.cm.BuGn - self.test_vector_cartesian(tmpdir) - - def test_vector_cartesian_xflip(self, tmpdir): - # Regression test for a bug that caused vectors to not be flipped - self.viewer.state.layers[0].vector_mode = 'Cartesian' - self.viewer.state.layers[0].vx_att = self.data.id['h'] - self.viewer.state.flip_x() - self._vector_common(tmpdir) - - def test_subset(self, tmpdir): - self.data_collection.new_subset_group('mysubset', self.data.id['a'] > 0.5) - self.assert_same(tmpdir) - - def test_density_map_with_subset(self, tmpdir): - self.viewer.state.dpi = 2 - self.viewer.state.layers[0].density_map = True - self.data_collection.new_subset_group('mysubset', self.data.id['a'] > 0.5) - self.assert_same(tmpdir) - - def test_density_map_cmap_with_subset(self, tmpdir): - self.viewer.state.dpi = 2 - self.viewer.state.layers[0].density_map = True - self.viewer.state.layers[0].cmap_mode = 'Linear' - self.viewer.state.layers[0].cmap_vmin = 0.2 - self.viewer.state.layers[0].cmap_vmax = 0.7 - self.viewer.state.layers[0].cmap = plt.cm.BuGn - self.data_collection.new_subset_group('mysubset', self.data.id['a'] > 0.5) - self.assert_same(tmpdir) - - def test_density_map_cmap_with_subset_legend(self, tmpdir): - self.viewer.state.legend.visible = True - self.viewer.state.dpi = 2 - self.viewer.state.layers[0].density_map = True - self.viewer.state.layers[0].cmap_mode = 'Linear' - self.viewer.state.layers[0].cmap_vmin = 0.2 - self.viewer.state.layers[0].cmap_vmax = 0.7 - self.viewer.state.layers[0].cmap = plt.cm.BuGn - self.data_collection.new_subset_group('mysubset', self.data.id['a'] > 0.5) - self.assert_same(tmpdir) - - def test_cmap_mode_change(self, tmpdir): - # Regression test for a bug that caused scatter markers to not change - # color when going from Linear to Fixed mode - self.viewer.state.layers[0].size_mode = 'Linear' - self.viewer.state.layers[0].cmap_mode = 'Linear' - self.viewer.state.layers[0].cmap_mode = 'Fixed' - self.assert_same(tmpdir) - - def test_density_map_change(self, tmpdir): - # Regression test for a bug that caused the density map to still - # be visible if using color-coding with the density map then - # switching to markers. - self.viewer.state.layers[0].density_map = True - self.viewer.state.layers[0].cmap_mode = 'Linear' - self.viewer.state.layers[0].cmap = plt.cm.BuGn - self.viewer.state.layers[0].density_map = False - self.assert_same(tmpdir) - - def test_simple_polar_plot_degrees(self, tmpdir): - self.viewer.state.plot_mode = 'polar' - self.viewer.state.angle_unit = 'degrees' - self.viewer.state.x_att = self.data.id['c'] - self.viewer.state.y_att = self.data.id['d'] - self.assert_same(tmpdir) - - def test_simple_polar_plot_radians(self, tmpdir): - self.viewer.state.plot_mode = 'polar' - self.viewer.state.angle_unit = 'radians' - self.viewer.state.x_att = self.data.id['c'] - self.viewer.state.y_att = self.data.id['d'] - self.assert_same(tmpdir) - - def _fullsphere_common_test(self, tmpdir): - # Note that all the full-sphere projections have the same bounds, - # so we can use the same sets of min/max values - x_bounds = self.viewer.state.x_min, self.viewer.state.x_max - y_bounds = self.viewer.state.y_min, self.viewer.state.y_max - for order in product([True, False], repeat=2): - self.viewer.state.x_min, self.viewer.state.x_max = sorted(x_bounds, reverse=order[0]) - self.viewer.state.y_min, self.viewer.state.y_max = sorted(y_bounds, reverse=order[1]) - self.viewer.state.plot_mode = 'aitoff' - self.viewer.state.x_att = self.data.id['c'] - self.viewer.state.y_att = self.data.id['d'] - self.assert_same(tmpdir) - self.viewer.state.plot_mode = 'hammer' - self.viewer.state.x_att = self.data.id['e'] - self.viewer.state.y_att = self.data.id['f'] - self.assert_same(tmpdir) - self.viewer.state.plot_mode = 'lambert' - self.viewer.state.x_att = self.data.id['g'] - self.viewer.state.y_att = self.data.id['h'] - self.assert_same(tmpdir) - self.viewer.state.plot_mode = 'mollweide' - self.viewer.state.x_att = self.data.id['a'] - self.viewer.state.y_att = self.data.id['b'] - self.assert_same(tmpdir) - - def test_full_sphere_degrees(self, tmpdir): - self.viewer.state.angle_unit = 'degrees' - self._fullsphere_common_test(tmpdir) - - def test_full_sphere_radians(self, tmpdir): - self.viewer.state.angle_unit = 'radians' - self._fullsphere_common_test(tmpdir) - - def test_cmap_size_noncartesian(self, tmpdir): - self.viewer.state.layers[0].size_mode = 'Linear' - self.viewer.state.layers[0].cmap_mode = 'Linear' - for proj in ['polar', 'aitoff', 'hammer', 'lambert', 'mollweide']: - self.viewer.state.plot_mode = proj - self.assert_same(tmpdir) - - def test_vectors_noncartesian(self, tmpdir): - for proj in ['polar', 'aitoff', 'hammer', 'lambert', 'mollweide']: - self.viewer.state.plot_mode = proj - self._vector_common(tmpdir) - - def test_errorbarxy_noncartesian(self, tmpdir): - self.viewer.state.layers[0].xerr_visible = True - self.viewer.state.layers[0].xerr_att = self.data.id['e'] - self.viewer.state.layers[0].yerr_visible = True - self.viewer.state.layers[0].yerr_att = self.data.id['f'] - self.viewer.state.layers[0].color = 'purple' - self.viewer.state.layers[0].alpha = 0.5 - for proj in ['polar', 'aitoff', 'hammer', 'lambert', 'mollweide']: - self.viewer.state.plot_mode = proj - self.assert_same(tmpdir) diff --git a/glue/viewers/scatter/state.py b/glue/viewers/scatter/state.py index 7c20aa83a..0ad007bb9 100644 --- a/glue/viewers/scatter/state.py +++ b/glue/viewers/scatter/state.py @@ -4,7 +4,7 @@ from glue.core import BaseData, Subset -from glue.config import colormaps +from glue.config import colormaps, stretches from glue.viewers.matplotlib.state import (MatplotlibDataViewerState, MatplotlibLayerState, DeferredDrawCallbackProperty as DDCProperty, @@ -327,13 +327,8 @@ def __init__(self, viewer_state=None, layer=None, **kwargs): ScatterLayerState.vector_origin.set_choices(self, ['tail', 'middle', 'tip']) ScatterLayerState.vector_origin.set_display_func(self, vector_origin_display.get) - stretch_display = {'linear': 'Linear', - 'sqrt': 'Square Root', - 'arcsinh': 'Arcsinh', - 'log': 'Logarithmic'} - ScatterLayerState.stretch.set_choices(self, ['linear', 'sqrt', 'arcsinh', 'log']) - ScatterLayerState.stretch.set_display_func(self, stretch_display.get) + ScatterLayerState.stretch.set_display_func(self, stretches.display_func) if self.viewer_state is not None: self.viewer_state.add_callback('x_att', self._on_xy_change, priority=10000) diff --git a/glue/viewers/table/qt/__init__.py b/glue/viewers/table/qt/__init__.py index 314de07c9..6220498c9 100644 --- a/glue/viewers/table/qt/__init__.py +++ b/glue/viewers/table/qt/__init__.py @@ -1,6 +1,4 @@ -from .data_viewer import TableViewer, TableLayerArtist # noqa - - -def setup(): - from glue.config import qt_client - qt_client.add(TableViewer) +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.viewers.table.qt is deprecated, use glue_qt.viewers.table instead', GlueDeprecationWarning) +from glue_qt.viewers.table import * # noqa diff --git a/glue/viewers/table/qt/data_viewer.py b/glue/viewers/table/qt/data_viewer.py index b347632d1..e54734d0b 100644 --- a/glue/viewers/table/qt/data_viewer.py +++ b/glue/viewers/table/qt/data_viewer.py @@ -1,405 +1,4 @@ -import os -from functools import lru_cache -import re - -import numpy as np - -from qtpy.QtCore import Qt -from qtpy import QtCore, QtGui, QtWidgets -from matplotlib.colors import ColorConverter - -from echo.qt import autoconnect_callbacks_to_qt -from glue.utils.qt import get_qapp -from glue.config import viewer_tool -from glue.core import BaseData, Data -from glue.utils.qt import load_ui -from glue.viewers.common.qt.data_viewer import DataViewer -from glue.viewers.common.qt.toolbar import BasicToolbar -from glue.viewers.common.tool import CheckableTool -from glue.viewers.common.layer_artist import LayerArtist -from glue.core.subset import ElementSubsetState -from glue.utils.colors import alpha_blend_colors -from glue.utils.qt import mpl_to_qt_color, messagebox_on_error -from glue.core.exceptions import IncompatibleAttribute -from glue.viewers.table.compat import update_table_viewer_state -from glue.viewers.table.state import TableViewerState - -try: - import dask.array as da - DASK_INSTALLED = True -except ImportError: - DASK_INSTALLED = False - -__all__ = ['TableViewer', 'TableLayerArtist'] - -COLOR_CONVERTER = ColorConverter() - - -class DataTableModel(QtCore.QAbstractTableModel): - - def __init__(self, table_viewer): - super(DataTableModel, self).__init__() - if table_viewer.data.ndim != 1: - raise ValueError("Can only use Table widget for 1D data") - self._table_viewer = table_viewer - self._data = table_viewer.data - self._state = table_viewer.state - self.filter_mask = None - self.show_coords = False - self.order = np.arange(self._data.shape[0]) - self._update_visible() - - def data_changed(self): - top_left = self.index(0, 0) - bottom_right = self.index(self.columnCount(), self.rowCount()) - self._update_visible() - self.data_by_row_and_column.cache_clear() - self.dataChanged.emit(top_left, bottom_right) - self.layoutChanged.emit() - - @property - def columns(self): - if self.show_coords: - return self._data.components - else: - return self._data.main_components + self._data.derived_components - - def columnCount(self, index=None): - return len(self.columns) - - def rowCount(self, index=None): - # Qt bug: Crashes on tables bigger than this - return min(self.order_visible.size, 71582788) - - def headerData(self, section, orientation, role): - if role != Qt.DisplayRole: - return None - - if orientation == Qt.Horizontal: - column_name = self.columns[section].label - units = self._data.get_component(self.columns[section]).units - if units is not None and units != '': - column_name += "\n{0}".format(units) - return column_name - elif orientation == Qt.Vertical: - return str(self.order_visible[section]) - - def data(self, index, role): - - if not index.isValid(): - return None - - return self.data_by_row_and_column(index.row(), index.column(), role) - - # The data() method gets called many times, often with the same parameters, - # for example if bringing the window to the foreground/background, shifting - # up/down/left/right by one cell, etc. This can be very slow when e.g. dask - # columns are present so we cache the most recent 65536 calls which should - # have a reasonably sensible memory footprint. - @lru_cache(maxsize=65536) - def data_by_row_and_column(self, row, column, role): - - if role == Qt.DisplayRole: - - c = self.columns[column] - idx = self.order_visible[row] - comp = self._data[c] - value = comp[idx] - if isinstance(value, bytes): - return value.decode('ascii') - else: - if DASK_INSTALLED and isinstance(value, da.Array): - return str(value.compute()) - else: - return str(comp[idx]) - - elif role == Qt.BackgroundRole: - - idx = self.order_visible[row] - - # Find all subsets that this index is part of - colors = [] - for layer_artist in self._table_viewer.layers[::-1]: - if isinstance(layer_artist.layer, BaseData): - continue - if layer_artist.visible: - subset = layer_artist.layer - try: - if subset.to_mask(view=slice(idx, idx + 1))[0]: - colors.append(subset.style.color) - except IncompatibleAttribute as exc: - # Only disable the layer if enabled, as otherwise we - # will recursively call clear and _refresh, causing - # an infinite loop and performance issues. - # Also make sure that a disabled layer is not visible - if layer_artist.enabled: - layer_artist.disable_invalid_attributes(*exc.args) - layer_artist.visible = False - else: - layer_artist.enabled = True - - # Blend the colors using alpha blending - if len(colors) > 0: - color = alpha_blend_colors(colors, additional_alpha=0.5) - color = mpl_to_qt_color(color) - return QtGui.QBrush(color) - - def sort(self, column, ascending): - c = self.columns[column] - comp = self._data.get_component(c) - self.order = np.argsort(comp.data) - if ascending == Qt.DescendingOrder: - self.order = self.order[::-1] - self._update_visible() - self.data_by_row_and_column.cache_clear() - self.layoutChanged.emit() - - def get_filter_mask(self): - if (self._state.filter is None) or (self._state.filter_att is None): - self.filter_mask = np.ones(self.order.shape, dtype=bool) - return - comp = self._data.get_component(self._state.filter_att) - - if self._state.regex: - p = re.compile(self._state.filter) - self.filter_mask = np.array([bool(p.search(x)) for x in comp.data]) - else: - self.filter_mask = np.array([self._state.filter in x for x in comp.data]) - self.data_changed() # This might be overkill - - def _update_visible(self): - """ - Given which layers are visible or not, convert order to order_visible - after applying the current filter_mask - """ - - self.data_by_row_and_column.cache_clear() - - # First, if the data layer is visible, show all rows - for layer_artist in self._table_viewer.layers: - if layer_artist.visible and isinstance(layer_artist.layer, BaseData): - if self.filter_mask is None: - self.order_visible = self.order - return - else: - mask = self.filter_mask[self.order] - self.order_visible = self.order[mask] - return - - # If not then we need to show only the rows with visible subsets - visible = np.zeros(self.order.shape, dtype=bool) - for layer_artist in self._table_viewer.layers: - if layer_artist.visible: - mask = layer_artist.layer.to_mask()[self.order] - if DASK_INSTALLED and isinstance(mask, da.Array): - mask = mask.compute() - visible |= mask - if self.filter_mask is not None: - visible &= self.filter_mask[self.order] - - self.order_visible = self.order[visible] - - -class TableLayerArtist(LayerArtist): - - def __init__(self, table_viewer, viewer_state, layer_state=None, layer=None): - self._table_viewer = table_viewer - super(TableLayerArtist, self).__init__(viewer_state, - layer_state=layer_state, - layer=layer) - self.redraw() - - def _refresh(self): - self._table_viewer.model.data_changed() - - def redraw(self): - self._refresh() - - def update(self): - self._refresh() - - def clear(self): - self._refresh() - - -@viewer_tool -class RowSelectTool(CheckableTool): - - tool_id = 'table:rowselect' - icon = 'glue_row_select' - action_text = 'Select row(s)' - tool_tip = ('Select rows by clicking on rows and pressing enter ' - 'once the selection is ready to be applied') - status_tip = ('CLICK to select, press ENTER to finalize selection, ' - 'ALT+CLICK or ALT+UP/DOWN to apply selection immediately') - - def __init__(self, viewer): - super(RowSelectTool, self).__init__(viewer) - self.deactivate() - - def activate(self): - self.viewer.ui.table.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection) - - def deactivate(self, block=False): - # Don't do anything if the viewer has already been closed - if self.viewer is None: - return - self.viewer.ui.table.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection) - self.viewer.ui.table.clearSelection() - - -class TableViewWithSelectionSignal(QtWidgets.QTableView): - """ - This is the TableViewer.ui.table object - """ - selection_changed = QtCore.Signal() - - def selectionChanged(self, *args, **kwargs): - self.selection_changed.emit() - super(TableViewWithSelectionSignal, self).selectionChanged(*args, **kwargs) - - -class TableViewer(DataViewer): - - LABEL = "Table Viewer" - - _toolbar_cls = BasicToolbar - _data_artist_cls = TableLayerArtist - _subset_artist_cls = TableLayerArtist - _state_cls = TableViewerState - - inherit_tools = False - tools = ['table:rowselect', 'window'] - subtools = { - 'window': ['window:movetab', 'window:title'] - } - - def __init__(self, session, state=None, parent=None, widget=None): - - super(TableViewer, self).__init__(session, state=state, parent=parent) - - self.ui = load_ui('data_viewer.ui', - directory=os.path.dirname(__file__)) - self.setCentralWidget(self.ui) - - hdr = self.ui.table.horizontalHeader() - hdr.setStretchLastSection(True) - hdr.setSectionResizeMode(hdr.Interactive) - - hdr = self.ui.table.verticalHeader() - hdr.setSectionResizeMode(hdr.Interactive) - - self.data = None - self.model = None - - self._connections = autoconnect_callbacks_to_qt(self.state, self.ui) - - self.state.add_callback('regex', self._on_filter_changed) - self.state.add_callback('filter', self._on_filter_changed) - self.state.add_callback('filter_att', self._on_filter_changed) - - self.ui.table.selection_changed.connect(self.selection_changed) - self.state.add_callback('layers', self._on_layers_changed) - self._on_layers_changed() - - def selection_changed(self): - app = get_qapp() - if app.queryKeyboardModifiers() == Qt.AltModifier: - self.finalize_selection(clear=False) - - def keyPressEvent(self, event): - if self.toolbar.active_tool is self.toolbar.tools['table:rowselect']: - if event.key() in [Qt.Key_Enter, Qt.Key_Return]: - self.finalize_selection() - super(TableViewer, self).keyPressEvent(event) - - def finalize_selection(self, clear=True): - model = self.ui.table.selectionModel() - selected_rows = [self.model.order_visible[x.row()] for x in model.selectedRows()] - subset_state = ElementSubsetState(indices=selected_rows, data=self.data) - mode = self.session.edit_subset_mode - mode.update(self._data, subset_state, focus_data=self.data) - if clear: - # We block the signals here to make sure that we don't update - # the subset again once the selection is cleared. - self.ui.table.blockSignals(True) - self.ui.table.clearSelection() - self.ui.table.blockSignals(False) - - def _on_filter_changed(self, *args): - # If we change the filter we deactivate the toolbar to keep - # any subset defined before we change what is displayed - if self.toolbar.active_tool is self.toolbar.tools['table:rowselect']: - old_tool = self.toolbar.active_tool - old_tool.deactivate(block=True) - button = self.toolbar.actions[old_tool.tool_id] - if button.isChecked(): - button.setChecked(False) - if self.model: - self.model.get_filter_mask() - - def _on_layers_changed(self, *args): - for layer_state in self.state.layers: - if isinstance(layer_state.layer, BaseData): - break - else: - return - - # If we aren't changing the data layer, we don't need to - # reset the model, just update visible rows - if layer_state.layer == self.data: - self.model._update_visible() - return - - self.data = layer_state.layer - - self.setUpdatesEnabled(False) - self.model = DataTableModel(self) - self.model.get_filter_mask() - - self.ui.table.setModel(self.model) - self.setUpdatesEnabled(True) - - @messagebox_on_error("Failed to add data") - def add_data(self, data): - with self._layer_artist_container.ignore_empty(): - self.state.layers[:] = [] - return super(TableViewer, self).add_data(data) - - @messagebox_on_error("Failed to add subset") - def add_subset(self, subset): - - if self.data is None: - self.add_data(subset.data) - self.state.layers[0].visible = False - elif subset.data != self.data: - raise ValueError("subset parent data does not match existing table data") - - return super(TableViewer, self).add_subset(subset) - - @property - def window_title(self): - if self.state.title: - return self.state.title - elif len(self.state.layers) > 0: - return 'Table: ' + self.state.layers[0].layer.label - else: - return 'Table' - - def closeEvent(self, event): - """ - On close, Qt seems to scan through the entire model - if the data set is big. To sidestep that, - we swap out with a tiny data set before closing - """ - super(TableViewer, self).closeEvent(event) - if self.model is not None: - self.model._data = Data(x=[0]) - event.accept() - - def get_layer_artist(self, cls, layer=None, layer_state=None): - return cls(self, self.state, layer=layer, layer_state=layer_state) - - @staticmethod - def update_viewer_state(rec, context): - return update_table_viewer_state(rec, context) +import warnings +from glue.utils.error import GlueDeprecationWarning +warnings.warn('Importing from glue.viewers.table.qt.data_viewer is deprecated, use glue_qt.viewers.table.data_viewer instead', GlueDeprecationWarning) +from glue_qt.viewers.table.data_viewer import * # noqa diff --git a/glue/viewers/table/qt/data_viewer.ui b/glue/viewers/table/qt/data_viewer.ui deleted file mode 100644 index 629809f2c..000000000 --- a/glue/viewers/table/qt/data_viewer.ui +++ /dev/null @@ -1,104 +0,0 @@ - - - Form - - - - 0 - 0 - 742 - 612 - - - - Form - - - - - - - - Qt::Horizontal - - - - 60 - 20 - - - - - - - - Search: - - - - - - - - - - in - - - - - - - - - - Use regex - - - - - - - - - - - QAbstractItemView::NoEditTriggers - - - QAbstractItemView::ExtendedSelection - - - QAbstractItemView::SelectRows - - - true - - - 107 - - - false - - - true - - - false - - - - - - - - - - TableViewWithSelectionSignal - QTableView -
glue.viewers.table.qt.data_viewer
-
-
- - -
diff --git a/glue/viewers/table/qt/tests/__init__.py b/glue/viewers/table/qt/tests/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/glue/viewers/table/qt/tests/test_data_viewer.py b/glue/viewers/table/qt/tests/test_data_viewer.py deleted file mode 100644 index 0c2a88a13..000000000 --- a/glue/viewers/table/qt/tests/test_data_viewer.py +++ /dev/null @@ -1,736 +0,0 @@ -import pytest -import numpy as np -from unittest.mock import MagicMock, patch - -from qtpy import QtCore, QtGui -from qtpy.QtCore import Qt - -from glue.utils.qt import get_qapp, process_events -from glue.core import Data, DataCollection, BaseData -from glue.utils.qt import qt_to_mpl_color -from glue.app.qt import GlueApplication - -from ..data_viewer import DataTableModel, TableViewer - -from glue.core.edit_subset_mode import AndNotMode, OrMode, ReplaceMode -from glue.tests.helpers import requires_pyqt_gt_59_or_pyside2 - - -class TestDataTableModel(): - - def setup_method(self, method): - self.gapp = GlueApplication() - self.viewer = self.gapp.new_data_viewer(TableViewer) - self.data = Data(x=[1, 2, 3, 4], y=[2, 3, 4, 5]) - self.gapp.data_collection.append(self.data) - self.viewer.add_data(self.data) - self.model = DataTableModel(self.viewer) - - def teardown_method(self, method): - self.gapp.close() - self.gapp = None - - def test_column_count(self): - assert self.model.columnCount() == 2 - - def test_column_count_hidden(self): - self.model.show_coords = True - assert self.model.columnCount() == 3 - - def test_header_data(self): - for i, c in enumerate(self.data.main_components): - result = self.model.headerData(i, Qt.Horizontal, Qt.DisplayRole) - assert result == c.label - - for i in range(self.data.size): - result = self.model.headerData(i, Qt.Vertical, Qt.DisplayRole) - assert result == str(i) - - def test_row_count(self): - assert self.model.rowCount() == 4 - - def test_data(self): - for i, c in enumerate(self.data.main_components): - for j in range(self.data.size): - idx = self.model.index(j, i) - result = self.model.data(idx, Qt.DisplayRole) - assert float(result) == self.data[c, j] - - @pytest.mark.xfail - def test_data_2d(self): - self.data = Data(x=[[1, 2], [3, 4]], y=[[2, 3], [4, 5]]) - self.model = DataTableModel(self.data) - for i, c in enumerate(self.data.main_components): - for j in range(self.data.size): - idx = self.model.index(j, i) - result = self.model.data(idx, Qt.DisplayRole) - assert float(result) == self.data[c].ravel()[j] - - -def check_values_and_color(model, data, colors): - - for i in range(len(colors)): - - for j, colname in enumerate(sorted(data)): - - # Get index of cell - idx = model.index(i, j) - - # Check values - value = model.data(idx, Qt.DisplayRole) - assert value == str(data[colname][i]) - - # Check colors - brush = model.data(idx, Qt.BackgroundRole) - if colors[i] is None: - assert brush is None - else: - assert qt_to_mpl_color(brush.color()) == colors[i] - - -def test_table_widget(tmpdir): - - # Start off by creating a glue application instance with a table viewer and - # some data pre-loaded. - - app = get_qapp() - - d = Data(a=[1, 2, 3, 4, 5], - b=[3.2, 1.2, 4.5, 3.3, 2.2], - c=['e', 'b', 'c', 'a', 'f']) - - dc = DataCollection([d]) - - gapp = GlueApplication(dc) - - widget = gapp.new_data_viewer(TableViewer) - widget.add_data(d) - - subset_mode = gapp._session.edit_subset_mode - - # Create two subsets - - sg1 = dc.new_subset_group('D <= 3', d.id['a'] <= 3) - sg1.style.color = '#aa0000' - sg2 = dc.new_subset_group('1 < D < 4', (d.id['a'] > 1) & (d.id['a'] < 4)) - sg2.style.color = '#0000cc' - - model = widget.ui.table.model() - - # We now check what the data and colors of the table are, and try various - # sorting methods to make sure that things are still correct. - - data = {'a': [1, 2, 3, 4, 5], - 'b': [3.2, 1.2, 4.5, 3.3, 2.2], - 'c': ['e', 'b', 'c', 'a', 'f']} - - colors = ['#aa0000', '#380088', '#380088', None, None] - - check_values_and_color(model, data, colors) - - model.sort(1, Qt.AscendingOrder) - - data = {'a': [2, 5, 1, 4, 3], - 'b': [1.2, 2.2, 3.2, 3.3, 4.5], - 'c': ['b', 'f', 'e', 'a', 'c']} - - colors = ['#380088', None, '#aa0000', None, '#380088'] - - check_values_and_color(model, data, colors) - - model.sort(2, Qt.AscendingOrder) - - data = {'a': [4, 2, 3, 1, 5], - 'b': [3.3, 1.2, 4.5, 3.2, 2.2], - 'c': ['a', 'b', 'c', 'e', 'f']} - - colors = [None, '#380088', '#380088', '#aa0000', None] - - check_values_and_color(model, data, colors) - - model.sort(0, Qt.DescendingOrder) - - data = {'a': [5, 4, 3, 2, 1], - 'b': [2.2, 3.3, 4.5, 1.2, 3.2], - 'c': ['f', 'a', 'c', 'b', 'e']} - - colors = [None, None, '#380088', '#380088', '#aa0000'] - - check_values_and_color(model, data, colors) - - model.sort(0, Qt.AscendingOrder) - - # We now modify the subsets using the table. - - selection = widget.ui.table.selectionModel() - - widget.toolbar.actions['table:rowselect'].toggle() - - def press_key(key): - event = QtGui.QKeyEvent(QtCore.QEvent.KeyPress, key, Qt.NoModifier) - app.postEvent(widget.ui.table, event) - app.processEvents() - - process_events() - - # We now use key presses to navigate down to the third row - - press_key(Qt.Key_Tab) - press_key(Qt.Key_Down) - press_key(Qt.Key_Down) - - process_events() - - indices = selection.selectedRows() - - # We make sure that the third row is selected - - assert len(indices) == 1 - assert indices[0].row() == 2 - - # At this point, the subsets haven't changed yet - - np.testing.assert_equal(d.subsets[0].to_mask(), [1, 1, 1, 0, 0]) - np.testing.assert_equal(d.subsets[1].to_mask(), [0, 1, 1, 0, 0]) - - # We specify that we are editing the second subset, and use a 'not' logical - # operation to remove the currently selected line from the second subset. - - subset_mode.edit_subset = [d.subsets[1]] - subset_mode.mode = AndNotMode - - press_key(Qt.Key_Enter) - - np.testing.assert_equal(d.subsets[0].to_mask(), [1, 1, 1, 0, 0]) - np.testing.assert_equal(d.subsets[1].to_mask(), [0, 1, 0, 0, 0]) - - # At this point, the selection should be cleared - - indices = selection.selectedRows() - assert len(indices) == 0 - - # We move to the fourth row and now do an 'or' selection with the first - # subset. - - press_key(Qt.Key_Down) - - subset_mode.mode = OrMode - - subset_mode.edit_subset = [d.subsets[0]] - - press_key(Qt.Key_Enter) - - np.testing.assert_equal(d.subsets[0].to_mask(), [1, 1, 1, 1, 0]) - np.testing.assert_equal(d.subsets[1].to_mask(), [0, 1, 0, 0, 0]) - - # Finally we move to the fifth row and deselect all subsets so that - # pressing enter now creates a new subset. - - press_key(Qt.Key_Down) - - subset_mode.mode = ReplaceMode - - subset_mode.edit_subset = None - - press_key(Qt.Key_Enter) - - np.testing.assert_equal(d.subsets[0].to_mask(), [1, 1, 1, 1, 0]) - np.testing.assert_equal(d.subsets[1].to_mask(), [0, 1, 0, 0, 0]) - np.testing.assert_equal(d.subsets[2].to_mask(), [0, 0, 0, 0, 1]) - - # Make the color for the new subset deterministic - dc.subset_groups[2].style.color = '#bababa' - - # Now finally check saving and restoring session - - session_file = tmpdir.join('table.glu').strpath - - gapp.save_session(session_file) - - gapp2 = GlueApplication.restore_session(session_file) - gapp2.show() - - d = gapp2.data_collection[0] - - widget2 = gapp2.viewers[0][0] - - model2 = widget2.ui.table.model() - - data = {'a': [1, 2, 3, 4, 5], - 'b': [3.2, 1.2, 4.5, 3.3, 2.2], - 'c': ['e', 'b', 'c', 'a', 'f']} - - # Need to take into account new selections above - colors = ['#aa0000', '#380088', '#aa0000', "#aa0000", "#bababa"] - - check_values_and_color(model2, data, colors) - - -def test_table_widget_session_no_subset(tmpdir): - - # Regression test for a bug that caused table viewers with no subsets to - # not be restored correctly and instead raise an exception. - - app = get_qapp() # noqa - - d = Data(a=[1, 2, 3, 4, 5], - b=[3.2, 1.2, 4.5, 3.3, 2.2], - c=['e', 'b', 'c', 'a', 'f'], label='test') - - dc = DataCollection([d]) - - gapp = GlueApplication(dc) - - widget = gapp.new_data_viewer(TableViewer) - widget.add_data(d) - - session_file = tmpdir.join('table.glu').strpath - - gapp.save_session(session_file) - - gapp2 = GlueApplication.restore_session(session_file) - gapp2.show() - - gapp2.data_collection[0] - gapp2.viewers[0][0] - - -def test_change_components(): - - # Regression test for a bug that caused table viewers to not update when - # adding/removing components. - - app = get_qapp() # noqa - - d = Data(a=[1, 2, 3, 4, 5], - b=[3.2, 1.2, 4.5, 3.3, 2.2], - c=['e', 'b', 'c', 'a', 'f'], label='test') - - dc = DataCollection([d]) - - gapp = GlueApplication(dc) - - viewer = gapp.new_data_viewer(TableViewer) - viewer.add_data(d) - - data_changed = MagicMock() - viewer.model.dataChanged.connect(data_changed) - - # layoutChanged needs to be emitted for the new/removed columns to be - # registered (dataChanged is not enough) - layout_changed = MagicMock() - viewer.model.layoutChanged.connect(layout_changed) - - assert data_changed.call_count == 0 - assert layout_changed.call_count == 0 - viewer.model.columnCount() == 2 - - d.add_component([3, 4, 5, 6, 2], 'z') - - assert data_changed.call_count == 1 - assert layout_changed.call_count == 1 - viewer.model.columnCount() == 3 - - d.remove_component(d.id['z']) - - assert data_changed.call_count == 2 - assert layout_changed.call_count == 2 - viewer.model.columnCount() == 2 - - -def test_table_title(): - - app = get_qapp() # noqa - - data1 = Data(a=[1, 2, 3, 4, 5], label='test1') - data2 = Data(a=[1, 2, 3, 4, 5], label='test2') - - dc = DataCollection([data1, data2]) - - gapp = GlueApplication(dc) - - viewer = gapp.new_data_viewer(TableViewer) - - assert viewer.windowTitle() == 'Table' - - viewer.add_data(data1) - - assert viewer.windowTitle() == 'Table: test1' - - viewer.add_data(data2) - - assert viewer.windowTitle() == 'Table: test2' - - -def test_add_subset(): - - # Regression test for a bug that occurred when adding a subset - # directly to the table viewer. - - data1 = Data(a=[1, 2, 3, 4, 5], label='test1') - data2 = Data(a=[1, 2, 3, 4, 5], label='test2') - dc = DataCollection([data1, data2]) - dc.new_subset_group('test subset 1', data1.id['a'] > 2) - - gapp = GlueApplication(dc) - - viewer = gapp.new_data_viewer(TableViewer) - viewer.add_subset(data1.subsets[0]) - - assert len(viewer.state.layers) == 2 - assert not viewer.state.layers[0].visible - assert viewer.state.layers[1].visible - - dc.new_subset_group('test subset 2', data1.id['a'] <= 2) - - assert len(viewer.state.layers) == 3 - assert not viewer.state.layers[0].visible - assert viewer.state.layers[1].visible - assert viewer.state.layers[2].visible - - viewer.remove_subset(data1.subsets[1]) - - assert len(viewer.state.layers) == 2 - assert not viewer.state.layers[0].visible - assert viewer.state.layers[1].visible - - viewer.add_subset(data1.subsets[1]) - - assert len(viewer.state.layers) == 3 - assert not viewer.state.layers[0].visible - assert viewer.state.layers[1].visible - assert viewer.state.layers[2].visible - - with pytest.raises(ValueError) as exc: - viewer.add_subset(data2.subsets[1]) - assert exc.value.args[0] == 'subset parent data does not match existing table data' - - -def test_graceful_close_after_invalid(capsys): - - # Regression test for a bug that caused an error if an invalid dataset - # was added to the viewer after the user had acknowledged the error. - - d = Data(a=[[1, 2], [3, 4]], label='test') - - dc = DataCollection([d]) - - gapp = GlueApplication(dc) - - viewer = gapp.new_data_viewer(TableViewer) - gapp.show() - - process_events() - - with pytest.raises(ValueError, match='Can only use Table widget for 1D data'): - viewer.add_data(d) - - viewer.close() - - process_events() - - # We use capsys here because the # error is otherwise only apparent in stderr. - out, err = capsys.readouterr() - assert out.strip() == "" - assert err.strip() == "" - - -def test_incompatible_subset(): - - # Regression test for a bug that caused the table to be refreshed in an - # infinite loop if incompatible subsets were present. - - data1 = Data(a=[1, 2, 3, 4, 5], label='test1') - data2 = Data(a=[1, 2, 3, 4, 5], label='test2') - dc = DataCollection([data1, data2]) - - gapp = GlueApplication(dc) - - viewer = gapp.new_data_viewer(TableViewer) - viewer.add_data(data1) - - dc.new_subset_group('test subset', data2.id['a'] > 2) - gapp.show() - process_events(0.5) - - with patch.object(viewer.layers[0], '_refresh') as refresh1: - with patch.object(viewer.layers[1], '_refresh') as refresh2: - process_events(0.5) - - assert refresh1.call_count == 0 - assert refresh2.call_count == 0 - - -@requires_pyqt_gt_59_or_pyside2 -def test_table_incompatible_attribute(): - """ - Regression test for a bug where the table viewer generates an - uncaught IncompatibleAttribute error in _update_visible() if - the dataset is not visible and an invalid subset exists at all. - This occurred because layer_artists depending on - invalid attributes were only being disabled (but were still - visible) and the viewer attempts to compute a mask for - all visible subsets if the underlying dataset is not visible. - """ - app = get_qapp() - d1 = Data(x=[1, 2, 3, 4], y=[5, 6, 7, 8], label='d1') - d2 = Data(a=['a', 'b', 'c'], b=['x', 'y', 'z'], label='d2') - dc = DataCollection([d1, d2]) - gapp = GlueApplication(dc) - viewer = gapp.new_data_viewer(TableViewer) - viewer.add_data(d2) - - # This subset should not be shown in the viewer - sg1 = dc.new_subset_group('invalid', d1.id['x'] <= 3) - - gapp.show() - process_events() - - assert len(viewer.layers) == 2 - assert not viewer.layers[1].visible - assert viewer.layers[0].visible - - # This subset can be shown in the viewer - sg2 = dc.new_subset_group('valid', d2.id['a'] == 'a') - - assert len(viewer.layers) == 3 - assert viewer.layers[2].visible - assert not viewer.layers[1].visible - assert viewer.layers[0].visible - - # The original IncompatibleAttribute was thrown - # here as making the data layer invisible made - # DataTableModel._update_visible() try and draw - # the invalid subset - viewer.layers[0].visible = False - assert viewer.layers[2].visible - assert not viewer.layers[1].visible - - -def test_table_with_dask_column(): - - da = pytest.importorskip('dask.array') - - app = get_qapp() - - d = Data(d=da.asarray([1, 2, 3, 4, 5]), - e=np.arange(5) + 1) - - dc = DataCollection([d]) - - gapp = GlueApplication(dc) - - widget = gapp.new_data_viewer(TableViewer) - widget.add_data(d) - - sg1 = dc.new_subset_group('D <= 3', d.id['d'] <= 3) - sg1.style.color = '#aa0000' - sg2 = dc.new_subset_group('1 < E < 4', (d.id['e'] > 1) & (d.id['e'] < 4)) - sg2.style.color = '#0000cc' - - assert widget.state.layers[0].visible - assert widget.state.layers[1].visible - assert widget.state.layers[2].visible - - model = widget.ui.table.model() - - # We now check what the data and colors of the table are, and try various - # sorting methods to make sure that things are still correct. - - data = {'d': [1, 2, 3, 4, 5], - 'e': [1, 2, 3, 4, 5]} - - colors = ['#aa0000', '#380088', '#380088', None, None] - - check_values_and_color(model, data, colors) - - widget.state.layers[2].visible = False - - colors = ['#aa0000', '#aa0000', '#aa0000', None, None] - - check_values_and_color(model, data, colors) - - -def test_table_preserve_model_after_selection(): - - # Regression test for a bug that caused table viewers to return - # to default sorting after a new subset was created with the row - # selection tool. This occurred because the model was reset. - - app = get_qapp() # noqa - - d = Data(a=[1, 2, 3, 4, 5], - b=[3.2, 1.2, 4.5, 3.3, 2.2], - c=['e', 'b', 'c', 'a', 'f'], label='test') - - dc = DataCollection([d]) - - gapp = GlueApplication(dc) - - viewer = gapp.new_data_viewer(TableViewer) - viewer.add_data(d) - - model = viewer.ui.table.model() - - model.sort(1, Qt.AscendingOrder) - - data = {'a': [2, 5, 1, 4, 3], - 'b': [1.2, 2.2, 3.2, 3.3, 4.5], - 'c': ['b', 'f', 'e', 'a', 'c']} - colors = [None for _ in range(5)] - - check_values_and_color(model, data, colors) - - # Create a new subset using the row selection tool - - subset_mode = gapp._session.edit_subset_mode - subset_mode.edit_subset = None - viewer.toolbar.actions['table:rowselect'].toggle() - - def press_key(key): - event = QtGui.QKeyEvent(QtCore.QEvent.KeyPress, key, Qt.NoModifier) - app.postEvent(viewer.ui.table, event) - app.processEvents() - - # Select the second row - press_key(Qt.Key_Tab) - press_key(Qt.Key_Down) - press_key(Qt.Key_Enter) - - process_events() - - # Check that the table model is still the same, which it - # should be since we aren't changing the viewer Data - - post_model = viewer.ui.table.model() - assert post_model == model - - # Check that the order is still the same - - color = d.subsets[0].style.color - colors[1] = color - - check_values_and_color(post_model, data, colors) - - -def test_table_widget_filter(tmpdir): - - # Test table interactions with filtering - - app = get_qapp() # noqa - - d = Data(a=[1, 2, 3, 4, 5], - b=['cat', 'dog', 'cat', 'dog', 'fish'], - c=['fluffy', 'rover', 'fluffball', 'spot', 'moby'], label='test') - - dc = DataCollection([d]) - - gapp = GlueApplication(dc) - - widget = gapp.new_data_viewer(TableViewer) - widget.add_data(d) - - widget.state.filter_att = d.components[2] - - widget.state.filter = 'cat' - model = widget.ui.table.model() - - np.testing.assert_equal(model.filter_mask, [True, False, True, False, False]) - - widget.state.filter_att = d.components[3] - widget.state.filter = 'ff' - np.testing.assert_equal(model.filter_mask, [True, False, True, False, False]) - - # Test matching regular expressions - widget.state.filter_att = d.components[3] - widget.state.regex = True - - widget.state.filter = '^[a-z]{1}o' - np.testing.assert_equal(model.filter_mask, [False, True, False, False, True]) - - sg1 = dc.new_subset_group('test subset 1', d.id['a'] > 2) - sg1.style.color = '#aa0000' - data = {'a': [2, 5], - 'b': ['dog', 'fish'], - 'c': ['rover', 'moby']} - - colors = [None, '#aa0000'] - - check_values_and_color(model, data, colors) - - widget.state.regex = False - widget.state.filter = '^[a-z]{1}o' - np.testing.assert_equal(model.filter_mask, [False, False, False, False, False]) - - # Check that changing the filter disables the rowselect tool - widget.toolbar.actions['table:rowselect'].toggle() - process_events() - widget.state.filter_att = d.components[2] - assert widget.toolbar.active_tool is None - - # Check that disabling the full dataset means we only see the subset - # This is a regression test since filtering originally broke this. - widget.state.filter = '' - for layer_artist in widget.layers: - if layer_artist.visible and isinstance(layer_artist.layer, BaseData): - layer_artist.visible = False - - data = {'a': [3, 4, 5], - 'b': ['cat', 'dog', 'fish'], - 'c': ['fluffball', 'spot', 'moby']} - - colors = ['#aa0000', '#aa0000', '#aa0000'] - - check_values_and_color(model, data, colors) - - widget.state.filter_att = d.components[2] - widget.state.filter = 'cat' - - data = {'a': [3], - 'b': ['cat'], - 'c': ['fluffball']} - - colors = ['#aa0000'] - - check_values_and_color(model, data, colors) - - -def test_table_widget_session_filter(tmpdir): - - # Test that filtering works with save/restore - - app = get_qapp() # noqa - - d = Data(a=[1, 2, 3, 4, 5], - b=['cat', 'dog', 'cat', 'dog', 'fish'], - c=['fluffy', 'rover', 'fluffball', 'spot', 'moby'], label='test') - - dc = DataCollection([d]) - - gapp = GlueApplication(dc) - - widget = gapp.new_data_viewer(TableViewer) - widget.add_data(d) - - widget.state.filter_att = d.components[2] - widget.state.filter = 'cat' - model = widget.ui.table.model() - - np.testing.assert_equal(model.filter_mask, [True, False, True, False, False]) - - session_file = tmpdir.join('table.glu').strpath - - gapp.save_session(session_file) - - gapp2 = GlueApplication.restore_session(session_file) - gapp2.show() - - d = gapp2.data_collection[0] - - widget2 = gapp2.viewers[0][0] - - model2 = widget2.ui.table.model() - - assert widget2.state.filter_att == d.components[2] - assert widget2.state.filter == 'cat' - np.testing.assert_equal(model2.filter_mask, [True, False, True, False, False]) diff --git a/glueviz.desktop b/glueviz.desktop deleted file mode 100644 index d6472e50e..000000000 --- a/glueviz.desktop +++ /dev/null @@ -1,7 +0,0 @@ -[Desktop Entry] -Type=Application -Name=Glueviz -Comment=Link visualizations of scientific datasets -Exec=glue -Icon=glueviz -Categories=Education;Science; diff --git a/glueviz.png b/glueviz.png deleted file mode 100644 index 4c8e7cede..000000000 Binary files a/glueviz.png and /dev/null differ diff --git a/setup.cfg b/setup.cfg index 0a8f1edda..c94696d1b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -13,13 +13,9 @@ classifiers = Programming Language :: Python :: 3.10 Topic :: Scientific/Engineering :: Visualization License :: OSI Approved :: BSD License -description = Multidimensional data visualization across files +description = Core library for the glue multidimensional data visualization project long_description = file: README.rst -# NOTE: below we have to exclude ipykernel 5.0.0 and 5.1.0 below due to a bug -# that caused issues in the IPython terminal. For more details: -# https://github.com/ipython/ipykernel/pull/376 - [options] zip_safe = False packages = find: @@ -33,10 +29,7 @@ install_requires = echo>=0.6 astropy>=4.0 setuptools>=30.3.0 - qtpy>=1.9 ipython>=4.0 - ipykernel>=4.0,!=5.0.0,!=5.1.0 - qtconsole>=4.3,!=5.4.2 dill>=0.2 h5py>=2.10; python_version<'3.11' xlrd>=1.2 @@ -45,29 +38,22 @@ install_requires = pvextractor>=0.2 importlib_resources>=1.3; python_version<'3.9' importlib_metadata>=3.6; python_version<'3.10' + # For now, we include a dependency on glue-qt so that imports of the + # Qt-related functionality continue to work albeit with a deprecation + # warning. Once the deprecation phase is over, we can remove this + # dependency as well as all the compatibility imports. + glue-qt>=0.1.0 [options.entry_points] glue.plugins = - export_d3po = glue.plugins.export_d3po:setup - pv_slicer = glue.plugins.tools.pv_slicer.qt:setup coordinate_helpers = glue.plugins.coordinate_helpers:setup wcs_autolinking = glue.plugins.wcs_autolinking:setup dendro_factory = glue.plugins.dendro_viewer:setup - dendro_viewer = glue.plugins.dendro_viewer.qt:setup - image_viewer = glue.viewers.image.qt:setup - scatter_viewer = glue.viewers.scatter.qt:setup - histogram_viewer = glue.viewers.histogram.qt:setup - profile_viewer = glue.viewers.profile.qt:setup - table_viewer = glue.viewers.table.qt:setup data_exporters = glue.core.data_exporters:setup fits_format = glue.io.formats.fits:setup export_python = glue.plugins.tools:setup - directory_importer = glue.io.qt.directory_importer:setup console_scripts = glue-config = glue.config_gen:main - glue-deps = glue._deps:main -gui_scripts = - glue = glue.main:main [options.extras_require] all = @@ -81,19 +67,17 @@ all = # for why we exclude pillow 7.1.0 pillow!=7.1.0 docs = - sphinx + sphinx<7 sphinx-automodapi sphinxcontrib-spelling numpydoc - sphinx-rtd-theme + sphinx-book-theme astronomy = PyAVM astrodendro spectral-cube recommended = scikit-image -qt = - PyQt5>=5.14 test = pytest pytest-cov @@ -105,10 +89,6 @@ test = [options.package_data] * = *.png, *.ui, *.glu, *.hdf5, *.fits, *.xlsx, *.txt, *.csv, *.svg, *.vot glue.core.data_factories.tests = data/*.jpg -glue.viewers.histogram.qt.tests = data/*.glu -glue.viewers.image.qt.tests = data/*.glu, baseline/*.png -glue.viewers.profile.qt.tests = data/*.glu -glue.viewers.scatter.qt.tests = data/*.glu [flake8] ignore = E226,E501,E731,F841,E127,E741,E402,W504,W605 @@ -124,17 +104,14 @@ filterwarnings = ignore:DragAndDropTerminal._style_sheet_changed is deprecated ignore:::ipykernel ignore:Accessing zmq Socket attribute - ignore:'U' mode is deprecated:DeprecationWarning:PyQt5 [coverage:run] omit = glue/*tests/* - glue/qt/ui/* glue/core/odict.py, glue/core/glue_pickle.py glue/external/* */glue/*tests/* - */glue/qt/ui/* */glue/core/odict.py, */glue/core/glue_pickle.py */glue/external/* diff --git a/tox.ini b/tox.ini index e7ae8b735..2fe7aa9c8 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,6 @@ [tox] envlist = - py{38,39,310,311}-{codestyle,test,docs}-{pyqt514,pyqt515,pyside514,pyside515,pyqt63,pyside63}-all-{dev,legacy} + py{38,39,310,311}-{codestyle,test,docs}-all-{dev,legacy} requires = pip >= 18.0 setuptools >= 30.3.0 @@ -9,9 +9,8 @@ requires = pip >= 18.0 passenv = DISPLAY HOME -# Set to 1 to get more debug information from PyQt setenv = - test: QT_DEBUG_PLUGINS = 0 + dev: PIP_EXTRA_INDEX_URL = https://pypi.anaconda.org/astropy/simple https://pypi.anaconda.org/scientific-python-nightly-wheels/simple whitelist_externals = find rm @@ -21,19 +20,12 @@ changedir = test: .tmp/{envname} docs: doc deps = - pyqt514: PyQt5==5.14.* - pyqt515: PyQt5==5.15.* - pyqt63: PyQt6==6.3.* - pyqt64: PyQt6==6.4.* - pyside514: PySide2==5.14.* - pyside515: PySide2==5.15.* - pyside63: PySide6==6.3.* - pyside64: PySide6==6.4.* - # Pin numpy-dev until #2353 is resolved - dev: git+https://github.com/numpy/numpy@9b6a7b4f8 - dev: git+https://github.com/astropy/astropy + dev: numpy>=0.0.dev0 + dev: astropy>=0.0.dev0 lts: astropy==5.0.* lts: matplotlib==3.5.* + # Pin numpy-lts until permanent solution for #2353/#2428 + lts: numpy==1.24.* legacy: numpy==1.17.* legacy: matplotlib==3.2.* legacy: scipy==1.1.* @@ -41,16 +33,13 @@ deps = legacy: echo==0.5.* legacy: astropy==4.0.* legacy: setuptools==30.3.* - legacy: qtpy==1.9.* legacy: ipython==7.16.* legacy: ipykernel==5.3.* - legacy: qtconsole==4.3.* legacy: dill==0.2.* legacy: xlrd==1.2.* legacy: h5py==2.10.* legacy: mpl-scatter-density==0.7.* legacy: openpyxl==3.0.* - all: pytest-qt extras = test all: all