From 2728ec33bae06218e4dbf9951ac142d0dc73a0ae Mon Sep 17 00:00:00 2001 From: "J.D.Eisenhamer" Date: Thu, 15 Dec 2016 11:10:23 -0500 Subject: [PATCH 01/21] Add hooks to use sphinx-autobuild for live updating --- docs/Makefile | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/Makefile b/docs/Makefile index e36100db..77089831 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -46,6 +46,7 @@ help: @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" @echo " coverage to run coverage check of the documentation (if enabled)" + @echo " livehtml to run with sphinx-autobuild for live updating" .PHONY: clean clean: @@ -214,3 +215,8 @@ pseudoxml: $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml @echo @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." + +.PHONY: livehtml +livehtml: + sphinx-autobuild -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + From 11601b34a0e26e365aa59646ba79f2d2d7e9178b Mon Sep 17 00:00:00 2001 From: "J.D.Eisenhamer" Date: Mon, 19 Dec 2016 14:31:42 -0500 Subject: [PATCH 02/21] Document the custom fitting classes WIP #237 --- specviz/analysis/models/blackbody.py | 47 +++++++++++++++++++++++++++ specviz/analysis/models/spline.py | 48 +++++++++++++++++++++++++++- 2 files changed, 94 insertions(+), 1 deletion(-) diff --git a/specviz/analysis/models/blackbody.py b/specviz/analysis/models/blackbody.py index b365b327..d31ad425 100644 --- a/specviz/analysis/models/blackbody.py +++ b/specviz/analysis/models/blackbody.py @@ -10,10 +10,40 @@ class BlackBody(Fittable1DModel): + """ + Produce a blackbody flux spectrum + + Notes + ----- + See `astropy.modeling.Fittable1DModel `_ + for further details. + + Description of the blackbody function itself is described in + `astropy.analytic_functions `_ + """ temp = Parameter(default=5000, min=10.) norm = Parameter(default=1.) def evaluate(self, x, temp, norm): + """ + Evaluate the blackbody for a given temperature over a wavalength range + + Parameters + ---------- + x: numpy.ndarray + The wavelengths to evaulate over. + + temp: float + The temperature to evualate at. + + norm: float + The normalization factor. + + Returns + ------- + blackbody_flux: numpy.ndarray + The blackbody flux. + """ # x is passed as a bare numpy array; must be # converted back to Quantity before calling # astropy's black body functions. @@ -29,8 +59,25 @@ def evaluate(self, x, temp, norm): class BlackBodyInitializer(object): + """ + `BlackBody` model initializer + """ def initialize(self, instance, wave, flux): + """ + Initialize the blackbody model + + Parameters + ---------- + instance: BlackBody + The `BlackBody` model + + wave: numpy.ndarray + The wavelength range. + + flux: numpy.ndarray + The source flux to normalize to. + """ instance.wave = wave instance.flux = flux diff --git a/specviz/analysis/models/spline.py b/specviz/analysis/models/spline.py index 6098a121..f2250820 100644 --- a/specviz/analysis/models/spline.py +++ b/specviz/analysis/models/spline.py @@ -7,11 +7,40 @@ class Spline1D(Fittable1DModel): + """ + Perform a spline fit + Notes + ----- + See + `astropy.modeling.Fittable1DModel `_ + for further details. + + The spline function is based on + `scipy.interpolate.UnivariateSpline `_ + """ degree = Parameter(default=3, fixed=True) smooth = Parameter(default=1, fixed=True) # default=None crashes the app def evaluate(self, x, degree, smooth): + """ + Evaluate the spline + + Parameters + ---------- + x: numpy.ndarray + The wavelengths to evaluate over. + + degree: int + The degree of spline to evaluate. + + smooth: float or None + The smoothing factor used to choose the number of knots. + + Returns + ------- + The evaluated spline + """ _f = UnivariateSpline(self.wave, self.flux, k=degree, s=smooth) @@ -19,8 +48,25 @@ def evaluate(self, x, degree, smooth): class Spline1DInitializer(object): - + """ + `Spline1D` model initializer + """ + def initialize(self, instance, wave, flux): + """ + Initialize the `Spline1D` model. + + Parameters + ---------- + instance: `Spline1D` instance + The `Spline1D` model. + + wave: numpy.ndarray + The wavelength range. + + flux: numpy.ndarray + The source flux. + """ instance.wave = wave instance.flux = flux From b656b762ad55a0d59e343a2a10f49c5298caa776 Mon Sep 17 00:00:00 2001 From: "J.D.Eisenhamer" Date: Mon, 19 Dec 2016 16:20:11 -0500 Subject: [PATCH 03/21] Document the event handling module comms.py WIP #237 --- specviz/core/annotation.py | 3 +- specviz/core/comms.py | 93 +++++++++++++++++++++++++++++++++++++- 2 files changed, 92 insertions(+), 4 deletions(-) diff --git a/specviz/core/annotation.py b/specviz/core/annotation.py index 86698225..bc83c756 100644 --- a/specviz/core/annotation.py +++ b/specviz/core/annotation.py @@ -12,6 +12,7 @@ 'vertical': {'anchor': (0, 0.5), 'angle': -90} } + class LineIDMarker(TextItem): ''' This class handles the drawing of a modified TextItem that's augmented with a linear vertical marker. These items are used @@ -62,5 +63,3 @@ def paint(self, p, *args): pen = QPen(QColor(functions.mkColor(self._color))) p.setPen(pen) p.drawPolygon(polygon) - - diff --git a/specviz/core/comms.py b/specviz/core/comms.py index 0675f8b2..0e87b9c0 100644 --- a/specviz/core/comms.py +++ b/specviz/core/comms.py @@ -1,3 +1,11 @@ +""" +Object Event Handling + +Elevator Pitch: The singleton `Dispatch` object manages the +set of `EventNode` events. Handlers or **listeners** are attached +to `EventNode`s. The `DispatchHandle` decorator is used to +decorate classes that handle events. +""" from __future__ import (absolute_import, division, print_function, unicode_literals) @@ -8,19 +16,53 @@ class EventNode(object): + """ + An event + + An event is defined by the arguments the listeners + of the event expect to be given. + + Parameters + args: [arg, ...] + The list of keyword arguments that the event + will provide to its listeners + """ def __init__(self, *args): self._args = args self.__handlers = [] def __iadd__(self, other): + """ + Add a listener via the addition operator + """ self.__handlers.append(other) return self def __isub__(self, other): + """ + Remove a listener via the subtraction operator + """ self.__handlers.remove(other) return self def emit(self, *args, **kwargs): + """ + Call the hanlders of this event + + Parameters + ---------- + args: [arg, ...] + The keyword arguments being provided to the event. + + kwargs: {arg: value, ...} + The keyword/value pairs passed to the listeners. + + Raises + ------ + ValueError + An keyword is being passed that does not belong + to this event. + """ if len(args) != len(self._args) and not set(kwargs.keys()).issubset( set(self._args)): raise ValueError("Unknown keyword in event emit arguments.") @@ -44,6 +86,18 @@ class Dispatch(object): Central communications object for all events. """ def register_event(self, name, args=None): + """ + Add an `EventNode` to the list of possible events + + Parameters + ---------- + name: str + The name of the event. + + args: [arg, ...] + The list of keyword arguments this event will pass + to its handlers. + """ args = args or [] if not hasattr(self, name): @@ -53,6 +107,17 @@ def register_event(self, name, args=None): "different name.".format(name)) def register_listener(self, name, func): + """ + Add a listener to an event + + Parameters + ---------- + name: str + The event name to add the listener to. + + func: function + The function that will be called when the is emitted + """ if hasattr(self, name): call_func = getattr(self, name) call_func += func @@ -61,6 +126,17 @@ def register_listener(self, name, func): "before listeners can be assigned.".format(name)) def unregister_listener(self, name, func): + """ + Remove a listener from an event + + Parameters + ---------- + name: str + The event from wich the listener should be removed. + + func: function + The function to be removed + """ if hasattr(self, name): call_func = getattr(self, name) call_func -= func @@ -78,6 +154,9 @@ class DispatchHandle(object): """ @staticmethod def setup(inst): + """ + Register all methods decorated by `register_listener` + """ logging.info("Dispatch is now watching: {}".format(inst)) members = inspect.getmembers(inst, predicate=inspect.ismethod) @@ -89,6 +168,9 @@ def setup(inst): @staticmethod def tear_down(inst): + """ + Remove all registered methods from their events + """ logging.info("Dispatch has stopped watching: {}".format(inst)) members = inspect.getmembers(inst, predicate=inspect.ismethod) @@ -100,6 +182,9 @@ def tear_down(inst): @staticmethod def register_listener(*args): + """ + Decorate event listeners + """ def decorator(func): func.wrapped = True func.event_names = args @@ -109,11 +194,15 @@ def wrapper(*args, **kwargs): try: return func(*args, **kwargs) except: - logging.error("Exception in '{}':\n{}".format(func.__name__, - traceback.format_exc())) + logging.error( + "Exception in '{}':\n{}".format(func.__name__, + traceback.format_exc()) + ) return wrapper return decorator + +# Register application-wide events dispatch = Dispatch() dispatch.register_event("on_activated_window", args=["window"]) From c811aee3fa6625f6ad14c04929423fed39b18a5e Mon Sep 17 00:00:00 2001 From: "J.D.Eisenhamer" Date: Mon, 19 Dec 2016 18:29:37 -0500 Subject: [PATCH 04/21] Document core/data.py WIP #237 --- specviz/core/data.py | 158 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 152 insertions(+), 6 deletions(-) diff --git a/specviz/core/data.py b/specviz/core/data.py index 73a5afd2..b46effdc 100644 --- a/specviz/core/data.py +++ b/specviz/core/data.py @@ -15,7 +15,27 @@ class Spectrum1DRefLayer(Spectrum1DRef): - """Class to handle layers in SpecViz.""" + """ + Class to handle layers in SpecViz. + + Parameters + ---------- + data: numpy.ndarray + The flux. + + wcs: `~astropy.wcs.WCS` + If specified, the WCS relating pixel to wavelength. + + parent: layer + If specified, the parent layer. + + layer_mask: layer + The layer defining the valid data mask. + + args, kwargs: + 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, @@ -25,6 +45,26 @@ def __init__(self, data, wcs=None, parent=None, layer_mask=None, *args, @classmethod def from_parent(cls, parent, layer_mask=None, name=None): + """ + Create a duplicate child layer from a parent layer + + Parameters + ---------- + parent: layer + The layer to duplicate. + + layer_mask: layer + The layer defining the valid data mask. + + name: str + Layer's name. If `None`, a name based on the parent + layer is used. + + Returns + ------- + child_layer: + The new layer. + """ return cls(name=name or parent.name + " Layer", data=parent.data, unit=parent.unit, uncertainty=parent.uncertainty, mask=parent.mask, wcs=parent.wcs, @@ -34,12 +74,46 @@ def from_parent(cls, parent, layer_mask=None, name=None): copy=False) def from_self(self, name="", layer_mask=None): + """ + Create a new, parentless, layer based on this layer + + Parameters + ---------- + name: str + Name of the new layer + + layer_mask: layer + The layer defining the valid data mask. + + Returns + ------- + 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) + return self.from_parent( + parent=gen_spec, layer_mask=layer_mask, name=name + ) @classmethod def from_formula(cls, formula, layers): + """ + Create a layer from an operation performed on other layers + + Parameters + ---------- + formula: str + The operation to perform on the given layers. + + layers: [layer, ...] + The layers which are arguments to the given formula. + + Returns + ------- + new_layer: + Result of the operation + """ if not formula: return @@ -133,6 +207,17 @@ def full_mask(self): return self.mask.astype(bool) | ~self.layer_mask.astype(bool) def set_units(self, disp_unit, data_unit): + """ + Set the dispersion and flux units + + Parameters + ---------- + disp_unit: `~astropy.units` + The dispersion units. + + data_unit: `~astropy.units` + The flux units. + """ if self.dispersion_unit.is_equivalent(disp_unit, equivalencies=spectral()): self._dispersion = self.dispersion.data.to( @@ -218,7 +303,21 @@ def _evaluate(cls, layers, formula): class Spectrum1DRefModelLayer(Spectrum1DRefLayer): - """A layer for spectrum with a model applied.""" + """ + A layer for spectrum with a model applied. + + Parameters + ---------- + data: numpy.ndarray + The flux. + + model: `~astropy.modeling` + The model + + args, kwargs: + Arguments passed to the + `~spectutils.core.generic.Spectrum1DRef` object. + """ def __init__(self, data, model=None, *args, **kwargs): super(Spectrum1DRefModelLayer, self).__init__(data, *args, **kwargs) @@ -226,6 +325,25 @@ def __init__(self, data, model=None, *args, **kwargs): @classmethod def from_parent(cls, parent, model=None, layer_mask=None): + """ + Create a duplicate child layer from a parent layer + + Parameters + ---------- + parent: layer + The layer to duplicate. + + model: `~astropy.modeling` + The model. + + layer_mask: layer + The layer defining the valid data mask. + + Returns + ------- + child_layer: + The new layer. + """ if model is not None: data = model(parent.dispersion.data.value) else: @@ -244,6 +362,22 @@ def from_parent(cls, parent, model=None, layer_mask=None): @classmethod def from_formula(cls, models, formula): + """ + Create a layer from an operation performed on other models + + Parameters + ---------- + formula: str + The operation to perform on the given layers. + + models: [model, ...] + The models which are arguments to the given formula. + + Returns + ------- + result_model: + Result of the operation + """ result_model = cls._evaluate(models, formula) return result_model @@ -319,6 +453,16 @@ def model(self, value): @classmethod def _evaluate(cls, models, formula): + """ + Parse a string into an arithmetic expression. + + Parameters + ---------- + models : list + List of `Layer` objects that correspond to the given variables. + formula : str + A string describing the arithmetic operations to perform. + """ try: parser = Parser() expr = parser.parse(formula) @@ -337,10 +481,12 @@ def _evaluate(cls, models, formula): return elif len(sorted_models) < len(vars): extras = [x for x in vars if x not in [y.name for y in - sorted_models]] + sorted_models]] for extra in extras: - matches = re.findall('([\+\*\-\/]?\s?{})'.format(extra), formula) + matches = re.findall( + '([\+\*\-\/]?\s?{})'.format(extra), formula + ) for match in matches: formula = formula.replace(match, "") @@ -356,4 +502,4 @@ def _evaluate(cls, models, formula): dict(pair for pair in zip(vars, sorted_models))) - return result \ No newline at end of file + return result From 78caa843d30646f5360b752f256430252d17167f Mon Sep 17 00:00:00 2001 From: "J.D.Eisenhamer" Date: Mon, 19 Dec 2016 19:03:11 -0500 Subject: [PATCH 05/21] Document core/linelist.py WIP #237 --- specviz/core/linelist.py | 126 +++++++++++++++++++++++++++++---------- 1 file changed, 94 insertions(+), 32 deletions(-) diff --git a/specviz/core/linelist.py b/specviz/core/linelist.py index b4a0f4b8..2b2b0bd3 100644 --- a/specviz/core/linelist.py +++ b/specviz/core/linelist.py @@ -1,3 +1,6 @@ +""" +Utilities and classes to handle Emission/Absorption Line lists +""" from __future__ import (absolute_import, division, print_function, unicode_literals) import os @@ -16,16 +19,30 @@ UNITS_COLUMN = 'units' -# Returns a list with LineList instances. Each original list is -# stripped out of lines that lie outside the wavelength range. - def ingest(range): - - # Lets skip the file dialog business. For now, we look - # for line lists and their accompanying YAML files in - # one single place. We also restrict our search for - # ascii line lists whose file names end in .txt - + """ + Returns a list with LineList instances. + + Each original list is stripped out of lines that lie outside the + wavelength range. + + Parameters + ---------- + range: + The wavelength range of interest. + + Returns + ------- + [LineList, ...] + The list of linelists found. + + Notes + ----- + Lets skip the file dialog business. For now, we look + for line lists and their accompanying YAML files in + one single place. We also restrict our search for + ascii line lists whose file names end in .txt + """ linelist_path = os.path.dirname(os.path.abspath(__file__)) dir_path = linelist_path + '/../data/linelists/' yaml_paths = glob.glob(dir_path + '*.yaml') @@ -50,6 +67,20 @@ def ingest(range): # with the registry machinery in astropy. class LineList(Table): + """ + A list of emission/absorption lines + + Parameters + ---------- + table: `~astropy.table.Table` + If specified, a table to initialize from. + + name: str + The name of the list. + + masked: bool + If true, a masked table is used. + """ def __init__(self, table=None, name=None, masked=None): Table.__init__(self, data=table, masked=masked) @@ -66,14 +97,20 @@ def __init__(self, table=None, name=None, masked=None): @classmethod def merge(cls, lists): - ''' Executes a 'vstack' of all input lists, and - then sorts the result by the wavelength column. + """ + Executes a 'vstack' of all input lists, and + then sorts the result by the wavelength column. - :param lists: list + Parameters + ---------- + lists: [LineList, ...] list of LineList instances - :return: LineList + + Returns + ------- + LineList merged line list - ''' + """ # Note that vstack operates on Table instances but # not on LineList instances. So we first extract the # raw Table instances. @@ -88,15 +125,21 @@ def merge(cls, lists): return cls(merged_table, "Merged") def extract_range(self, wrange): - ''' Builds a LineList instance out of self, with - the subset of lines that fall within the - wavelength range defined by 'wmin' and 'wmax' - - :param wrange: tuple of 2 floats + """ + Builds a LineList instance out of self, with + the subset of lines that fall within the + wavelength range defined by 'wmin' and 'wmax' + + Parameters + ---------- + wrange: (float, float) minimum and maximum wavelength of the wavelength range - :return: LineList + + Returns + ------- + LineList line list with subset of lines - ''' + """ wavelengths = self[WAVELENGTH_COLUMN].quantity wmin = wrange[0] @@ -114,14 +157,20 @@ def extract_range(self, wrange): return self._remove_lines(indices_to_remove) def extract_rows(self, indices): - ''' Builds a LineList instance out of self, with - the subset of lines pointed by 'indices' - - :param indices: list - with QModelIndex instances - :return: LineList + """ + Builds a LineList instance out of self, with + the subset of lines pointed by 'indices' + + Parameters + ---------- + indices: [QModelIndex, ...] + List of QModelIndex instances to extract from. + + Returns + ------- + LineList line list with subset of lines - ''' + """ row_indices = [] for index in indices: row_indices.append(index.row()) @@ -130,13 +179,27 @@ def extract_rows(self, indices): for index in range(len(self.columns[0])): line_indices.append(index) - indices_to_remove = list(filter(lambda x:x not in row_indices, line_indices)) + indices_to_remove = list( + filter(lambda x: x not in row_indices, line_indices) + ) return self._remove_lines(indices_to_remove) def _remove_lines(self, indices_to_remove): - # makes a copy of self and removes - # unwanted lines from the copy. + """ + Makes a copy of self and removes + unwanted lines from the copy. + + Parameters + ---------- + indices_to_remove: [int, ...] + List of row numbers to remove + + Returns + ------- + LineList: + A new copy of the `LineList` with the rows removed. + """ table = Table(self) table.remove_rows(indices_to_remove) @@ -144,4 +207,3 @@ def _remove_lines(self, indices_to_remove): result = LineList(table, self.name) return result - From f747142daa6d63049c35a8295f89c2f1919eafff Mon Sep 17 00:00:00 2001 From: "J.D.Eisenhamer" Date: Mon, 19 Dec 2016 19:23:50 -0500 Subject: [PATCH 06/21] Document core/plots.py WIP #237 --- specviz/core/plots.py | 99 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/specviz/core/plots.py b/specviz/core/plots.py index e8b67833..b13d2181 100644 --- a/specviz/core/plots.py +++ b/specviz/core/plots.py @@ -1,3 +1,6 @@ +""" +Line plotting classes +""" from __future__ import (absolute_import, division, print_function, unicode_literals) @@ -23,6 +26,29 @@ class LinePlot(object): + """ + Plot representation of a layer + + Parameters + ---------- + layer: `Spectrum1DRefLayer` + The layer to plot + + plot: LinePlot + LinePlot instance to reuse. + + visible: bool + If True, the plot will be visible + + style: str + The plotting style + + pen: str + If defined, the pen style to use. + + err_pen: str + If defined, the pen style to use for the error/uncertainty. + """ def __init__(self, layer, plot=None, visible=True, style='line', pen=None, err_pen=None): self._layer = layer @@ -65,6 +91,21 @@ def __init__(self, layer, plot=None, visible=True, style='line', @staticmethod def from_layer(layer, **kwargs): + """Create a LinePlot from a layer + + Parameters + ---------- + layer: `Spectrum1DRefLayer` + The layer to create from. + + kwargs: dict + Other arguments for `LinePlot` class. + + Returns + ------- + plot_container: + The new LinePlot + """ plot_data_item = pg.PlotDataItem(layer.dispersion, layer.data) plot_container = LinePlot(layer=layer, plot=plot_data_item, **kwargs) @@ -90,6 +131,20 @@ def from_layer(layer, **kwargs): return plot_container def change_units(self, x, y=None, z=None): + """ + Change the displayed units + + Parameters + ---------- + x: `~astropy.units` + The new units for the dispersion + + y: `~astropy.units` + The new units for the flux + + z: `~astropy.units` + The new units for the multi-spectral dimension. + """ if x is None or not self._layer.dispersion_unit.is_equivalent( x, equivalencies=spectral()): logging.error("Failed to convert x-axis plot units. {} to {" @@ -106,6 +161,18 @@ def change_units(self, x, y=None, z=None): self.update() def set_plot_visibility(self, show=None, inactive=None): + """ + Set visibility and active state + + Parameters + ---------- + show: bool + If True, show the plot + + inactive: bool + If True, set plot style to indicate this is not + the active plot. + """ if show is not None: if show: self._plot.setPen(self._pen_stash['pen_on']) @@ -117,6 +184,14 @@ def set_plot_visibility(self, show=None, inactive=None): self._plot.setPen(self._pen_stash['pen_inactive']) def set_error_visibility(self, show=None): + """ + Show the error/uncertainty + + Parameters + ---------- + show: bool + If True, show the error/uncertainty info. + """ if self.error is not None and show is not None: if show: self.error.setOpts(pen=self._pen_stash['error_pen_on']) @@ -169,6 +244,14 @@ def error_pen(self, pen): self.error.setOpts(pen=pg.mkPen(pen)) def set_mode(self, mode): + """ + Set the line plotting mode + + Parameters + ---------- + mode: 'line' | 'scatter | 'histogram' + The plot mode + """ if mode in ['line', 'scatter', 'histogram']: self.mode = mode else: @@ -177,12 +260,28 @@ def set_mode(self, mode): self.update() def set_line_width(self, width): + """ + Set the line plot width + + Parameters + ---------- + width: float + The width of the line + """ self.line_width = width _pen = pg.mkPen(self._plot.opts['pen']) _pen.setWidth(self.line_width) self.pen = _pen def update(self, autoscale=False): + """ + Refresh the plot + + Parameters + ---------- + autoscale: bool + If True, rescale the plot to match the data. + """ if hasattr(self.layer, '_model'): disp = self.layer.unmasked_dispersion.compressed().value data = self.layer.unmasked_data.compressed().value From 1b4f78e57688c212a1d7814b29b6a8e1d1259842 Mon Sep 17 00:00:00 2001 From: "J.D.Eisenhamer" Date: Thu, 22 Dec 2016 11:13:33 -0500 Subject: [PATCH 07/21] Update documentation for the threads module WIP #237 --- specviz/core/threads.py | 122 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) diff --git a/specviz/core/threads.py b/specviz/core/threads.py index 433a6c43..bb3e5172 100644 --- a/specviz/core/threads.py +++ b/specviz/core/threads.py @@ -1,3 +1,6 @@ +""" +Threaded tasks +""" from qtpy.QtCore import QThread, Signal import os import logging @@ -9,6 +12,40 @@ class FileLoadThread(QThread): + """ + Asynchronously read in a file + + Parameters + ---------- + parent: QtWidget + The parent widget or None + + Call + ---- + file_name: str + Name of the file to read. + + file_filter: str + Type of file to read. + + Attributes + ---------- + file_name: str + Name of the file to read. + + file_filter: str + Type of file to read. If `Auto`, try all known formats. + + Signals + ------- + status(message, timeout) + State of the thread + message: The status message + timeout: Time (msec) to display message + + result(Spectrum1DRef) + The file's data + """ status = Signal(str, int) result = Signal(Spectrum1DRef) @@ -18,10 +55,16 @@ def __init__(self, parent=None): self.file_filter = "" def __call__(self, file_name, file_filter): + """ + Initialize the thread + """ self.file_name = file_name self.file_filter = file_filter def run(self): + """ + Start thread to read the file. + """ self.status.emit("Loading file...", 0) data = self.read_file(self.file_name, self.file_filter) @@ -38,6 +81,22 @@ def run(self): def read_file(self, file_name, file_filter): """ Convenience method that directly reads a spectrum from a file. + + Parameters + ---------- + file_name: str + Name of the file to read. + + file_filter: str + Type of file to read. If `Auto`, try all known formats. + + Returns + ------- + data: Spectrum1DRef + The file's data or None if no known formats are found. + + Notes + ----- This exists mostly to facilitate development workflow. In time it could be augmented to support fancier features such as wildcards, file lists, mixed file types, and the like. @@ -69,6 +128,46 @@ def read_file(self, file_name, file_filter): class FitModelThread(QThread): + """ + Asynchronously fit a model to a layer + + Parameters + ---------- + parent: QtWidget + The parent widget or None + + Call + ---- + model_layer: Spectrum1DRefLayer + The layer to fit to. + + fitter_name: An `~astropy.modeling` fitter + The fitter to use + + mask: numpy.ndarray + The mask to apply + + Attributes + ---------- + model_layer: Spectrum1DRefLayer + The layer to fit to. + + fitter_name: An `~astropy.modeling` fitter + The fitter to use + + mask: numpy.ndarray + The mask to apply + + Signals + ------- + status(message, timeout) + State of the thread + message: The status message + timeout: Time (msec) to display message + + result(Spectrum1DRefModelLayer) + The fit + """ status = Signal(str, int) result = Signal(Spectrum1DRefModelLayer) @@ -83,6 +182,9 @@ def __call__(self, model_layer, fitter_name, mask=None): self.mask = mask def run(self): + """ + Start thread to fit the model + """ self.status.emit("Fitting model...", 0) model_layer, message = self.fit_model(self.model_layer, self.fitter_name, @@ -96,6 +198,26 @@ def run(self): self.result.emit(model_layer) def fit_model(self, model_layer, fitter_name, mask=None): + """ + Fit the model + + Parameters + ---------- + model_layer: Spectrum1DRefLayer + The layer to fit to. + + fitter_name: An `~astropy.modeling` fitter + The fitter to use + + mask: numpy.ndarray + The mask to apply + + Returns + ------- + (model_layer, fitter_message): Spectrum1DRefLayer, str + The model_layer.model is updated with the fit paramters. + The message is from the fitter itself. + """ if not hasattr(model_layer, 'model'): logging.warning("This layer has no model to fit.") return From 0ef116769c7887b13a0f5087bb4a7eae27ea49fe Mon Sep 17 00:00:00 2001 From: "J.D.Eisenhamer" Date: Thu, 22 Dec 2016 11:52:58 -0500 Subject: [PATCH 08/21] Document module py_model_io WIP #237 --- specviz/interfaces/model_io/py_model_io.py | 195 +++++++++++++++++---- 1 file changed, 158 insertions(+), 37 deletions(-) diff --git a/specviz/interfaces/model_io/py_model_io.py b/specviz/interfaces/model_io/py_model_io.py index 351da5b0..7e3cb38d 100644 --- a/specviz/interfaces/model_io/py_model_io.py +++ b/specviz/interfaces/model_io/py_model_io.py @@ -1,15 +1,17 @@ -# -# Functions in this module support the reading and writing -# of astropy's spectral compound models from/to file. -# -# The format used in these files is plain python, directly -# importable by the user. This format was introduced and is -# discussed in the specfit project at: -# -# https://github.com/ibusko/specfit -# - -import os, sys, re, dis +""" +Functions in this module support the reading and writing +of astropy's spectral compound models from/to file. + +The format used in these files is plain python, directly +importable by the user. This format was introduced and is +discussed in the specfit project at: +https://github.com/ibusko/specfit +""" + +import dis +import os +import re +import sys from io import StringIO from qtpy.QtWidgets import QFileDialog @@ -23,6 +25,7 @@ def _get_component_name(function): class_string = str(function.__class__) return class_string.split('\'>')[0].split(".")[-1] + def _get_component_path(function): class_string = str(function.__class__) module_path = class_string.split('\'')[1] @@ -31,8 +34,27 @@ def _get_component_path(function): return module_path -# Builds a compound model specified in a .py file def buildModelFromFile(fname): + """ + Builds a compound model specified in a .py file + + Parameters + ---------- + fname: str + The model definition file + + Returns + ------- + (compound_model, model_expression, directory) + A 3-tuple consisting of: + compound_model: `~astropy.modeling.models` + The model from the file. + model_expression: str + Currently always an empty string. + directory: str + The path where the file was read from. + If any issues occur, the 3-tuple (None, None, None) is returned + """ directory = os.path.dirname(fname) sys.path.append(directory) @@ -69,11 +91,25 @@ def buildModelFromFile(fname): return None,None,None -# Writes a compound model expression to file. The 'header' string -# contains the import statements that refer to each component type -# that appear in the expression. def _writeToFile(expression_string, model_directory, parent, header): + """ + Writes a compound model expression to file. + + Parameters + ---------- + expression_string: str + The model expression to write. + + model_directory: str + The path to write the model to. + parent: QtWidget + The parent widget the file dialog should belong to. + + header: str + The 'header' string contains the import statements that refer to + each component type that appear in the expression. + """ fname = QFileDialog.getSaveFileName(parent, 'Export to .py file', model_directory)[0] if len(fname) > 0: @@ -87,12 +123,26 @@ def _writeToFile(expression_string, model_directory, parent, header): f.write(expression_string) f.close() -# here we handle the case of a spectral model with a single component. -# It's not strictly a compound model, but saving and retrieving isolated -# components as if they were compound models makes for a simpler interface. -# Unfortunately, isolated components cannot be added to an already existing -# compound model if that model was fitted already. def _writeSingleComponentModel(model, model_directory, parent): + """ + Handle the case of a spectral model with a single component. + + It's not strictly a compound model, but saving and retrieving isolated + components as if they were compound models makes for a simpler interface. + Unfortunately, isolated components cannot be added to an already existing + compound model if that model was fitted already. + + Parameters + ---------- + model: `~astropy.modeling.models` + The model to write. + + model_directory: str + The path to write the model to. + + parent: QtWidget + The parent widget the file dialog should belong to. + """ name = _get_component_name(model) path = _get_component_path(model) @@ -102,10 +152,22 @@ def _writeSingleComponentModel(model, model_directory, parent): _writeToFile(expression_string, model_directory, parent, header) -# Builds a multi-component model expression inside a string, -# and dumps string to file. def _writeCompoundModel(model, model_directory, parent): + """ + Builds a multi-component model expression inside a string, + and dumps string to file. + + Parameters + ---------- + model: `~astropy.modeling.models` + The model to write. + + model_directory: str + The path to write the model to. + parent: QtWidget + The parent widget the file dialog should belong to. + """ # The following assumes that the formatted string expression # in an astropy compound model has operands of the form [0], [1], # etc, that is, a sequential number enclosed in square brackets. @@ -148,21 +210,55 @@ def _writeCompoundModel(model, model_directory, parent): _writeToFile(expression_string, model_directory, parent, header) -# Saves spectral model to file. This is the main entry -# point for the 'save to file' functionality. -# parent: optional QWidget used for screen centering. -# expression: not used for .py files. def saveModelToFile(parent, model, model_directory, expression=None): + """ + Saves spectral model to file. + + This is the main entry + point for the 'save to file' functionality. + parent: optional QWidget used for screen centering. + expression: not used for .py files. + + Parameters + ---------- + parent: QtWidget + The parent widget the file dialog should belong to. + + model: `~astropy.modeling.models` + The model to write. + + model_directory: str + The path to write the model to. + + expression: str + The model expression to write. + + """ if not hasattr(model, '_format_expression'): _writeSingleComponentModel(model, model_directory, parent) else: _writeCompoundModel(model, model_directory, parent) -# Disassembles a tie callable. Ties read from a model -# file are not directly accessible in text form because -# the model file is compiled at import time. def get_tie_text(tie): + """ + Disassembles a tie callable. + + Ties read from a model + file are not directly accessible in text form because + the model file is compiled at import time. + + Parameters + ---------- + tie: str + The model file to read from. + + Returns + ------- + parsed: + The parsed text. + If there is an issue, return 'False' + """ if tie: # dis() only outputs on standard output..... keep = sys.stdout @@ -177,13 +273,26 @@ def get_tie_text(tie): return result -# This parses the text returned by the disassembler for -# a lambda function that multiplies a constant by a -# variable. That is, we are assuming that ties are coded -# as lambda functions with multiplication by a constant, -# as in the STSDAS' specfit task. parser = re.compile(r'\(([^)]+)\)') # picks up whatever is enclosed in parenthesis def _parse_assembler_text(text): + """ + This parses the text returned by the disassembler for + a lambda function that multiplies a constant by a + variable. + + That is, we are assuming that ties are coded + as lambda functions with multiplication by a constant, + as in the STSDAS' specfit task. + + Parameters + ---------- + text: + The disassembled text + + Returns + ------- + The variable names and values in text format. + """ tokens = parser.findall(text) factor = tokens[0] lambda_variable_name = tokens[1] @@ -198,9 +307,21 @@ def _parse_assembler_text(text): par_name) -# Builds the text string that describes an operand (a spectral component) -# for an astropy compound model. def _assemble_component_spec(component): + """ + Builds the text string that describes an operand (a spectral component) + for an astropy compound model. + + Parameters + ---------- + component: `~astropy.modeling.models` + The component + + Returns + ------- + str + The component in text form + """ result = "" # function name - Note that get_component_name works From 516f87da716dca3a4be87705a405ae35c1326c03 Mon Sep 17 00:00:00 2001 From: "J.D.Eisenhamer" Date: Thu, 22 Dec 2016 12:22:26 -0500 Subject: [PATCH 09/21] Document module yaml_mode_io WIP #237 --- specviz/interfaces/model_io/py_model_io.py | 3 + specviz/interfaces/model_io/yaml_model_io.py | 257 ++++++++++++++++--- 2 files changed, 227 insertions(+), 33 deletions(-) diff --git a/specviz/interfaces/model_io/py_model_io.py b/specviz/interfaces/model_io/py_model_io.py index 7e3cb38d..7b8b98c0 100644 --- a/specviz/interfaces/model_io/py_model_io.py +++ b/specviz/interfaces/model_io/py_model_io.py @@ -49,10 +49,13 @@ def buildModelFromFile(fname): A 3-tuple consisting of: compound_model: `~astropy.modeling.models` The model from the file. + model_expression: str Currently always an empty string. + directory: str The path where the file was read from. + If any issues occur, the 3-tuple (None, None, None) is returned """ directory = os.path.dirname(fname) diff --git a/specviz/interfaces/model_io/yaml_model_io.py b/specviz/interfaces/model_io/yaml_model_io.py index 8b05a21c..d2298543 100644 --- a/specviz/interfaces/model_io/yaml_model_io.py +++ b/specviz/interfaces/model_io/yaml_model_io.py @@ -1,7 +1,7 @@ -# -# Functions in this module support the reading and writing -# of astropy's spectral compound models from/to YAML files. -# +""" +Functions in this module support the reading and writing +of astropy's spectral compound models from/to YAML files. +""" import os import sys @@ -28,11 +28,29 @@ def _get_model_class_name(function): return class_string.split('\'>')[0].split(".")[-1] - # ---------- From YAML file to model ------------------------' def _ingest_constraints(param_dict): + """ + Convert constraints from YAML to actual values + + Parameters + ---------- + param_dict: dict + The parameters from the parsed YAML + + Returns + ------- + (bounds, fixed, tied) + bounds: dict + The bounds as a dictionary of tuples + + fixed: dict + The fixed values + tied: dict + The tied components + """ bounds = param_dict['constraints']['bounds'] fixed = param_dict['constraints']['fixed'] tied = param_dict['constraints']['tied'] @@ -57,6 +75,23 @@ def _ingest_constraints(param_dict): def _build_single_model(in_map, model_name=None): + """ + Construct the model from the parsed YAML + + Parameters + ---------- + in_map: dict + The parsed YAML + + model_name: str + The name of the model. If None, use + the name from YAML + + Returns + ------- + `~astropy.modeling.models` + The reconstructed model. + """ if model_name is None: entry_name = list(in_map.keys())[0] else: @@ -88,10 +123,22 @@ def _build_single_model(in_map, model_name=None): return model -# If no arithmetic behavior expression is know, -# build a compound model by adding together all -# models present in the map. def _build_additive_model(in_map): + """ + If no arithmetic behavior expression is know, + build a compound model by adding together all + models present in the map. + + Parameters + ---------- + in_map: dict + The parsed YAML + + Returns + ------- + `~astropy.modeling.models` + The reconstructed model. + """ model = None for model_name in in_map[MODEL_NAME]: in_model = _build_single_model(in_map[MODEL_NAME], model_name=model_name) @@ -102,9 +149,21 @@ def _build_additive_model(in_map): return model -# If an arithmetic behavior expression is present, -# use it to build the compound model def _build_compound_model(in_map): + """ + If an arithmetic behavior expression is present, + use it to build the compound model + + Parameters + ---------- + in_map: dict + The parsed YAML + + Returns + ------- + Spectrum1DRefModelLayer + The reconstructed model. + """ model_list = [] for model_name in in_map[MODEL_NAME]: model_list.append(_build_single_model(in_map[MODEL_NAME], model_name=model_name)) @@ -126,6 +185,18 @@ def buildModelFromFile(fname): ---------- fname: str the ffully qualified file name + + Returns + ------- + (compound_model, model_expression, directory) + compound_model: `~astropy.modeling.models` + The model from the file. + + model_expression: str + The model expression. + + directory: str + The path where the file was read from. """ directory = os.path.dirname(fname) @@ -152,9 +223,21 @@ def buildModelFromFile(fname): # ---------- From model to YAML file ------------------------' -# Builds a dictionary with model constraints by directly -# referring to the _constraints attribute in a model. def _build_constraints_dict(model): + """ + Builds a dictionary with model constraints by directly + referring to the _constraints attribute in a model. + + Parameters + ---------- + model: `~astropy.modeling.models` + The model to get the constraints from. + + Returns + ------- + constriants_dict: dict + The constraints extracted from the model. + """ constraints_dict = copy.deepcopy(model._constraints) # bounds are stored as strings so @@ -174,8 +257,23 @@ def _build_constraints_dict(model): return constraints_dict -# From a single model, builds the dict to be output to YAML file. def _build_output_dict_single(model): + """ + From a single model, builds the dict to be output to YAML file. + + Parameters + ---------- + model: `~astropy.modeling.models` + The model to get the constraints from. + + Returns + (model_name, model_dict) + model_name: str + Name of the model + + model_dict: dict + Model informaiton broken into a dictionary + """ model_name = model.name param_dict = {} @@ -193,8 +291,23 @@ def _build_output_dict_single(model): return model_name, model_dict -# From a compound model, builds the dict to be output to YAML file. def _build_output_dict_compound(model): + """ + From a compound model, builds the dict to be output to YAML file. + + Parameters + ---------- + model: `~astropy.modeling.models` + The model to get the constraints from. + + Returns + (model_name, model_dict) + model_name: str + Name of the model + + model_dict: dict + Model informaiton broken into a dictionary + """ model_name = model.name param_dict = {} @@ -223,9 +336,22 @@ def _build_output_dict_compound(model): return model_name, model_dict -# Writes a dict to YAML file. def _writeToFile(out_model_dict, model_directory, parent): + """ + Writes a dict to YAML file. + Parameters + ---------- + out_model_dict: dict + The model dictionary to write. + + model_directory: str + The path to write to + + parent: QtWidget + The parent widget to make the file dialog belong to. + May be None. + """ fname = QFileDialog.getSaveFileName(parent, 'Save to file', model_directory)[0] if len(fname) > 0: @@ -238,11 +364,26 @@ def _writeToFile(out_model_dict, model_directory, parent): f.close() -# Handles the case of a spectral model with a single component. It's -# not strictly a compound model, but saving and retrieving isolated -# components as if they were compound models makes for a simpler -# interface. def _writeSingleComponentModel(model, model_directory, parent): + """ +Handles the case of a spectral model with a single component. + + It's not strictly a compound model, but saving and retrieving + isolated components as if they were compound models makes for a + simpler interface. + + Parameters + ---------- + model: dict + The model dictionary to write. + + model_directory: str + The path to write to + + parent: QtWidget + The parent widget to make the file dialog belong to. + May be None. + """ out_model_dict = {} model_name, model_dict = _build_output_dict_single(model) @@ -251,8 +392,25 @@ def _writeSingleComponentModel(model, model_directory, parent): _writeToFile(out_model_dict, model_directory, parent) -# Handles the case of a compound model def _writeCompoundModel(compound_model, model_directory, parent, expression): + """ + Handles the case of a compound model + + Parameters + ---------- + compound_model: dict + The model dictionary to write. + + model_directory: str + The path to write to + + parent: QtWidget + The parent widget to make the file dialog belong to. + May be None. + + expression: str + The formula associationed with the model. + """ out_model_dict = {MODEL_NAME:{}} for model in compound_model: @@ -273,10 +431,17 @@ def saveModelToFile(parent, model, model_directory, expression=None): Parameters ---------- - parent : QWidget or None - optional widget used for screen centering. + parent: QtWidget or None + Optional widget used for screen centering. + + model: dict + The model dictionary to write. + + model_directory: str + The path to write to + expression: str - the formula associated with the compound model + The formula associated with the compound model """ if not hasattr(model, '_format_expression'): _writeSingleComponentModel(model, model_directory, parent) @@ -289,10 +454,25 @@ def saveModelToFile(parent, model, model_directory, expression=None): # Utility functions that might be used when we implement ties. # -# Disassembles a tie callable. Ties read from a model -# file are not directly accessible in text form because -# the model file is compiled at import time. def get_tie_text(tie): + """ + Disassembles a tie callable. + + Ties read from a model + file are not directly accessible in text form because + the model file is compiled at import time. + + Parameters + ---------- + tie: str + The model file to read from. + + Returns + ------- + parsed: + The parsed text. + If there is an issue, return 'False' + """ if tie: # dis() only outputs on standard output..... keep = sys.stdout @@ -307,13 +487,26 @@ def get_tie_text(tie): return result -# This parses the text returned by the disassembler for -# a lambda function that multiplies a constant by a -# variable. That is, we are assuming that ties are coded -# as lambda functions with multiplication by a constant, -# as in the STSDAS' specfit task. parser = re.compile(r'\(([^)]+)\)') # picks up whatever is enclosed in parenthesis def _parse_assembler_text(text): + """ + This parses the text returned by the disassembler for + a lambda function that multiplies a constant by a + variable. + + That is, we are assuming that ties are coded + as lambda functions with multiplication by a constant, + as in the STSDAS' specfit task. + + Parameters + ---------- + text: + The disassembled text + + Returns + ------- + The variable names and values in text format. + """ tokens = parser.findall(text) factor = tokens[0] lambda_variable_name = tokens[1] @@ -326,5 +519,3 @@ def _parse_assembler_text(text): lambda_variable_name, function_id, par_name) - - From 7fd324401ca3dd984ba8b622d098fe33a3570855 Mon Sep 17 00:00:00 2001 From: "J.D.Eisenhamer" Date: Thu, 22 Dec 2016 14:37:31 -0500 Subject: [PATCH 10/21] Document the factories module WIP #237 --- specviz/interfaces/factories.py | 63 ++++++++++++++++++++++++++------- 1 file changed, 51 insertions(+), 12 deletions(-) diff --git a/specviz/interfaces/factories.py b/specviz/interfaces/factories.py index 07951db0..7c91053f 100644 --- a/specviz/interfaces/factories.py +++ b/specviz/interfaces/factories.py @@ -1,3 +1,6 @@ +""" +App-wide factories +""" from __future__ import (absolute_import, division, print_function, unicode_literals) @@ -21,18 +24,23 @@ class Factory(object): #TODO a base class for Model and Fitter classes might be of help here. class ModelFactory(Factory): - - # Ideally we should be getting these classes from astropy directly and - # transparently, instead of explicitly naming them here. This is basically - # a maintenance issue: at each new release of astropy we should check if - # new models became available, and existing models got deprecated. - # - # This might not be possible unless astropy itself somehow manages to - # further subclass its Fittable1DModel class into spectrum-specific and - # other types. Right now we have a mix of spectral models, galaxy surface - # brightness models, and others, all lumped into a single type. Thus we - # have to keep picking the spectral relevant types by hand for the time - # being. + """ + Create a model + + Notes + ----- + Ideally we should be getting these classes from astropy directly and + transparently, instead of explicitly naming them here. This is basically + a maintenance issue: at each new release of astropy we should check if + new models became available, and existing models got deprecated. + + This might not be possible unless astropy itself somehow manages to + further subclass its Fittable1DModel class into spectrum-specific and + other types. Right now we have a mix of spectral models, galaxy surface + brightness models, and others, all lumped into a single type. Thus we + have to keep picking the spectral relevant types by hand for the time + being. + """ all_models = { 'Gaussian': models.Gaussian1D, 'GaussianAbsorption': models.GaussianAbsorption1D, @@ -64,6 +72,20 @@ class ModelFactory(Factory): @classmethod def create_model(cls, name): + """ + Create a model + + Parameters + ---------- + name: str + The name of the model desired. + + Returns + ------- + model: `~astropy.modeling.models` + The requested model. None if the requested + model does not exist. + """ name = str(name) if name in cls.all_models: @@ -73,6 +95,9 @@ def create_model(cls, name): class FitterFactory(Factory): + """ + Create a fitter + """ default_fitter = fitting.LevMarLSQFitter all_fitters = { @@ -83,6 +108,20 @@ class FitterFactory(Factory): @classmethod def create_fitter(cls, name): + """ + Create a fitter + + Parameters + ---------- + name: str + The name of the fitter desired. + + Returns + ------- + fitter: `~astropy.fitting.Fitter` + The fitter class requested. + None if the requested fitter does not exist. + """ name = str(name) if name in cls.all_fitters: From 3791d56ebfaabb2285fb80051c459a9ce7e0f670 Mon Sep 17 00:00:00 2001 From: "J.D.Eisenhamer" Date: Thu, 22 Dec 2016 14:52:40 -0500 Subject: [PATCH 11/21] Document the initializers module WIP #237 --- specviz/interfaces/initializers.py | 158 ++++++++++++++++++++++++----- 1 file changed, 131 insertions(+), 27 deletions(-) diff --git a/specviz/interfaces/initializers.py b/specviz/interfaces/initializers.py index dedbb61c..c7f99845 100644 --- a/specviz/interfaces/initializers.py +++ b/specviz/interfaces/initializers.py @@ -1,9 +1,12 @@ -# This module is used to initialize spectral models to the -# data at hand. This is used by model-fitting code that has -# to create spectral model instances with sensible parameter -# values such that they can be used as first guesses by the -# fitting algorithms. - +""" +This module is used to initialize spectral models to the +data at hand. + +This is used by model-fitting code that has +to create spectral model instances with sensible parameter +values such that they can be used as first guesses by the +fitting algorithms. +""" import numpy as np from ..analysis.models import spline, blackbody @@ -18,13 +21,33 @@ def _get_model_name(model): return class_string.split('\'>')[0].split(".")[-1] -# Initialization that is specific to the Linear1D model. -# In a way, we need this specialized initializer because -# the linear 1D model is more like a kind of polynomial. -# It doesn't mesh well with other non-linear models. class _Linear1DInitializer(object): - + """ + Initialization that is specific to the Linear1D model. + + Notes + ----- + In a way, we need this specialized initializer because + the linear 1D model is more like a kind of polynomial. + It doesn't mesh well with other non-linear models. + """ def initialize(self, instance, x, y): + """ + Initialize the model + + Parameters + ---------- + instance: `~astropy.modeling.models` + The model to initialize. + + x, y: numpy.ndarray + The data to use to initialize from. + + Returns + ------- + instance: `~astropy.modeling.models` + The initialized model. + """ # y_range = np.max(y) - np.min(y) # x_range = x[-1] - x[0] @@ -39,17 +62,40 @@ def initialize(self, instance, x, y): return instance -# Initialization that is applicable to all "wide band" -# models, that is, models that have an amplitude and -# a position in wavelength space where this amplitude -# is defined. class _WideBand1DInitializer(object): - + """ + Initialization that is applicable to all "wide band" + models + + A "wide band" model is one that has an amplitude and + a position in wavelength space where this amplitude + is defined. + + Parameters + ---------- + factor: float + The scale factor to apply to the amplitutde + """ def __init__(self, factor=1.0): self._factor = factor def initialize(self, instance, x, y): - + """ + Initialize the model + + Parameters + ---------- + instance: `~astropy.modeling.models` + The model to initialize. + + x, y: numpy.ndarray + The data to use to initialize from. + + Returns + ------- + instance: `~astropy.modeling.models` + The initialized model. + """ y_mean = np.mean(y) x_range = x[-1] - x[0] position = x_range / 2.0 + x[0] @@ -62,15 +108,39 @@ def initialize(self, instance, x, y): return instance -# Initialization that is applicable to all "line profile" -# models, that is, models that have an amplitude, a width, -# and a defined position in wavelength space. class _LineProfile1DInitializer(object): - + """ + Initialization that is applicable to all "line profile" + models. + + A "line profile" model is one that has an amplitude, a width, + and a defined position in wavelength space. + + Parameters + ---------- + factor: float + The scale factor to apply to the amplitutde + """ def __init__(self, factor=1.0): self._factor = factor def initialize(self, instance, x, y): + """ + Initialize the model + + Parameters + ---------- + instance: `~astropy.modeling.models` + The model to initialize. + + x, y: numpy.ndarray + The data to use to initialize from. + + Returns + ------- + instance: `~astropy.modeling.models` + The initialized model. + """ # X centroid estimates the position centroid = np.sum(x * y) / np.sum(y) @@ -93,10 +163,27 @@ def initialize(self, instance, x, y): return instance -# Sets parameter value by mapping parameter name to model type. -# Prevents the parameter value setting to be stopped on its tracks -# by non-existent model names or parameter names. def _setattr(instance, mname, pname, value): + """ + Sets parameter value by mapping parameter name to model type. + + Prevents the parameter value setting to be stopped on its tracks + by non-existent model names or parameter names. + + Parameters + ---------- + instance: `~astropy.modeling.models` + The model to initialize. + + mname: str + Model name. + + pname: str + Parameter name. + + value: any + The value to assign. + """ try: # conflicts happen when we try to set a parameter value # as a Quantity. Use the raw value instead. @@ -147,10 +234,27 @@ def _setattr(instance, mname, pname, value): } -# Main entry point. X and Y are for now Quantity arrays with the -# independent and dependent variables. It's assumed X values -# are stored in increasing order in the array. def initialize(instance, x, y): + """ + Main entry point. X and Y are for now Quantity arrays with the + independent and dependent variables. It's assumed X values + are stored in increasing order in the array. + + Parameters + ---------- + instance: `~astropy.modeling.models` + The model to initialize. + + x, y: numpy.ndarray + The data to use to initialize from. + + Returns + ------- + instance: `~astropy.modeling.models` + The initialized model. + If there are any errors, the instance is returned + uninitialized. + """ if x is None or y is None: return instance From 64a51ac590fdcb0163a722ee431ed555d0ec20fa Mon Sep 17 00:00:00 2001 From: "J.D.Eisenhamer" Date: Thu, 22 Dec 2016 15:00:18 -0500 Subject: [PATCH 12/21] Document the registries module WIP #237 --- specviz/interfaces/registries.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/specviz/interfaces/registries.py b/specviz/interfaces/registries.py index 78732090..2536b204 100644 --- a/specviz/interfaces/registries.py +++ b/specviz/interfaces/registries.py @@ -1,3 +1,6 @@ +""" +Registry library +""" from __future__ import (absolute_import, division, print_function, unicode_literals) @@ -18,9 +21,15 @@ 'PluginRegistry', 'LoaderRegistry'] + class Registry(object): """ Maintains a set of referential objects. + + Attributes + ---------- + members: list + The list of members belonging to this registry. """ def __init__(self): self._members = [] @@ -76,6 +85,8 @@ def __init__(self): class LoaderRegistry(Registry): + """Loads and stores the IO data loaders + """ def __init__(self): super(LoaderRegistry, self).__init__() @@ -172,6 +183,8 @@ def _load_yaml(self): class YAMLLoader(yaml.YAMLObject): + """ Helper to load YAML files + """ yaml_tag = u'!CustomLoader' def __init__(self, extension, name, data, dispersion, uncertainty, mask, From 3ad0a245c3bb065fd97030177730b02db69e8828 Mon Sep 17 00:00:00 2001 From: "J.D.Eisenhamer" Date: Thu, 22 Dec 2016 15:24:11 -0500 Subject: [PATCH 13/21] Document the generic_loader module WIP #237 --- specviz/io/loaders/generic_loader.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/specviz/io/loaders/generic_loader.py b/specviz/io/loaders/generic_loader.py index 31062bf1..ba4c6a36 100644 --- a/specviz/io/loaders/generic_loader.py +++ b/specviz/io/loaders/generic_loader.py @@ -1,3 +1,6 @@ +""" +Generic loader definitions +""" import os from astropy.io import fits @@ -11,6 +14,7 @@ __all__ = ['fits_identify', 'simple_generic_loader'] + def fits_identify(*args, **kwargs): """ Check whether given filename is FITS. This is used for Astropy I/O @@ -22,6 +26,23 @@ def fits_identify(*args, **kwargs): @data_loader(label="Simple Fits", identifier=fits_identify) def simple_generic_loader(file_name, **kwargs): + """ + Basic FITS file loader + + Presumption is the primary data is a table with columns 'flux' + and 'err'. The dispersion information is encoded in the FITS + header keywords. + + Parameters + ---------- + file_name: str + The path to the FITS file + + Returns + ------- + data: Spectrum1DRef + The data. + """ name = os.path.basename(file_name.name.rstrip(os.sep)).rsplit('.', 1)[0] hdulist = fits.open(file_name, **kwargs) From f7ab18e65bca15139c0b4963854312cfc6097de2 Mon Sep 17 00:00:00 2001 From: "J.D.Eisenhamer" Date: Thu, 22 Dec 2016 15:28:14 -0500 Subject: [PATCH 14/21] Document all remaning IO loaders WIP #237 --- specviz/io/loaders/hstcos_loader.py | 10 ++++++++++ specviz/io/loaders/hststis_loader.py | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/specviz/io/loaders/hstcos_loader.py b/specviz/io/loaders/hstcos_loader.py index 7e30f46c..67fa1f1c 100644 --- a/specviz/io/loaders/hstcos_loader.py +++ b/specviz/io/loaders/hstcos_loader.py @@ -9,6 +9,7 @@ __all__ = ['cos_identify', 'cos_spectrum_loader'] + def cos_identify(*args, **kwargs): """ Check whether given file contains HST/COS spectral data. @@ -25,6 +26,15 @@ def cos_identify(*args, **kwargs): def cos_spectrum_loader(file_name, **kwargs): """ Load file from COS spectral data into a spectrum object + Parameters + ---------- + file_name: str + The path to the FITS file + + Returns + ------- + data: Spectrum1DRef + The data. """ name = os.path.basename(file_name) diff --git a/specviz/io/loaders/hststis_loader.py b/specviz/io/loaders/hststis_loader.py index 12cda560..ea94b4fb 100644 --- a/specviz/io/loaders/hststis_loader.py +++ b/specviz/io/loaders/hststis_loader.py @@ -9,6 +9,7 @@ __all__ = ['stis_identify', 'stis_spectrum_loader'] + def stis_identify(*args, **kwargs): """ Check whether given file contains HST/STIS spectral data. @@ -25,6 +26,15 @@ def stis_identify(*args, **kwargs): def stis_spectrum_loader(file_name, **kwargs): """ Load file from STIS spectral data into a spectrum object + Parameters + ---------- + file_name: str + The path to the FITS file + + Returns + ------- + data: Spectrum1DRef + The data. """ name = os.path.basename(file_name) From 3c3af14efe1bdfb727264298ce493d05dea1ee42 Mon Sep 17 00:00:00 2001 From: "J.D.Eisenhamer" Date: Thu, 22 Dec 2016 16:17:33 -0500 Subject: [PATCH 15/21] Document the axes module WIP #237 --- specviz/ui/widgets/axes.py | 41 +++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/specviz/ui/widgets/axes.py b/specviz/ui/widgets/axes.py index 0b52495e..dc1f4a45 100644 --- a/specviz/ui/widgets/axes.py +++ b/specviz/ui/widgets/axes.py @@ -6,6 +6,21 @@ class DynamicAxisItem(pg.AxisItem): """ + Axis that understand spectral modes + + Attributes + ---------- + mode: int + Display mode. Can be one of: + 0: velocity + 1: redshift + 2: pixel (default) + + redshift: float + Reference redshift. + + ref_wave: float + Reference wavelength for velocity determination. """ def __init__(self, *args, **kwargs): super(DynamicAxisItem, self).__init__(*args, **kwargs) @@ -16,6 +31,25 @@ def __init__(self, *args, **kwargs): self.ref_wave = 0.0 def update_axis(self, layer, mode, **kwargs): + """ + Update axis display + + Parameters + ---------- + layer: Spectrum1DRefLayer + The layer to update + + mode: int + The display mode to use. + + ref_wave: float + if `mode` == 0, velocity, this specfies + the reference wavelength. + + redshift: float + If `mode` == `, redshift, this + specifies the redshift. + """ self.mode = mode if self.mode == 0: @@ -31,6 +65,11 @@ def update_axis(self, layer, mode, **kwargs): self.show() def tickStrings(self, values, scale, spacing): + """ + Defines the tick marking format based on display mode + + See `~pygtgraph.AxisItem` for parameter definitions. + """ if self._layer is None: return super(DynamicAxisItem, self).tickStrings(values, scale, spacing) @@ -64,4 +103,4 @@ def tickStrings(self, values, scale, spacing): return list(inds) - return super(DynamicAxisItem, self).tickStrings(values, scale, spacing) \ No newline at end of file + return super(DynamicAxisItem, self).tickStrings(values, scale, spacing) From ff9c2bacf3ea7e0ee73c3500c0ab0c4b53aaced6 Mon Sep 17 00:00:00 2001 From: "J.D.Eisenhamer" Date: Thu, 22 Dec 2016 16:19:54 -0500 Subject: [PATCH 16/21] Document the dialogs module WIP #237 --- specviz/ui/widgets/dialogs.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/specviz/ui/widgets/dialogs.py b/specviz/ui/widgets/dialogs.py index 104e19dc..3b932d11 100644 --- a/specviz/ui/widgets/dialogs.py +++ b/specviz/ui/widgets/dialogs.py @@ -1,3 +1,6 @@ +""" +UI Dialog definitions +""" from qtpy.QtWidgets import * from qtpy.QtGui import * from qtpy.QtCore import * From f7bbc1023a821d7c2c4eb86d1e7b3add24fdeb39 Mon Sep 17 00:00:00 2001 From: "J.D.Eisenhamer" Date: Thu, 22 Dec 2016 16:27:07 -0500 Subject: [PATCH 17/21] Document all modules in the widgets directory Document updates were minimal as all classes are Qt-based or pyqtgraph-based subclasses. WIP #237 --- specviz/ui/widgets/linelists_window.py | 3 +++ specviz/ui/widgets/menus.py | 12 ++++++++++++ specviz/ui/widgets/region_items.py | 4 +++- specviz/ui/widgets/sub_windows.py | 6 ++++-- specviz/ui/widgets/windows.py | 3 +++ 5 files changed, 25 insertions(+), 3 deletions(-) diff --git a/specviz/ui/widgets/linelists_window.py b/specviz/ui/widgets/linelists_window.py index 3bcccab0..38f5a745 100644 --- a/specviz/ui/widgets/linelists_window.py +++ b/specviz/ui/widgets/linelists_window.py @@ -1,3 +1,6 @@ +""" +Define all the line list-based windows and dialogs +""" from qtpy.QtWidgets import * from qtpy.QtGui import * from qtpy.QtCore import * diff --git a/specviz/ui/widgets/menus.py b/specviz/ui/widgets/menus.py index 23c1085a..81be95f2 100644 --- a/specviz/ui/widgets/menus.py +++ b/specviz/ui/widgets/menus.py @@ -1,14 +1,23 @@ +""" +Base contextual menu classes +""" from __future__ import (absolute_import, division, print_function, unicode_literals) from qtpy.QtWidgets import * class BaseContextMenu(QMenu): + """ + Base class for all context menus + """ def __init__(self, *args, **kwargs): super(BaseContextMenu, self).__init__(*args, **kwargs) class LayerContextMenu(BaseContextMenu): + """ + Base class for Layer-based contextual menus + """ def __init__(self, *args, **kwargs): super(LayerContextMenu, self).__init__(*args, **kwargs) @@ -18,6 +27,9 @@ def __init__(self, *args, **kwargs): class ModelContextMenu(BaseContextMenu): + """ + Base class for all Model-based contextual menus + """ def __init__(self, *args, **kwargs): super(ModelContextMenu, self).__init__(*args, **kwargs) diff --git a/specviz/ui/widgets/region_items.py b/specviz/ui/widgets/region_items.py index 9dd80df1..96ac3701 100644 --- a/specviz/ui/widgets/region_items.py +++ b/specviz/ui/widgets/region_items.py @@ -5,6 +5,9 @@ class LinearRegionItem(pg.LinearRegionItem): + """ + Linear Region Item + """ sigHoverEvent = QtCore.Signal(object) sigRemoveRequested = QtCore.Signal(object) sigClicked = QtCore.Signal(object, object) @@ -63,4 +66,3 @@ def mouseClickEvent(self, ev): self.sigClicked.emit(self, ev) else: ev.ignore() - diff --git a/specviz/ui/widgets/sub_windows.py b/specviz/ui/widgets/sub_windows.py index fe658335..9c18e036 100644 --- a/specviz/ui/widgets/sub_windows.py +++ b/specviz/ui/widgets/sub_windows.py @@ -29,6 +29,9 @@ class UiPlotSubWindow(QMainWindow): + """ + Main plotting window. + """ def __init__(self, *args, **kwargs): super(UiPlotSubWindow, self).__init__(*args, **kwargs) @@ -353,7 +356,7 @@ def _plot_linelists(self, table_views, **kwargs): # Build new line lists with only the selected rows. linelists_with_selections = [] - + for table_view in table_views: # Find matching line list by its name. This could be made # simpler by the use of a dict. That breaks code elsewhere @@ -452,4 +455,3 @@ def _dismiss_linelists_window(self, *args, **kwargs): if self._is_selected and self._linelist_window: self._linelist_window.hide() self._linelist_window = None - diff --git a/specviz/ui/widgets/windows.py b/specviz/ui/widgets/windows.py index 7656f0c9..8bfd7e50 100644 --- a/specviz/ui/widgets/windows.py +++ b/specviz/ui/widgets/windows.py @@ -7,6 +7,9 @@ class UiMainWindow(QMainWindow): + """ + Main application window + """ def __init__(self, parent=None): super(UiMainWindow, self).__init__(parent) DispatchHandle.setup(self) From ec81bcd4d71ed295b348bcb3413cc828e09bb0bc Mon Sep 17 00:00:00 2001 From: "J.D.Eisenhamer" Date: Thu, 22 Dec 2016 16:45:02 -0500 Subject: [PATCH 18/21] Document all plugin modules Basically this just involved adding a basic doc string to all modules and top-level classes WIP #237 --- specviz/plugins/data_list_plugin.py | 6 ++++++ specviz/plugins/layer_list_plugin.py | 8 +++++++- specviz/plugins/model_fitting_plugin.py | 6 ++++++ specviz/plugins/plot_tools_plugin.py | 6 ++++++ specviz/plugins/statistics_plugin.py | 6 ++++++ specviz/plugins/tool_tray_plugin.py | 6 ++++++ 6 files changed, 37 insertions(+), 1 deletion(-) diff --git a/specviz/plugins/data_list_plugin.py b/specviz/plugins/data_list_plugin.py index 1bf76ac2..921450bf 100644 --- a/specviz/plugins/data_list_plugin.py +++ b/specviz/plugins/data_list_plugin.py @@ -1,3 +1,6 @@ +""" +Plugin to manage the loaded data +""" import os from ..ui.widgets.plugin import Plugin @@ -15,6 +18,9 @@ class DataListPlugin(Plugin): + """ + UI plugin to manage the data. + """ name = "Data List" location = "left" diff --git a/specviz/plugins/layer_list_plugin.py b/specviz/plugins/layer_list_plugin.py index d8d157b6..9f971b7b 100644 --- a/specviz/plugins/layer_list_plugin.py +++ b/specviz/plugins/layer_list_plugin.py @@ -1,3 +1,6 @@ +""" +Plugin to manage the layers +""" import os from ..ui.widgets.plugin import Plugin @@ -15,6 +18,9 @@ class LayerListPlugin(Plugin): + """ + UI plugin to manage the data layers + """ name = "Layer List" location = "left" @@ -381,4 +387,4 @@ def __init__(self, plugin): plugin.layout_vertical.addLayout(plugin.layout_horizontal) - plugin.dialog_layer_arithmetic = LayerArithmeticDialog() \ No newline at end of file + plugin.dialog_layer_arithmetic = LayerArithmeticDialog() diff --git a/specviz/plugins/model_fitting_plugin.py b/specviz/plugins/model_fitting_plugin.py index f9d51548..1e296302 100644 --- a/specviz/plugins/model_fitting_plugin.py +++ b/specviz/plugins/model_fitting_plugin.py @@ -1,3 +1,6 @@ +""" +Plugin enabling model definition and fitting +""" import os from ..ui.widgets.plugin import Plugin @@ -21,6 +24,9 @@ class ModelFittingPlugin(Plugin): + """ + UI plugin for model definition, fitting, and management + """ name = "Model Fitting" location = "right" diff --git a/specviz/plugins/plot_tools_plugin.py b/specviz/plugins/plot_tools_plugin.py index fbc83704..c715d0f7 100644 --- a/specviz/plugins/plot_tools_plugin.py +++ b/specviz/plugins/plot_tools_plugin.py @@ -1,3 +1,6 @@ +""" +Manage plot attributes +""" import os import logging from collections import OrderedDict @@ -11,6 +14,9 @@ class PlotToolsPlugin(Plugin): + """ + UI plugin to manage plot attributes of the various layers + """ name = "Plot Tools" location = "hidden" _all_categories = {} diff --git a/specviz/plugins/statistics_plugin.py b/specviz/plugins/statistics_plugin.py index 9a8a4866..10fec7ea 100644 --- a/specviz/plugins/statistics_plugin.py +++ b/specviz/plugins/statistics_plugin.py @@ -1,3 +1,6 @@ +""" +Manage and execute the various statistical operations +""" from ..ui.widgets.plugin import Plugin from qtpy.QtWidgets import * from qtpy.QtCore import * @@ -16,6 +19,9 @@ class StatisticsPlugin(Plugin): + """ + UI to manage and execute the statistical operations + """ name = "Statistics" location = "left" diff --git a/specviz/plugins/tool_tray_plugin.py b/specviz/plugins/tool_tray_plugin.py index 9d226253..f9e459ba 100644 --- a/specviz/plugins/tool_tray_plugin.py +++ b/specviz/plugins/tool_tray_plugin.py @@ -1,3 +1,6 @@ +""" +Holder for the general UI operations +""" import os from ..ui.widgets.plugin import Plugin @@ -5,6 +8,9 @@ class ToolTrayPlugin(Plugin): + """ + UI plugin for the general UI operations + """ name = "Tools" location = "hidden" priority = 0 From bf200d8ef62c1277f08945511b9cbe872aa84e6c Mon Sep 17 00:00:00 2001 From: "J.D.Eisenhamer" Date: Wed, 28 Dec 2016 08:56:09 -0500 Subject: [PATCH 19/21] Update astropy/scipy links --- specviz/analysis/models/blackbody.py | 7 ++++--- specviz/analysis/models/spline.py | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/specviz/analysis/models/blackbody.py b/specviz/analysis/models/blackbody.py index d31ad425..c615de0d 100644 --- a/specviz/analysis/models/blackbody.py +++ b/specviz/analysis/models/blackbody.py @@ -15,11 +15,12 @@ class BlackBody(Fittable1DModel): Notes ----- - See `astropy.modeling.Fittable1DModel `_ - for further details. + See `~astropy.modeling.Fittable1DModel` + for further details on modeling and all + possible parameters that can be passed in. Description of the blackbody function itself is described in - `astropy.analytic_functions `_ + `~astropy.analytic_functions.blackbody` """ temp = Parameter(default=5000, min=10.) norm = Parameter(default=1.) diff --git a/specviz/analysis/models/spline.py b/specviz/analysis/models/spline.py index f2250820..6d624b54 100644 --- a/specviz/analysis/models/spline.py +++ b/specviz/analysis/models/spline.py @@ -13,11 +13,11 @@ class Spline1D(Fittable1DModel): Notes ----- See - `astropy.modeling.Fittable1DModel `_ + `~astropy.modeling.Fittable1DModel` for further details. The spline function is based on - `scipy.interpolate.UnivariateSpline `_ + `~scipy.interpolate.UnivariateSpline` """ degree = Parameter(default=3, fixed=True) smooth = Parameter(default=1, fixed=True) # default=None crashes the app From 33f71910ae56a1236ce6f528372d5821cab7f774 Mon Sep 17 00:00:00 2001 From: "J.D.Eisenhamer" Date: Wed, 28 Dec 2016 14:09:47 -0500 Subject: [PATCH 20/21] Full documentation of the core code WIP #237 --- docs/source/api.rst | 46 ++++++++++++++++++++++++++++++++++----- specviz/core/comms.py | 2 +- specviz/core/data.py | 12 ++++++++-- specviz/core/linelist.py | 8 +++++-- specviz/core/plots.py | 30 +++++++++++++++---------- specviz/core/threads.py | 6 ++++- specviz/io/yaml_loader.py | 9 +++++--- 7 files changed, 87 insertions(+), 26 deletions(-) diff --git a/docs/source/api.rst b/docs/source/api.rst index eccc7402..cdc97d05 100644 --- a/docs/source/api.rst +++ b/docs/source/api.rst @@ -4,22 +4,58 @@ SpecViz API Analysis Functions ------------------ .. automodapi:: specviz.analysis - :no-heading: + :no-heading: SpecViz core ------------ -.. automodapi:: specviz.core - :no-heading: +Data Objects +^^^^^^^^^^^^ +.. automodapi:: specviz.core.data + :no-heading: + :no-main-docstr: + :headings:"" +Object Event Handling +^^^^^^^^^^^^^^^^^^^^^ +.. automodapi:: specviz.core.comms + :no-heading: + :no-main-docstr: + :headings:"" + +Spectrum Layer Plotting +^^^^^^^^^^^^^^^^^^^^^^^ +.. automodapi:: specviz.core.plots + :no-heading: + :no-main-docstr: + :headings:"" + +Emission/Absorption Line list utilities +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. automodapi:: specviz.core.linelist + :no-heading: + :no-main-docstr: + :headings:"" + +Thread Helpers +^^^^^^^^^^^^^^ +.. automodapi:: specviz.core.threads + :no-heading: + :no-main-docstr: + :headings:"" + + Interfaces ---------- .. automodapi:: specviz.interfaces - :no-heading: + :no-heading: I/O module ---------- .. automodapi:: specviz.io - :no-heading: + :no-heading: + +.. automodapi:: specviz.io.yaml_loader + :no-heading: diff --git a/specviz/core/comms.py b/specviz/core/comms.py index 0e87b9c0..847f8a09 100644 --- a/specviz/core/comms.py +++ b/specviz/core/comms.py @@ -1,7 +1,7 @@ """ Object Event Handling -Elevator Pitch: The singleton `Dispatch` object manages the +The singleton `Dispatch` object manages the set of `EventNode` events. Handlers or **listeners** are attached to `EventNode`s. The `DispatchHandle` decorator is used to decorate classes that handle events. diff --git a/specviz/core/data.py b/specviz/core/data.py index b46effdc..f89c24fb 100644 --- a/specviz/core/data.py +++ b/specviz/core/data.py @@ -1,10 +1,11 @@ -"""This module handles spectrum data objects.""" +""" +Data Objects +""" from __future__ import (absolute_import, division, print_function, unicode_literals) # STDLIB import logging -logging.basicConfig(level=logging.INFO) import re # THIRD-PARTY @@ -13,6 +14,13 @@ from ..third_party.py_expression_eval import Parser from specutils.core.generic import Spectrum1DRef +logging.basicConfig(level=logging.INFO) + +__all__ = [ + 'Spectrum1DRefLayer', + 'Spectrum1DRefModelLayer', +] + class Spectrum1DRefLayer(Spectrum1DRef): """ diff --git a/specviz/core/linelist.py b/specviz/core/linelist.py index 2b2b0bd3..5a14369d 100644 --- a/specviz/core/linelist.py +++ b/specviz/core/linelist.py @@ -1,5 +1,5 @@ """ -Utilities and classes to handle Emission/Absorption Line lists +Emission/Absorption Line list utilities """ from __future__ import (absolute_import, division, print_function, unicode_literals) @@ -10,6 +10,11 @@ from astropy.table import Table, vstack +__all__ = [ + 'LineList', + 'ingest', +] + FORMAT = 'line_list' COLUMN_NAME = 'name' COLUMN_START = 'start' @@ -18,7 +23,6 @@ ID_COLUMN = 'Line ID' UNITS_COLUMN = 'units' - def ingest(range): """ Returns a list with LineList instances. diff --git a/specviz/core/plots.py b/specviz/core/plots.py index b13d2181..a034ed0e 100644 --- a/specviz/core/plots.py +++ b/specviz/core/plots.py @@ -1,5 +1,5 @@ """ -Line plotting classes +Spectrum Layer Plotting """ from __future__ import (absolute_import, division, print_function, unicode_literals) @@ -12,17 +12,23 @@ import logging import numpy as np -AVAILABLE_COLORS = cycle([(0, 0, 0), - (0.2980392156862745, 0.4470588235294118, 0.6901960784313725), - (0.3333333333333333, 0.6588235294117647, 0.40784313725490196), - (0.7686274509803922, 0.3058823529411765, 0.3215686274509804), - (0.5058823529411764, 0.4470588235294118, 0.6980392156862745), - (0.8, 0.7254901960784313, 0.4549019607843137), - (0.39215686274509803, 0.7098039215686275, 0.803921568627451), - (0.2980392156862745, 0.4470588235294118, 0.6901960784313725), - (0.3333333333333333, 0.6588235294117647, 0.40784313725490196), - (0.7686274509803922, 0.3058823529411765, 0.3215686274509804), - (0.5058823529411764, 0.4470588235294118, 0.6980392156862745)]) +__all__ = [ + 'LinePlot', +] + +AVAILABLE_COLORS = cycle([ + (0, 0, 0), + (0.2980392156862745, 0.4470588235294118, 0.6901960784313725), + (0.3333333333333333, 0.6588235294117647, 0.40784313725490196), + (0.7686274509803922, 0.3058823529411765, 0.3215686274509804), + (0.5058823529411764, 0.4470588235294118, 0.6980392156862745), + (0.8, 0.7254901960784313, 0.4549019607843137), + (0.39215686274509803, 0.7098039215686275, 0.803921568627451), + (0.2980392156862745, 0.4470588235294118, 0.6901960784313725), + (0.3333333333333333, 0.6588235294117647, 0.40784313725490196), + (0.7686274509803922, 0.3058823529411765, 0.3215686274509804), + (0.5058823529411764, 0.4470588235294118, 0.6980392156862745) +]) class LinePlot(object): diff --git a/specviz/core/threads.py b/specviz/core/threads.py index bb3e5172..52ddfca2 100644 --- a/specviz/core/threads.py +++ b/specviz/core/threads.py @@ -1,5 +1,5 @@ """ -Threaded tasks +Thread Helpers """ from qtpy.QtCore import QThread, Signal import os @@ -10,6 +10,10 @@ import astropy.io.registry as io_registry +__all__ = [ + 'FileLoadThread', + 'FitModelThread', +] class FileLoadThread(QThread): """ diff --git a/specviz/io/yaml_loader.py b/specviz/io/yaml_loader.py index 2ac1f731..229ba499 100644 --- a/specviz/io/yaml_loader.py +++ b/specviz/io/yaml_loader.py @@ -19,9 +19,12 @@ from specviz.core import linelist from specviz.core.linelist import LineList -__all__ = ['fits_reader', 'fits_identify', - 'ascii_reader', 'ascii_identify', - 'linelist_reader', 'linelist_identify'] +__all__ = [ + 'AsciiYamlRegister', + 'FitsYamlRegister', + 'LineListYamlRegister', + 'YamlRegister', +] # Loader automatically falls back to these units for some cases default_waveunit = u.Unit('Angstrom') From d7db979094839fb9521665db950e96e0e70954f4 Mon Sep 17 00:00:00 2001 From: "J.D.Eisenhamer" Date: Thu, 29 Dec 2016 09:04:17 -0500 Subject: [PATCH 21/21] Completed incorporation of the interfaces documentation WIP #237 --- docs/source/api.rst | 14 ++++++++++++++ specviz/interfaces/factories.py | 5 +++++ specviz/interfaces/initializers.py | 8 +++++++- 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/docs/source/api.rst b/docs/source/api.rst index cdc97d05..64d7bf80 100644 --- a/docs/source/api.rst +++ b/docs/source/api.rst @@ -51,7 +51,21 @@ Interfaces .. automodapi:: specviz.interfaces :no-heading: +Model Factories +^^^^^^^^^^^^^^^ +.. automodapi:: specviz.interfaces.factories + :no-heading: + :no-main-docstr: + :headings:"" + +Model Initialization +^^^^^^^^^^^^^^^^^^^^ +.. automodapi:: specviz.interfaces.initializers + :no-heading: + :no-main-docstr: + :headings:"" + I/O module ---------- .. automodapi:: specviz.io diff --git a/specviz/interfaces/factories.py b/specviz/interfaces/factories.py index 7c91053f..2b2e6cc5 100644 --- a/specviz/interfaces/factories.py +++ b/specviz/interfaces/factories.py @@ -14,6 +14,11 @@ # THIRD-PARTY from astropy.modeling import models, fitting +__all__ = [ + 'Factory', + 'FitterFactory', + 'ModelFactory', +] class Factory(object): """ diff --git a/specviz/interfaces/initializers.py b/specviz/interfaces/initializers.py index c7f99845..c7be37c5 100644 --- a/specviz/interfaces/initializers.py +++ b/specviz/interfaces/initializers.py @@ -11,6 +11,10 @@ from ..analysis.models import spline, blackbody +__all__ = [ + 'initialize' +] + AMPLITUDE = 'amplitude' POSITION = 'position' WIDTH = 'width' @@ -236,7 +240,9 @@ def _setattr(instance, mname, pname, value): def initialize(instance, x, y): """ - Main entry point. X and Y are for now Quantity arrays with the + Initialize given model. + + X and Y are for now Quantity arrays with the independent and dependent variables. It's assumed X values are stored in increasing order in the array.