Skip to content
This repository has been archived by the owner on Jul 22, 2021. It is now read-only.

Commit

Permalink
Merge pull request #330 from nmearl/master
Browse files Browse the repository at this point in the history
CubeViz infrastructure refactor
  • Loading branch information
nmearl authored Nov 2, 2017
2 parents fbcf893 + 2b23567 commit b2db802
Show file tree
Hide file tree
Showing 36 changed files with 751 additions and 605 deletions.
31 changes: 16 additions & 15 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,15 @@ env:
- ASTROPY_VERSION=stable
- MAIN_CMD='python setup.py'
- SETUP_CMD='test'
- PIP_DEPENDENCIES=''
- EVENT_TYPE='pull_request push'


# List other runtime dependencies for the package that are available as
# conda packages here.

# For this package-template, we include examples of Cython modules,
# so Cython is required for testing. If your package does not include
# Cython code, you can set CONDA_DEPENDENCIES=''
- CONDA_DEPENDENCIES='pyqt'

# List other runtime dependencies for the package that are available as
# pip packages here.
- PIP_DEPENDENCIES='scipy specutils<=0.2.2 six pyyaml pyqtgraph qtpy py_expression_eval sphinx_rtd_theme sphinx_automodapi'
Expand Down Expand Up @@ -78,19 +80,16 @@ matrix:
# Check for sphinx doc build warnings - we do this first because it
# may run for a long time
- os: linux
env: SETUP_CMD='build_docs -w'
env: SETUP_CMD='build_docs -w' SPHINX_VERSION='<1.6'

# Now try Astropy dev and LTS vesions with the latest 3.x and 2.7.
- os: linux
env: PYTHON_VERSION=2.7 ASTROPY_VERSION=development
EVENT_TYPE='pull_request push cron'
# Now try Astropy dev with the latest Python and LTS with Python 2.7 and 3.x.
- os: linux
env: ASTROPY_VERSION=development
EVENT_TYPE='pull_request push cron'
- os: linux
env: PYTHON_VERSION=2.7 ASTROPY_VERSION=lts NUMPY_VERSION=1.12
env: PYTHON_VERSION=2.7 ASTROPY_VERSION=lts
- os: linux
env: ASTROPY_VERSION=lts NUMPY_VERSION=1.12
env: ASTROPY_VERSION=lts

# Try all python versions and Numpy versions. Since we can assume that
# the Numpy developers have taken care of testing Numpy with different
Expand All @@ -103,15 +102,17 @@ matrix:
env: PYTHON_VERSION=3.4 NUMPY_VERSION=1.10
- os: linux
env: PYTHON_VERSION=3.5 NUMPY_VERSION=1.11
- os: linux
env: NUMPY_VERSION=1.12

# Try numpy pre-release
- os: linux
env: NUMPY_VERSION=prerelease
EVENT_TYPE='pull_request push cron'

# # Do a PEP8 test with pycodestyle
# - os: linux
# env: MAIN_CMD='pycodestyle specviz --count' SETUP_CMD=''
# Do a PEP8 test with pycodestyle
- os: linux
env: MAIN_CMD='pycodestyle specviz --count' SETUP_CMD=''

allow_failures:
# Do a PEP8 test with pycodestyle
Expand All @@ -132,7 +133,7 @@ install:
# in how to install a package, in which case you can have additional
# commands in the install: section below.

- git clone git://github.com/astropy/ci-helpers.git
- git clone --depth 1 git://github.com/astropy/ci-helpers.git
- source ci-helpers/travis/setup_conda.sh

# As described above, using ci-helpers, you should be able to set up an
Expand Down
7 changes: 6 additions & 1 deletion docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,12 @@ Data Objects

Object Event Handling
^^^^^^^^^^^^^^^^^^^^^
.. automodapi:: specviz.core.comms
.. automodapi:: specviz.core.events
:no-heading:

Object Dispatch Machinery
^^^^^^^^^^^^^^^^^^^^^^^^^
.. automodapi:: specviz.core.dispatch
:no-heading:

Spectrum Layer Plotting
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@
PACKAGENAME, [os.path.normpath(
os.path.join("data", os.path.relpath(x[0], root_dir), "*"))
for x in os.walk(root_dir)])
package_info['package_data'][PACKAGENAME].append('external/glue/*.ui')
package_info['package_data'][PACKAGENAME].append('third_party/glue/*.ui')

# Define entry points for command-line scripts
entry_points = {'console_scripts': []}
Expand Down
2 changes: 1 addition & 1 deletion specviz/analysis/models/blackbody.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from astropy.modeling import Fittable1DModel
from astropy.modeling.parameters import Parameter
from astropy.analytic_functions import blackbody_lambda
from astropy.modeling.blackbody import blackbody_lambda

