Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added 'simple' viewers which can be used for testing or simple use cases #2458

Merged
merged 4 commits into from
Nov 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 96 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
version: 2.1

jobs:

# The following job is to run any visual comparison test, and runs on any branch
# or in any pull request. It will generate a summary page for each tox environment
# being run which is accessible through the CircleCI artifacts.

visual:
parameters:
jobname:
type: string
docker:
- image: cimg/python:3.11
environment:
TOXENV: << parameters.jobname >>
steps:
- checkout
- run:
name: Install dependencies
command: |
sudo apt update
pip install pip tox --upgrade
- run:
name: Run tests
command: tox -v
- store_artifacts:
path: results
- run:
name: "Image comparison page is available at: "
command: echo "${CIRCLE_BUILD_URL}/artifacts/${CIRCLE_NODE_INDEX}/results/fig_comparison.html"

# The following job runs only on main - and its main purpose is to update the
# reference images in the glue-core-visual-tests repository. This job needs
# a deploy key. To produce this, go to the glue-core-visual-tests
# repository settings and go to SSH keys, then add your public SSH key.
deploy-reference-images:
parameters:
jobname:
type: string
docker:
- image: cimg/python:3.11
environment:
TOXENV: << parameters.jobname >>
steps:
- checkout
- run:
name: Install dependencies
command: |
sudo apt update
pip install pip tox --upgrade
- run: ssh-add -D
- add_ssh_keys:
fingerprints: "44:09:69:d7:c6:77:25:e9:46:da:f1:22:7d:d4:38:29"
- run: ssh-keyscan github.com >> ~/.ssh/known_hosts
- run: git config --global user.email "glue@circleci" && git config --global user.name "Glue Circle CI"
- run: git clone [email protected]:glue-viz/glue-core-visual-tests.git --depth 1 ~/glue-core-visual-tests/
- run:
name: Generate reference images
command: tox -v -- --mpl-generate-path=/home/circleci/glue-core-visual-tests/images/$TOXENV
- run: |
cd ~/glue-core-visual-tests/
git pull
git status
git add .
git commit -m "Update reference images from ${CIRCLE_BRANCH}" || echo "No changes to reference images to deploy"
git push

workflows:
version: 2

visual-tests:
jobs:
- visual:
name: << matrix.jobname >>
matrix:
parameters:
jobname:
- "py311-test-visual"

- deploy-reference-images:
name: baseline-<< matrix.jobname >>
matrix:
parameters:
jobname:
- "py311-test-visual"
requires:
- << matrix.jobname >>
filters:
branches:
only:
- main

notify:
webhooks:
- url: https://giles.cadair.dev/circleci
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,5 @@ glue/_githash.py
.vscode
# vscode plugin
.history

results
Empty file added glue/tests/visual/__init__.py
Empty file.
46 changes: 46 additions & 0 deletions glue/tests/visual/helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Licensed under a 3-clause BSD style license - see LICENSE.rst

from functools import wraps

import pytest

try:
import pytest_mpl # noqa
except ImportError:
HAS_PYTEST_MPL = False
else:
HAS_PYTEST_MPL = True


def visual_test(*args, **kwargs):
"""
A decorator that defines a visual test.

This automatically decorates tests with mpl_image_compare with common
options used by all figure tests in glue-core.
"""

tolerance = kwargs.pop("tolerance", 0)
style = kwargs.pop("style", {})
savefig_kwargs = kwargs.pop("savefig_kwargs", {})
savefig_kwargs["metadata"] = {"Software": None}

def decorator(test_function):
@pytest.mark.mpl_image_compare(
tolerance=tolerance, style=style, savefig_kwargs=savefig_kwargs, **kwargs
)
@pytest.mark.skipif(
not HAS_PYTEST_MPL, reason="pytest-mpl is required for the figure tests"
)
@wraps(test_function)
def test_wrapper(*args, **kwargs):
return test_function(*args, **kwargs)

return test_wrapper

# If the decorator was used without any arguments, the only positional
# argument will be the test to decorate so we do the following:
if len(args) == 1:
return decorator(*args)

