Source code for glue_jupyter.bqplot.profile.layer_artist

from __future__ import absolute_import, division, print_function

import sys
import warnings

import numpy as np

from glue.core import BaseData
from glue.utils import defer_draw, nanmin, nanmax, color2hex
from glue.viewers.profile.state import ProfileLayerState
from glue.core.exceptions import IncompatibleAttribute, IncompatibleDataException

import bqplot

from glue_jupyter.compat import LayerArtist

from ...link import dlink

__all__ = ['BqplotProfileLayerArtist']


[docs]class BqplotProfileLayerArtist(LayerArtist): _layer_state_cls = ProfileLayerState def __init__(self, view, viewer_state, layer_state=None, layer=None): super().__init__(viewer_state, layer_state=layer_state, layer=layer) # Watch for changes in the viewer state which would require the # layers to be redrawn self._viewer_state.add_global_callback(self._update_profile) self.state.add_global_callback(self._update_profile) self.view = view self.line_mark = bqplot.Lines(scales=self.view.scales, x=[0, 1], y=[0, 1]) self.view.figure.marks = list(self.view.figure.marks) + [self.line_mark] dlink((self.state, 'color'), (self.line_mark, 'colors'), lambda x: [color2hex(x)]) dlink((self.state, 'alpha'), (self.line_mark, 'opacities'), lambda x: [x]) self.line_mark.colors = [color2hex(self.state.color)] self.line_mark.opacities = [self.state.alpha] def _calculate_profile(self, reset=False): try: self._calculate_profile_thread(reset=reset) except Exception: self._calculate_profile_error(sys.exc_info()) else: self._calculate_profile_postthread() def _calculate_profile_thread(self, reset=False): # We need to ignore any warnings that happen inside the thread # otherwise the thread tries to send these to the glue logger (which # uses Qt), which then results in this kind of error: # QObject::connect: Cannot queue arguments of type 'QTextCursor' with warnings.catch_warnings(): warnings.simplefilter("ignore") if reset: self.state.reset_cache() self.state.update_profile(update_limits=False) def _calculate_profile_postthread(self): # It's possible for this method to get called but for the state to have # been updated in the mean time to have a histogram that raises an # exception (for example an IncompatibleAttribute). If any errors happen # here, we simply ignore them since _calculate_histogram_error will get # called directly. try: visible_data = self.state.profile except Exception: return if visible_data is None: return self.enable() x, y = visible_data # Update the data values. if len(x) > 0: self.state.update_limits() # Normalize profile values to the [0:1] range based on limits if self._viewer_state.normalize: y = self.state.normalize_values(y) with self.line_mark.hold_sync(): self.line_mark.x = x self.line_mark.y = y self.line_mark.visible = self.state.visible else: self.line_mark.visible = False if not self._viewer_state.normalize and len(y) > 0: y_min = nanmin(y) y_max = nanmax(y) y_range = y_max - y_min self.state._y_min = y_min - y_range * 0.1 self.state._y_max = y_max + y_range * 0.1 largest_y_max = max(getattr(layer, '_y_max', 0) for layer in self._viewer_state.layers) if largest_y_max != self._viewer_state.y_max: self._viewer_state.y_max = largest_y_max smallest_y_min = min(getattr(layer, '_y_min', np.inf) for layer in self._viewer_state.layers) if smallest_y_min != self._viewer_state.y_min: self._viewer_state.y_min = smallest_y_min self.redraw() def _calculate_profile_error(self, exc): self.line_mark.visible = False self.redraw() if issubclass(exc[0], IncompatibleAttribute): if isinstance(self.state.layer, BaseData): self.disable_invalid_attributes(self.state.attribute) else: self.disable_incompatible_subset() elif issubclass(exc[0], IncompatibleDataException): self.disable("Incompatible data") def _update_visual_attributes(self): if not self.enabled: return self.line_mark.visible = self.state.visible self.line_mark.stroke_width = self.state.linewidth self.redraw() def _update_profile(self, force=False, **kwargs): # TODO: we need to factor the following code into a common method. if (self._viewer_state.x_att is None or self.state.attribute is None or self.state.layer is None): return changed = set() if force else self.pop_changed_properties() if force or any(prop in changed for prop in ('layer', 'x_att', 'attribute', 'function', 'normalize', 'v_min', 'v_max')): self._calculate_profile(reset=force) if force or any(prop in changed for prop in ('alpha', 'color', 'zorder', 'visible', 'linewidth')): self._update_visual_attributes()
[docs] @defer_draw def update(self): self.state.reset_cache() self._update_profile(force=True) self.redraw()