__all__ = ['BlackBody']

Expand Down
2 changes: 1 addition & 1 deletion specviz/analysis/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def resample(data_in, x_in, x_out, y, data_out=None, kind='linear'):
array([(4100, 1.0, 1.0), (4300, 1.0, 1.0), (4500, 1.0, 1.0)],
dtype=[('wlen', '<i8'), ('flux', '<f8'), ('ivar', '<f8')])
The input grid can also be external to the structured array of spectral
The input grid can also be third_party to the structured array of spectral
data, for example:
>>> data = np.ones((5,), [('flux', float), ('ivar', float)])
Expand Down
61 changes: 29 additions & 32 deletions specviz/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
from docopt import docopt

from .widgets.utils import ICON_PATH
from .core.comms import dispatch
from .core.events import dispatch
from .widgets.windows import MainWindow

try:
Expand All @@ -40,7 +40,12 @@


class App(object):
def __init__(self, hide_plugins=False):
def __init__(self, hidden=None, disabled=None):
hidden = hidden or {}
disabled = disabled or {}

self._instanced_plugins = {}

# Instantiate main window object
self._all_tool_bars = {}

Expand All @@ -53,52 +58,54 @@ def __init__(self, hide_plugins=False):
# self.main_window.setDockNestingEnabled(True)

# Load system and user plugins
self.load_plugins(hidden=hide_plugins)
self.load_plugins(hidden=hidden, disabled=disabled)

# Setup up top-level connections
self._setup_connections()

# Parse arguments
args = docopt(__doc__, version=version)
self._parse_args(args)
try:
args = docopt(__doc__, version=version)
except SystemExit:
logging.error("Received unknown command line arguments.")
else:
self._parse_args(args)

def _parse_args(self, args):
if args.get("load", False):
file_filter = args.get("--loader", "Auto (*)")
dispatch.on_file_read.emit(args.get("<path>"),
file_filter=file_filter)

def load_plugins(self, hidden=False):
def load_plugins(self, hidden=None, disabled=None):
from .interfaces.registries import plugin_registry

instance_plugins = [x() for x in plugin_registry.members]
self._instanced_plugins = {
x.name:x() for x in plugin_registry.members
if not disabled.get(x.name, False)}

for instance_plugin in sorted(instance_plugins,
for inst_plgn in sorted(self._instanced_plugins.values(),
key=lambda x: x.priority):
if instance_plugin.location != 'hidden':
if instance_plugin.location == 'right':
if inst_plgn.location != 'hidden':
if inst_plgn.location == 'right':
location = Qt.RightDockWidgetArea
elif instance_plugin.location == 'top':
elif inst_plgn.location == 'top':
location = Qt.TopDockWidgetArea
else:
location = Qt.LeftDockWidgetArea

self.main_window.addDockWidget(location, instance_plugin)
self.main_window.addDockWidget(location, inst_plgn)

if hidden:
instance_plugin.hide()
if hidden.get(inst_plgn.name):
inst_plgn.hide()

# Add this dock's visibility action to the menu bar
self.menu_docks.addAction(
instance_plugin.toggleViewAction())

# Resize the widgets now that they are all present
for ip in instance_plugins[::-1]:
ip.setMinimumSize(ip.sizeHint())
# QApplication.processEvents()
inst_plgn.toggleViewAction())

# Sort actions based on priority
all_actions = [y for x in instance_plugins for y in x._actions]
all_actions = [y for x in self._instanced_plugins.values()
for y in x._actions]
all_categories = {}

for act in all_actions:
Expand Down Expand Up @@ -134,16 +141,6 @@ def _get_tool_bar(self, name, priority):
tool_bar.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)
tool_bar.setMovable(False)

tool_bar.setStyleSheet("""
QToolBar {
icon-size: 32px;
}
QToolBar QToolButton {
height: 48px;
}
""")

self._all_tool_bars[name] = dict(widget=tool_bar,
priority=int(priority),
name=name)
Expand Down Expand Up @@ -219,7 +216,7 @@ def glue_setup():
raise Exception("glue 0.10.2 or later is required for the specviz "
"plugin")

from .external.glue.data_viewer import SpecVizViewer
from .third_party.glue.data_viewer import SpecVizViewer
from glue.config import qt_client
qt_client.add(SpecVizViewer)

Expand Down
2 changes: 1 addition & 1 deletion specviz/core/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
from .comms import dispatch, DispatchHandle
from .events import dispatch
66 changes: 31 additions & 35 deletions specviz/core/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,11 @@
from __future__ import (absolute_import, division, print_function,
unicode_literals)