return decorator
6 changes: 6 additions & 0 deletions glue/tests/visual/py311-test-visual.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"glue.viewers.histogram.tests.test_viewer.test_simple_viewer": "cb08123fbad135ab614bb7ec13475fcc83321057d884fe80c3a32970b2d14762",
"glue.viewers.image.tests.test_viewer.test_simple_viewer": "72abd60b484d14f721254f027bb0ab9b36245d5db77eb87693f4dd9998fd28be",
"glue.viewers.profile.tests.test_viewer.test_simple_viewer": "f68a21be5080fec513388b2d2b220512e7b0df5498e2489da54e58708de435b3",
"glue.viewers.scatter.tests.test_viewer.test_simple_viewer": "1020a7bd3abe40510b9e03047c3b423b75c3c64ac18e6dcd6257173cec1ed53f"
}
2 changes: 2 additions & 0 deletions glue/viewers/histogram/layer_artist.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
from glue.viewers.matplotlib.layer_artist import MatplotlibLayerArtist
from glue.core.exceptions import IncompatibleAttribute, IncompatibleDataException

__all__ = ["HistogramLayerArtist"]


class HistogramLayerArtist(MatplotlibLayerArtist):

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,34 @@
from glue.viewers.common.viewer import Viewer
from glue.viewers.histogram.state import HistogramViewerState
import numpy as np

from astropy.utils import NumpyRNGContext

from glue.tests.visual.helpers import visual_test
from glue.viewers.histogram.viewer import SimpleHistogramViewer
from glue.core.application_base import Application
from glue.core.data import Data


class TestHistogramViewer(Viewer):
_state_cls = HistogramViewerState
@visual_test
def test_simple_viewer():

# Make sure the simple viewer can be instantiated

with NumpyRNGContext(12345):

Check warning on line 16 in glue/viewers/histogram/tests/test_viewer.py

View check run for this annotation

Codecov / codecov/patch

glue/viewers/histogram/tests/test_viewer.py#L16

Added line #L16 was not covered by tests

data1 = Data(x=np.random.normal(1, 2, 1000), label='data1')
data2 = Data(y=np.random.uniform(-1, 5, 1000), label='data2')

Check warning on line 19 in glue/viewers/histogram/tests/test_viewer.py

View check run for this annotation

Codecov / codecov/patch

glue/viewers/histogram/tests/test_viewer.py#L18-L19

Added lines #L18 - L19 were not covered by tests

app = Application()
app.data_collection.append(data1)
app.data_collection.append(data2)

Check warning on line 23 in glue/viewers/histogram/tests/test_viewer.py

View check run for this annotation

Codecov / codecov/patch

glue/viewers/histogram/tests/test_viewer.py#L21-L23

Added lines #L21 - L23 were not covered by tests

viewer = app.new_data_viewer(SimpleHistogramViewer)
viewer.add_data(data1)
viewer.add_data(data2)

Check warning on line 27 in glue/viewers/histogram/tests/test_viewer.py

View check run for this annotation

Codecov / codecov/patch

glue/viewers/histogram/tests/test_viewer.py#L25-L27

Added lines #L25 - L27 were not covered by tests

app.data_collection.new_subset_group(label='subset1', subset_state=data1.id['x'] > 2)

Check warning on line 29 in glue/viewers/histogram/tests/test_viewer.py

View check run for this annotation

Codecov / codecov/patch

glue/viewers/histogram/tests/test_viewer.py#L29

Added line #L29 was not covered by tests

return viewer.figure

Check warning on line 31 in glue/viewers/histogram/tests/test_viewer.py

View check run for this annotation

Codecov / codecov/patch

glue/viewers/histogram/tests/test_viewer.py#L31

Added line #L31 was not covered by tests


def test_remove_data_collection():
Expand All @@ -22,7 +45,7 @@
app.data_collection.append(data1)
app.data_collection.append(data2)

viewer = app.new_data_viewer(TestHistogramViewer)
viewer = app.new_data_viewer(SimpleHistogramViewer)
viewer.add_data(data1)
viewer.add_data(data2)

Expand All @@ -46,7 +69,7 @@
app.data_collection.append(data1)
app.data_collection.append(data2)

viewer = app.new_data_viewer(TestHistogramViewer)
viewer = app.new_data_viewer(SimpleHistogramViewer)
viewer.add_data(data1)
viewer.add_data(data2)

Expand Down
16 changes: 15 additions & 1 deletion glue/viewers/histogram/viewer.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@
from glue.core.subset import roi_to_subset_state
from glue.utils import mpl_to_datetime64

from glue.viewers.matplotlib.viewer import SimpleMatplotlibViewer
from glue.viewers.histogram.compat import update_histogram_viewer_state
from glue.viewers.histogram.layer_artist import HistogramLayerArtist
from glue.viewers.histogram.state import HistogramViewerState

__all__ = ['MatplotlibHistogramMixin']
__all__ = ['MatplotlibHistogramMixin', 'SimpleHistogramViewer']


class MatplotlibHistogramMixin(object):
Expand Down Expand Up @@ -69,3 +72,14 @@ def apply_roi(self, roi, override_mode=None):
@staticmethod
def update_viewer_state(rec, context):
return update_histogram_viewer_state(rec, context)


class SimpleHistogramViewer(MatplotlibHistogramMixin, SimpleMatplotlibViewer):

_state_cls = HistogramViewerState
_data_artist_cls = HistogramLayerArtist
_subset_artist_cls = HistogramLayerArtist

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
MatplotlibHistogramMixin.setup_callbacks(self)
27 changes: 27 additions & 0 deletions glue/viewers/image/tests/test_viewer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import numpy as np

from glue.tests.visual.helpers import visual_test
from glue.viewers.image.viewer import SimpleImageViewer
from glue.core.application_base import Application
from glue.core.data import Data


@visual_test
def test_simple_viewer():

# Make sure the simple viewer can be instantiated

data1 = Data(x=np.arange(6).reshape((2, 3)), label='data1')
data2 = Data(y=2 * np.arange(6).reshape((2, 3)), label='data2')

Check warning on line 15 in glue/viewers/image/tests/test_viewer.py

View check run for this annotation

Codecov / codecov/patch

glue/viewers/image/tests/test_viewer.py#L14-L15

Added lines #L14 - L15 were not covered by tests

app = Application()
app.data_collection.append(data1)
app.data_collection.append(data2)

Check warning on line 19 in glue/viewers/image/tests/test_viewer.py

View check run for this annotation

Codecov / codecov/patch

glue/viewers/image/tests/test_viewer.py#L17-L19

Added lines #L17 - L19 were not covered by tests

viewer = app.new_data_viewer(SimpleImageViewer)
viewer.add_data(data1)
viewer.add_data(data2)

Check warning on line 23 in glue/viewers/image/tests/test_viewer.py

View check run for this annotation

Codecov / codecov/patch

glue/viewers/image/tests/test_viewer.py#L21-L23

Added lines #L21 - L23 were not covered by tests

app.data_collection.new_subset_group(label='subset1', subset_state=data1.pixel_component_ids[1] > 1.2)

Check warning on line 25 in glue/viewers/image/tests/test_viewer.py

View check run for this annotation

Codecov / codecov/patch

glue/viewers/image/tests/test_viewer.py#L25

Added line #L25 was not covered by tests

return viewer.figure

Check warning on line 27 in glue/viewers/image/tests/test_viewer.py

View check run for this annotation

Codecov / codecov/patch

glue/viewers/image/tests/test_viewer.py#L27

Added line #L27 was not covered by tests
14 changes: 13 additions & 1 deletion glue/viewers/image/viewer.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,16 @@
from glue.core.coordinate_helpers import dependent_axes
from glue.core.data_region import RegionData

from glue.viewers.matplotlib.viewer import SimpleMatplotlibViewer
from glue.viewers.scatter.layer_artist import ScatterLayerArtist, ScatterRegionLayerArtist
from glue.viewers.image.layer_artist import ImageLayerArtist, ImageSubsetLayerArtist
from glue.viewers.image.compat import update_image_viewer_state
from glue.viewers.image.state import ImageViewerState

from glue.viewers.image.frb_artist import imshow
from glue.viewers.image.composite_array import CompositeArray

__all__ = ['MatplotlibImageMixin']
__all__ = ['MatplotlibImageMixin', 'SimpleImageViewer']


def get_identity_wcs(naxis):
Expand Down Expand Up @@ -258,3 +260,13 @@
x_ticklabel_size=self.state.x_ticklabel_size,
y_ticklabel_size=self.state.y_ticklabel_size)
return [], EXTRA_FOOTER.format(**options) + os.linesep * 2 + script


class SimpleImageViewer(MatplotlibImageMixin, SimpleMatplotlibViewer):

_state_cls = ImageViewerState

def __init__(self, *args, **kwargs):
kwargs['wcs'] = True
super().__init__(*args, **kwargs)
MatplotlibImageMixin.setup_callbacks(self)

Check warning on line 272 in glue/viewers/image/viewer.py

View check run for this annotation

Codecov / codecov/patch

glue/viewers/image/viewer.py#L270-L272

Added lines #L270 - L272 were not covered by tests
13 changes: 11 additions & 2 deletions glue/viewers/matplotlib/viewer.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@
from matplotlib.artist import setp as msetp