# STDLIB
import logging
import re

# THIRD-PARTY
import numpy as np
from astropy.units import Quantity, LogQuantity, LogUnit, spectral_density, spectral
from astropy.units import Quantity, LogQuantity, LogUnit, spectral_density, spectral, Unit
from py_expression_eval import Parser

# FIXME: the latest developer version of Astropy removes OrderedDict which is needed by
Expand All @@ -21,6 +19,7 @@
utils.OrderedDict = OrderedDict

from specutils.core.generic import Spectrum1DRef
from astropy.nddata import StdDevUncertainty

logging.basicConfig(level=logging.INFO)

Expand Down Expand Up @@ -50,6 +49,7 @@ def _make_quantity(data, unit):
else:
return Quantity(data, unit=unit)


class Spectrum1DRefLayer(Spectrum1DRef):
"""
Class to handle layers in SpecViz.
Expand All @@ -72,15 +72,21 @@ class Spectrum1DRefLayer(Spectrum1DRef):
Arguments passed to the
`~spectutils.core.generic.Spectrum1DRef` object.
"""
def __init__(self, data, wcs=None, parent=None, layer_mask=None, *args,
**kwargs):
super(Spectrum1DRefLayer, self).__init__(data, wcs=wcs, *args,
**kwargs)
def __init__(self, data, wcs=None, parent=None, layer_mask=None,
uncertainty=None, unit=None, mask=None, *args,**kwargs):
uncertainty = StdDevUncertainty(np.zeros(data.shape)) if uncertainty is None else uncertainty
unit = unit or Unit('')
mask = mask if mask is not None else np.zeros(data.shape).astype(bool)

super(Spectrum1DRefLayer, self).__init__(data, wcs=wcs, unit=unit,
uncertainty=uncertainty,
mask=mask,
*args,**kwargs)
self._parent = parent
self._layer_mask = layer_mask

@classmethod
def from_parent(cls, parent, layer_mask=None, name=None):
def from_parent(cls, parent, layer_mask=None, name=None, copy=True):
"""
Create a duplicate child layer from a parent layer
Expand All @@ -107,7 +113,7 @@ def from_parent(cls, parent, layer_mask=None, name=None):
dispersion=parent.dispersion,
dispersion_unit=parent.dispersion_unit,
layer_mask=layer_mask, parent=parent, meta=parent.meta,
copy=False)
copy=copy)

def from_self(self, name="", layer_mask=None):
"""
Expand All @@ -126,10 +132,8 @@ def from_self(self, name="", layer_mask=None):
new_layer:
The new, parentless, layer.
"""
gen_spec = Spectrum1DRef.copy(self, name=name)

return self.from_parent(
parent=gen_spec, layer_mask=layer_mask, name=name
parent=self._parent, layer_mask=layer_mask, name=name, copy=True
)

@classmethod
Expand Down Expand Up @@ -302,40 +306,32 @@ def _evaluate(cls, layers, formula):
formula = formula.replace(layer.name,
layer.name.replace(" ", "_"))

layer_vars = {layer.name.replace(" ", "_"):layer
for layer in layers}

try:
expr = parser.parse(formula)
except Exception as e:
logging.error(e)
return

# Extract variables
vars = expr.variables()
expr_vars = expr.variables()

# List the models in the same order as the variables
# sorted_layers = [next(l for v in vars for l in layers
# if l.name.replace(" ", "_") == v)]
# sorted_layers = [l for v in vars for l in layers
# if l.name.replace(" ", "_") == v]
sorted_layers = []

for v in vars:
for l in layers:
if l.name.replace(" ", "_") == v:
sorted_layers.append(l)
break

if len(sorted_layers) != len(vars):
logging.error("Incorrect layer arithmetic formula: the number "
"of layers does not match the number of variables.")
# Get the union of the sets of variables listed in the expression and
# layer names of the current layer list
union_set = set(layer_vars.keys()).union(set(expr_vars))

if len(union_set) != 0:
logging.error("Mis-match between current layer list and expression:"
"%s", union_set)

try:
result = parser.evaluate(expr.simplify({}).toString(),
dict(pair for pair in
zip(vars, sorted_layers)))
result._dispersion = sorted_layers[0]._dispersion
result.dispersion_unit = sorted_layers[0].dispersion_unit
result = parser.evaluate(expr.simplify({}).toString(), layer_vars)
result = result.__class__.copy({'dispersion': layers[0].dispersion,
'dispersion_unit': layers[0].dispersion_unit})
except Exception as e:
logging.error("While evaluating formula: {}".format(e))
logging.error("While evaluating formula: %s", e)
return

return result
Expand Down
Loading

0 comments on commit b2db802

Please sign in to comment.