from glue.config import settings
from glue.viewers.matplotlib.mpl_axes import update_appearance_from_settings
from glue.viewers.common.viewer import Viewer
from glue.viewers.matplotlib.mpl_axes import update_appearance_from_settings, init_mpl
from echo import delay_callback
from glue.utils import mpl_to_datetime64

__all__ = ['MatplotlibViewerMixin']
__all__ = ['MatplotlibViewerMixin', 'SimpleMatplotlibViewer']

SCRIPT_HEADER = """
# Initialize figure
Expand Down Expand Up @@ -352,3 +353,11 @@ def _script_legend(self):
if not self.state.legend.visible:
legend_str = indent(legend_str, "# ")
return [], legend_str


class SimpleMatplotlibViewer(MatplotlibViewerMixin, Viewer):

def __init__(self, session, parent=None, wcs=None, state=None, projection=None):
super().__init__(session, state=state)
self.figure, self.axes = init_mpl(wcs=wcs, projection=projection)
MatplotlibViewerMixin.setup_callbacks(self)
27 changes: 27 additions & 0 deletions glue/viewers/profile/tests/test_viewer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from glue.tests.visual.helpers import visual_test
from glue.viewers.profile.viewer import SimpleProfileViewer
from glue.core.application_base import Application
from glue.core.data import Data


@visual_test
def test_simple_viewer():

# Make sure the simple viewer can be instantiated

data1 = Data(x=[1, 2, 3], label='data1')
data2 = Data(y=[1, 2, 3], label='data2')

Check warning on line 13 in glue/viewers/profile/tests/test_viewer.py

View check run for this annotation

Codecov / codecov/patch

glue/viewers/profile/tests/test_viewer.py#L12-L13

Added lines #L12 - L13 were not covered by tests

app = Application()
app.data_collection.append(data1)
app.data_collection.append(data2)

Check warning on line 17 in glue/viewers/profile/tests/test_viewer.py

View check run for this annotation

Codecov / codecov/patch

glue/viewers/profile/tests/test_viewer.py#L15-L17

Added lines #L15 - L17 were not covered by tests

viewer = app.new_data_viewer(SimpleProfileViewer)
viewer.add_data(data1)
viewer.add_data(data2)

Check warning on line 21 in glue/viewers/profile/tests/test_viewer.py

View check run for this annotation

Codecov / codecov/patch

glue/viewers/profile/tests/test_viewer.py#L19-L21

Added lines #L19 - L21 were not covered by tests

app.data_collection.new_subset_group(label='subset1', subset_state=data1.pixel_component_ids[0] > 0.8)

Check warning on line 23 in glue/viewers/profile/tests/test_viewer.py

View check run for this annotation

Codecov / codecov/patch

glue/viewers/profile/tests/test_viewer.py#L23

Added line #L23 was not covered by tests

viewer.state.layers[2].linewidth = 5

Check warning on line 25 in glue/viewers/profile/tests/test_viewer.py

View check run for this annotation

Codecov / codecov/patch

glue/viewers/profile/tests/test_viewer.py#L25

Added line #L25 was not covered by tests

return viewer.figure

Check warning on line 27 in glue/viewers/profile/tests/test_viewer.py

View check run for this annotation

Codecov / codecov/patch

glue/viewers/profile/tests/test_viewer.py#L27

Added line #L27 was not covered by tests
17 changes: 16 additions & 1 deletion glue/viewers/profile/viewer.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@
from glue.core.units import UnitConverter
from glue.core.subset import roi_to_subset_state

__all__ = ['MatplotlibProfileMixin']
from glue.viewers.matplotlib.viewer import SimpleMatplotlibViewer
from glue.viewers.profile.state import ProfileViewerState
from glue.viewers.profile.layer_artist import ProfileLayerArtist

__all__ = ['MatplotlibProfileMixin', 'SimpleProfileViewer']


class MatplotlibProfileMixin(object):
Expand Down Expand Up @@ -55,3 +59,14 @@

subset_state = roi_to_subset_state(roi, x_att=self.state.x_att)
self.apply_subset_state(subset_state, override_mode=override_mode)


class SimpleProfileViewer(MatplotlibProfileMixin, SimpleMatplotlibViewer):

_state_cls = ProfileViewerState
_data_artist_cls = ProfileLayerArtist
_subset_artist_cls = ProfileLayerArtist

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
MatplotlibProfileMixin.setup_callbacks(self)

Check warning on line 72 in glue/viewers/profile/viewer.py

View check run for this annotation

Codecov / codecov/patch

glue/viewers/profile/viewer.py#L71-L72

Added lines #L71 - L72 were not covered by tests
Empty file.
Loading