Exemplo n.º 1
0
class GroupedSubset(Subset):

    """
    A member of a SubsetGroup, whose internal representation
    is shared with other group members
    """
    subset_state = Pointer('group.subset_state')
    label = Pointer('group.label')

    def __init__(self, data, group):
        """
        :param data: :class:`~glue.core.data.Data` instance to bind to
        :param group: :class:`~glue.core.subset_group.SubsetGroup`
        """

        # We deliberately don't call Subset.__init__ here because we don't want
        # to set e.g. the subset state, color, transparency, etc. Instead we
        # just want to defer to the SubsetGroup for these.

        self._broadcasting = False  # must be first def

        self.group = group

        self.data = data
        self.label = group.label  # trigger disambiguation

    @property
    def style(self):
        return self.group.style

    @property
    def subset_group(self):
        return self.group.subset_group

    @property
    def verbose_label(self):
        return "%s (%s)" % (self.label, self.data.label)

    def __eq__(self, other):
        return other is self

    # In Python 3, if __eq__ is defined, then __hash__ has to be re-defined
    if six.PY3:
        __hash__ = object.__hash__

    def __gluestate__(self, context):
        return dict(group=context.id(self.group),
                    style=context.do(self.style))

    @classmethod
    def __setgluestate__(cls, rec, context):
        dummy_grp = SubsetGroup()  # __init__ needs group.label
        self = cls(None, dummy_grp)
        yield self
        self.group = context.object(rec['group'])
Exemplo n.º 2
0
class GingaLayerArtist(LayerArtistBase):

    zorder = Pointer('_zorder')
    visible = Pointer('_visible')

    def __init__(self, layer, canvas):
        super(GingaLayerArtist, self).__init__(layer)
        self._canvas = canvas
        self._visible = True

    def redraw(self, whence=0):
        self._canvas.redraw(whence=whence)
Exemplo n.º 3
0
class GroupedSubset(Subset):
    """
    A member of a SubsetGroup, whose internal representation
    is shared with other group members
    """
    subset_state = Pointer('group.subset_state')
    label = Pointer('group.label')

    def __init__(self, data, group):
        """
        :param data: :class:`~glue.core.data.Data` instance to bind to
        :param group: :class:`~glue.core.subset_group.SubsetGroup`
        """
        self.group = group
        super(GroupedSubset, self).__init__(data,
                                            label=group.label,
                                            color=group.style.color,
                                            alpha=group.style.alpha)

    def _setup(self, color, alpha, label):
        self.color = color
        self.label = label  # trigger disambiguation
        self.style = VisualAttributes(parent=self)
        self.style.markersize *= 2.5
        self.style.color = color
        self.style.alpha = alpha
        # skip state setting here

    @property
    def verbose_label(self):
        return "%s (%s)" % (self.label, self.data.label)

    def sync_style(self, other):
        self.style.set(other)

    def __eq__(self, other):
        return other is self

    # In Python 3, if __eq__ is defined, then __hash__ has to be re-defined
    if six.PY3:
        __hash__ = object.__hash__

    def __gluestate__(self, context):
        return dict(group=context.id(self.group), style=context.do(self.style))

    @classmethod
    def __setgluestate__(cls, rec, context):
        dummy_grp = SubsetGroup()  # __init__ needs group.label
        self = cls(None, dummy_grp)
        yield self
        self.group = context.object(rec['group'])
        self.style = context.object(rec['style'])
Exemplo n.º 4
0
class LayerArtistBase(PropertySetMixin):
    _property_set = ['zorder', 'visible', 'layer']

    # the order of this layer in the visualizations. High-zorder
    # layers are drawn on top of low-zorder layers.
    # Subclasses should refresh plots when this property changes
    zorder = Pointer('_zorder')

    # whether this layer should be rendered.
    # Subclasses should refresh plots when this property changes
    visible = Pointer('_visible')

    # whether this layer is capable of being rendered
    # Subclasses should refresh plots when this property changes
    enabled = Pointer('_enabled')

    def __init__(self, layer):
        """Create a new LayerArtist

        Parameters
        ----------
        layer : :class:`~glue.core.data.Data` or :class:`~glue.core.subset.Subset`
            Data or Subset to draw
        layer : :class:`~glue.core.data.Data` or `glue.core.subset.Subset`
        """
        self._visible = True
        self._zorder = 0
        self._enabled = True
        self._layer = layer

        self.view = None  # cache of last view, if relevant
        self._state = None  # cache of subset state, if relevant
        self._changed = True  # hint at whether underlying data has changed since last render

        self._disabled_reason = ''  # A string explaining why this layer is disabled.

    def get_layer_color(self):
        # This method can return either a plain color or a colormap. This is
        # used by the UI layer to determine a 'representative' color or colormap
        # for the layer to be used e.g. in icons.
        return self._layer.style.color

    def enable(self):
        if self.enabled:
            return
        self._disabled_reason = ''
        self._enabled = True
        self.redraw()
        if self._layer is not None and self._layer.hub is not None:
            message = LayerArtistEnabledMessage(self)
            self._layer.hub.broadcast(message)

    def disable(self, reason):
        """
        Disable the layer for a particular reason.

        Layers should only be disabled when drawing is impossible,
        e.g. because a subset cannot be applied to a dataset.

        Parameters
        ----------
        reason : str
           A short explanation for why the layer can't be drawn.
           Used by the UI
        """
        self._disabled_reason = reason
        self._enabled = False
        self.clear()
        if self._layer is not None and self._layer.hub is not None:
            message = LayerArtistDisabledMessage(self)
            self._layer.hub.broadcast(message)

    def disable_invalid_attributes(self, *attributes):
        """
        Disable a layer because visualization depends on knowing a set
        of ComponentIDs that cannot be derived from a dataset or subset

        Automatically generates a disabled message.

        Parameters
        ----------
        attributes : sequence of ComponentIDs
        """
        if len(attributes) == 0:
            self.disable('')
            return

        datasets = ', '.join(
            sorted(set([cid.parent.label for cid in attributes])))
        self.disable(DISABLED_LAYER_WARNING.format(datasets))

    def disable_incompatible_subset(self):
        """
        Disable a layer because the subset mask cannot be computed.

        Automatically generates a disabled message.
        """
        self.disable(DISABLED_MASK_MESSAGE)

    @property
    def disabled_message(self):
        """
        Returns why a layer is disabled
        """
        if self.enabled:
            return ''
        return "Cannot visualize this layer: %s" % self._disabled_reason

    @property
    def layer(self):
        """
        The Data or Subset visualized in this layer
        """
        return self._layer

    @layer.setter
    def layer(self, value):
        self._layer = value

    def redraw(self):
        """
        Re-render the plot
        """
        pass

    def update(self):
        """
        Sync the visual appearance of the layer, and redraw
        """
        pass

    def clear(self):
        """
        Clear the visualization for this layer
        """
        pass

    def remove(self):
        """
        Remove the visualization for this layer.

        This is called when the layer artist is removed for good from the
        viewer. It defaults to calling clear, but can be overriden in cases
        where clear and remove should be different.
        """
        self.clear()

    def force_update(self, *args, **kwargs):
        """
        Sets the _changed flag to true, and calls update.

        Force an update of the layer, overriding any
        caching that might be going on for speed
        """
        self._changed = True
        return self.update(*args, **kwargs)

    def _check_subset_state_changed(self):
        """Checks to see if layer is a subset and, if so,
        if it has changed subset state. Sets _changed flag to True if so"""
        if not isinstance(self.layer, Subset):
            return
        state = self.layer.subset_state
        if state is not self._state:
            self._changed = True
            self._state = state

    def __str__(self):
        return "%s for %s" % (self.__class__.__name__, self.layer.label)

    def __gluestate__(self, context):
        # note, this doesn't yet have a restore method. Will rely on client
        return dict((k, context.id(v)) for k, v in self.properties.items())

    __repr__ = __str__
Exemplo n.º 5
0
class SpectrumContext(object):
    """
    Base class for different interaction contexts
    """
    client = Pointer('main.client')
    data = Pointer('main.data')
    profile_axis = Pointer('main.profile_axis')
    canvas = Pointer('main.canvas')
    profile = Pointer('main.profile')

    def __init__(self, main):

        self.main = main
        self.grip = None
        self.panel = None
        self.widget = None

        self._setup_grip()
        self._setup_widget()
        self._connect()

    def _setup_grip(self):
        """ Create a :class:`~glue.plugins.tools.spectrum_tool.profile_viewer.Grip` object
            to interact with the plot. Assign to self.grip
        """
        raise NotImplementedError()

    def _setup_widget(self):
        """
        Create a context-specific widget
        """
        # this is the widget that is displayed to the right of the
        # spectrum
        raise NotImplementedError()

    def _connect(self):
        """
        Attach event handlers
        """
        pass

    def set_enabled(self, enabled):
        self.enable() if enabled else self.disable()

    def enable(self):
        if self.grip is not None:
            self.grip.enable()

    def disable(self):
        if self.grip is not None:
            self.grip.disable()

    def recenter(self, lim):
        """Re-center the grip to the given x axlis limit tuple"""
        if self.grip is None:
            return
        if hasattr(self.grip, 'value'):
            self.grip.value = sum(lim) / 2.
            return

        # Range grip
        cen = sum(lim) / 2
        wid = max(lim) - min(lim)
        self.grip.range = cen - wid / 4, cen + wid / 4
Exemplo n.º 6
0
class SubsetFacet(QtGui.QDialog):

    log = ButtonProperty('ui.checkbox_log')
    vmin = FloatLineProperty('ui.value_min')
    vmax = FloatLineProperty('ui.value_max')
    steps = ValueProperty('ui.value_n_subsets')
    data = Pointer('ui.component_selector.data')
    component = Pointer('ui.component_selector.component')

    def __init__(self, collect, default=None, parent=None):
        """Create a new dialog for subset faceting

        :param collect: The :class:`~glue.core.data_collection.DataCollection` to use
        :param default: The default dataset in the collection (optional)
        """

        super(SubsetFacet, self).__init__(parent=parent)

        self.ui = load_ui('subset_facet.ui',
                          self,
                          directory=os.path.dirname(__file__))
        self.ui.setWindowTitle("Subset Facet")
        self._collect = collect

        self.ui.component_selector.setup(self._collect)
        if default is not None:
            self.ui.component_selector.data = default

        val = QtGui.QDoubleValidator(-1e100, 1e100, 4, None)
        self.ui.component_selector.component_changed.connect(self._set_limits)

        combo = self.ui.color_scale
        for cmap in [cm.cool, cm.RdYlBu, cm.RdYlGn, cm.RdBu, cm.Purples]:
            combo.addItem(QtGui.QIcon(cmap2pixmap(cmap)), cmap.name, cmap)

    def _set_limits(self):
        data = self.ui.component_selector.data
        cid = self.ui.component_selector.component

        vals = data[cid]

        wmin = self.ui.value_min
        wmax = self.ui.value_max

        wmin.setText(pretty_number(np.nanmin(vals)))
        wmax.setText(pretty_number(np.nanmax(vals)))

    @property
    def cmap(self):
        combo = self.ui.color_scale
        index = combo.currentIndex()
        return combo.itemData(index)

    def _apply(self):
        try:
            lo, hi = self.vmin, self.vmax
        except ValueError:
            return  # limits not set. Abort
        if not np.isfinite(lo) or not np.isfinite(hi):
            return

        subsets = facet_subsets(self._collect,
                                self.component,
                                lo=lo,
                                hi=hi,
                                steps=self.steps,
                                log=self.log)
        colorize_subsets(subsets, self.cmap)

    @classmethod
    def facet(cls, collect, default=None, parent=None):
        """Class method to create facted subsets
        The arguments are the same as __init__
        """
        self = cls(collect, parent=parent, default=default)
        value = self.exec_()

        if value == QtGui.QDialog.Accepted:
            self._apply()
Exemplo n.º 7
0
class LayerArtistBase(PropertySetMixin):
    _property_set = ['zorder', 'visible', 'layer']

    # the order of this layer in the visualizations. High-zorder
    # layers are drawn on top of low-zorder layers.
    # Subclasses should refresh plots when this property changes
    zorder = Pointer('_zorder')

    # whether this layer should be rendered.
    # Subclasses should refresh plots when this property changes
    visible = Pointer('_visible')

    # whether this layer is capable of being rendered
    # Subclasses should refresh plots when this property changes
    enabled = Pointer('_enabled')

    def __init__(self, layer):
        """Create a new LayerArtist

        Parameters
        ----------
        layer : :class:`~glue.core.data.Data` or :class:`~glue.core.subset.Subset`
            Data or Subset to draw
        layer : :class:`~glue.core.data.Data` or `glue.core.subset.Subset`
        """
        self._visible = True
        self._zorder = 0
        self._enabled = True
        self._layer = layer

        self.view = None      # cache of last view, if relevant
        self._state = None    # cache of subset state, if relevant
        self._changed = True  # hint at whether underlying data has changed since last render

        self._disabled_reason = ''  # A string explaining why this layer is disabled.

    def disable(self, reason):
        """
        Disable the layer for a particular reason.

        Layers should only be disabled when drawing is impossible,
        e.g. because a subset cannot be applied to a dataset.

        Parameters
        ----------
        reason : str
           A short explanation for why the layer can't be drawn.
           Used by the UI
        """
        self._disabled_reason = reason
        self._enabled = False
        self.clear()

    def disable_invalid_attributes(self, *attributes):
        """
        Disable a layer because visualization depends on knowing a set
        of ComponentIDs that cannot be derived from a dataset or subset

        Automatically generates a disabled message.

        Parameters
        ----------
        attributes : sequence of ComponentIDs
        """
        if len(attributes) == 0:
            self.disable('')

        msg = ('Layer depends on attributes that '
               'cannot be derived for %s:\n -%s' %
               (self._layer.data.label,
                '\n -'.join(map(str, attributes))))

        self.disable(msg)

    @property
    def disabled_message(self):
        """
        Returns why a layer is disabled
        """
        if self.enabled:
            return ''
        return "Cannot visualize this layer\n%s" % self._disabled_reason

    @property
    def layer(self):
        """
        The Data or Subset visualized in this layer
        """
        return self._layer

    @layer.setter
    def layer(self, value):
        self._layer = value

    @abstractmethod
    def redraw(self):
        """
        Re-render the plot
        """
        raise NotImplementedError()

    @abstractmethod
    def update(self, view=None):
        """
        Sync the visual appearance of the layer, and redraw

        Subclasses may skip the update if the _changed attribute
        is set to False.

        Parameters
        ----------
        view : (ComponentID, numpy_style view) or None
            A hint about what sub-view into the data is relevant.
        """
        raise NotImplementedError()

    @abstractmethod
    def clear(self):
        """Clear the visulaization for this layer"""
        raise NotImplementedError()

    def force_update(self, *args, **kwargs):
        """
        Sets the _changed flag to true, and calls update.

        Force an update of the layer, overriding any
        caching that might be going on for speed
        """
        self._changed = True
        return self.update(*args, **kwargs)

    def _check_subset_state_changed(self):
        """Checks to see if layer is a subset and, if so,
        if it has changed subset state. Sets _changed flag to True if so"""
        if not isinstance(self.layer, Subset):
            return
        state = self.layer.subset_state
        if state is not self._state:
            self._changed = True
            self._state = state

    def __str__(self):
        return "%s for %s" % (self.__class__.__name__, self.layer.label)

    def __gluestate__(self, context):
        # note, this doesn't yet have a restore method. Will rely on client
        return dict((k, context.id(v)) for k, v in self.properties.items())

    __repr__ = __str__
Exemplo n.º 8
0
class ImageWidgetBase(DataViewer):
    """
    Widget for ImageClient

    This base class avoids any matplotlib-specific logic
    """

    LABEL = "Image Viewer"
    _property_set = DataViewer._property_set + \
        'data attribute rgb_mode rgb_viz ratt gatt batt slice'.split()

    attribute = CurrentComboProperty('ui.attributeComboBox',
                                     'Current attribute')
    data = CurrentComboProperty('ui.displayDataCombo', 'Current data')
    aspect_ratio = CurrentComboProperty('ui.aspectCombo',
                                        'Aspect ratio for image')
    rgb_mode = ButtonProperty('ui.rgb', 'RGB Mode?')
    rgb_viz = Pointer('ui.rgb_options.rgb_visible')

    _layer_style_widget_cls = {ScatterLayerArtist: ScatterLayerStyleWidget}

    def __init__(self, session, parent=None):
        super(ImageWidgetBase, self).__init__(session, parent)
        self._setup_widgets()
        self.client = self.make_client()
        self._connect()

    def _setup_widgets(self):
        self.central_widget = self.make_central_widget()
        self.label_widget = QtWidgets.QLabel("", self.central_widget)
        self.setCentralWidget(self.central_widget)
        self.option_widget = QtWidgets.QWidget()
        self.ui = load_ui('options_widget.ui',
                          self.option_widget,
                          directory=os.path.dirname(__file__))
        self.ui.slice = DataSlice()
        self.ui.slice_layout.addWidget(self.ui.slice)
        self._tweak_geometry()

        self.ui.aspectCombo.addItem("Square Pixels", userData='equal')
        self.ui.aspectCombo.addItem("Automatic", userData='auto')

    def make_client(self):
        """ Instantiate and return an ImageClient subclass """
        raise NotImplementedError()

    def make_central_widget(self):
        """ Create and return the central widget to display the image """
        raise NotImplementedError()

    def _tweak_geometry(self):
        self.central_widget.resize(600, 400)
        self.resize(self.central_widget.size())
        self.ui.rgb_options.hide()
        self.statusBar().setSizeGripEnabled(False)
        self.setFocusPolicy(Qt.StrongFocus)

    @defer_draw
    def add_data(self, data):
        """
        Add a new dataset to the viewer
        """
        # overloaded from DataViewer

        # need to delay callbacks, otherwise might
        # try to set combo boxes to nonexisting items
        with delay_callback(self.client, 'display_data', 'display_attribute'):

            # If there is not already any image data set, we can't add 1-D
            # datasets (tables/catalogs) to the image widget yet.
            if data.data.ndim == 1 and self.client.display_data is None:
                QtWidgets.QMessageBox.information(
                    self.window(),
                    "Note", "Cannot create image viewer from a 1-D "
                    "dataset. You will need to first "
                    "create an image viewer using data "
                    "with 2 or more dimensions, after "
                    "which you will be able to overlay 1-D "
                    "data as a scatter plot.",
                    buttons=QtWidgets.QMessageBox.Ok)
                return

            r = self.client.add_layer(data)
            if r is not None and self.client.display_data is not None:
                self.add_data_to_combo(data)
                if self.client.can_image_data(data):
                    self.client.display_data = data
                self.set_attribute_combo(self.client.display_data)

        return r is not None

    @defer_draw
    def add_subset(self, subset):
        self.client.add_scatter_layer(subset)
        assert subset in self.client.artists

    def add_data_to_combo(self, data):
        """ Add a data object to the combo box, if not already present
        """
        if not self.client.can_image_data(data):
            return
        combo = self.ui.displayDataCombo
        try:
            pos = _find_combo_data(combo, data)
        except ValueError:
            combo.addItem(data.label, userData=data)

    @property
    def ratt(self):
        """ComponentID assigned to R channel in RGB Mode"""
        return self.ui.rgb_options.attributes[0]

    @ratt.setter
    def ratt(self, value):
        att = list(self.ui.rgb_options.attributes)
        att[0] = value
        self.ui.rgb_options.attributes = att

    @property
    def gatt(self):
        """ComponentID assigned to G channel in RGB Mode"""
        return self.ui.rgb_options.attributes[1]

    @gatt.setter
    def gatt(self, value):
        att = list(self.ui.rgb_options.attributes)
        att[1] = value
        self.ui.rgb_options.attributes = att

    @property
    def batt(self):
        """ComponentID assigned to B channel in RGB Mode"""
        return self.ui.rgb_options.attributes[2]

    @batt.setter
    def batt(self, value):
        att = list(self.ui.rgb_options.attributes)
        att[2] = value
        self.ui.rgb_options.attributes = att

    @property
    def slice(self):
        return self.client.slice

    @slice.setter
    def slice(self, value):
        self.client.slice = value

    def set_attribute_combo(self, data):
        """ Update attribute combo box to reflect components in data"""
        labeldata = ((f.label, f) for f in data.visible_components)
        update_combobox(self.ui.attributeComboBox, labeldata)

    def _connect(self):
        ui = self.ui

        ui.monochrome.toggled.connect(self._update_rgb_console)
        ui.rgb_options.colors_changed.connect(self.update_window_title)

        # sync client and widget slices
        ui.slice.slice_changed.connect(
            lambda: setattr(self, 'slice', self.ui.slice.slice))
        update_ui_slice = lambda val: setattr(ui.slice, 'slice', val)
        add_callback(self.client, 'slice', update_ui_slice)
        add_callback(self.client, 'display_data', self.ui.slice.set_data)

        # sync window title to data/attribute
        add_callback(self.client, 'display_data',
                     nonpartial(self._display_data_changed))
        add_callback(self.client, 'display_attribute',
                     nonpartial(self._display_attribute_changed))
        add_callback(self.client, 'display_aspect',
                     nonpartial(self.client._update_aspect))

        # sync data/attribute combos with client properties
        connect_current_combo(self.client, 'display_data',
                              self.ui.displayDataCombo)
        connect_current_combo(self.client, 'display_attribute',
                              self.ui.attributeComboBox)
        connect_current_combo(self.client, 'display_aspect',
                              self.ui.aspectCombo)

    def _display_data_changed(self):

        if self.client.display_data is None:
            self.ui.attributeComboBox.clear()
            return

        with self.client.artists.ignore_empty():
            self.set_attribute_combo(self.client.display_data)
        self.client.add_layer(self.client.display_data)
        self.client._update_and_redraw()
        self.update_window_title()

    def _display_attribute_changed(self):

        if self.client.display_attribute is None:
            return

        self.client._update_and_redraw()
        self.update_window_title()

    @defer_draw
    def _update_rgb_console(self, is_monochrome):
        if is_monochrome:
            self.ui.rgb_options.hide()
            self.ui.mono_att_label.show()
            self.ui.attributeComboBox.show()
            self.client.rgb_mode(False)
        else:
            self.ui.mono_att_label.hide()
            self.ui.attributeComboBox.hide()
            self.ui.rgb_options.show()
            rgb = self.client.rgb_mode(True)
            if rgb is not None:
                self.ui.rgb_options.artist = rgb

    def register_to_hub(self, hub):
        super(ImageWidgetBase, self).register_to_hub(hub)
        self.client.register_to_hub(hub)

        dc_filt = lambda x: x.sender is self.client._data
        display_data_filter = lambda x: x.data is self.client.display_data

        hub.subscribe(self,
                      core.message.DataCollectionAddMessage,
                      handler=lambda x: self.add_data_to_combo(x.data),
                      filter=dc_filt)
        hub.subscribe(self,
                      core.message.DataCollectionDeleteMessage,
                      handler=lambda x: self.remove_data_from_combo(x.data),
                      filter=dc_filt)
        hub.subscribe(self,
                      core.message.DataUpdateMessage,
                      handler=lambda x: self._sync_data_labels())
        hub.subscribe(self,
                      core.message.ComponentsChangedMessage,
                      handler=lambda x: self.set_attribute_combo(x.data),
                      filter=display_data_filter)

    def unregister(self, hub):
        super(ImageWidgetBase, self).unregister(hub)
        for obj in [self, self.client]:
            hub.unsubscribe_all(obj)

    def remove_data_from_combo(self, data):
        """ Remove a data object from the combo box, if present """
        combo = self.ui.displayDataCombo
        pos = combo.findText(data.label)
        if pos >= 0:
            combo.removeItem(pos)

    def _set_norm(self, mode):
        """ Use the `ContrastMouseMode` to adjust the transfer function """

        # at least one of the clip/vmin pairs will be None
        clip_lo, clip_hi = mode.get_clip_percentile()
        vmin, vmax = mode.get_vmin_vmax()
        stretch = mode.stretch
        return self.client.set_norm(clip_lo=clip_lo,
                                    clip_hi=clip_hi,
                                    stretch=stretch,
                                    vmin=vmin,
                                    vmax=vmax,
                                    bias=mode.bias,
                                    contrast=mode.contrast)

    @property
    def window_title(self):
        if self.client.display_data is None or self.client.display_attribute is None:
            title = ''
        else:
            data = self.client.display_data.label
            a = self.client.rgb_mode()
            if a is None:  # monochrome mode
                title = "%s - %s" % (self.client.display_data.label,
                                     self.client.display_attribute.label)
            else:
                r = a.r.label if a.r is not None else ''
                g = a.g.label if a.g is not None else ''
                b = a.b.label if a.b is not None else ''
                title = "%s Red = %s  Green = %s  Blue = %s" % (data, r, g, b)

        return title

    def _sync_data_combo_labels(self):
        combo = self.ui.displayDataCombo
        for i in range(combo.count()):
            combo.setItemText(i, combo.itemData(i).label)

    def _sync_data_labels(self):
        self.update_window_title()
        self._sync_data_combo_labels()

    def __str__(self):
        return "Image Widget"

    def _confirm_large_image(self, data):
        """Ask user to confirm expensive operations

        :rtype: bool. Whether the user wishes to continue
        """

        warn_msg = ("WARNING: Image has %i pixels, and may render slowly."
                    " Continue?" % data.size)
        title = "Contour large image?"
        ok = QtWidgets.QMessageBox.Ok
        cancel = QtWidgets.QMessageBox.Cancel
        buttons = ok | cancel
        result = QtWidgets.QMessageBox.question(self,
                                                title,
                                                warn_msg,
                                                buttons=buttons,
                                                defaultButton=cancel)
        return result == ok

    def options_widget(self):
        return self.option_widget

    @defer_draw
    def restore_layers(self, rec, context):
        with delay_callback(self.client, 'display_data', 'display_attribute'):
            self.client.restore_layers(rec, context)

            for artist in self.layers:
                self.add_data_to_combo(artist.layer.data)

            self.set_attribute_combo(self.client.display_data)

        self._sync_data_combo_labels()

    def closeEvent(self, event):
        # close window and all plugins
        super(ImageWidgetBase, self).closeEvent(event)
Exemplo n.º 9
0
class RGBImageLayerArtist(ImageLayerArtist, RGBImageLayerBase):
    _property_set = ImageLayerArtist._property_set + \
        ['r', 'g', 'b', 'rnorm', 'gnorm', 'bnorm', 'color_visible']

    r = ChangedTrigger()
    g = ChangedTrigger()
    b = ChangedTrigger()
    rnorm = Pointer('_rnorm')
    gnorm = Pointer('_gnorm')
    bnorm = Pointer('_bnorm')

    # dummy class-level variables will be masked
    # at instance level, needed for ABC to be happy
    layer_visible = None
    contrast_layer = None

    def __init__(self, layer, ax, last_view=None):
        super(RGBImageLayerArtist, self).__init__(layer, ax)
        self.contrast_layer = 'green'
        self.aspect = 'equal'
        self.layer_visible = dict(red=True, green=True, blue=True)
        self.last_view = last_view

    def set_norm(self, *args, **kwargs):
        spr = super(RGBImageLayerArtist, self).set_norm
        if self.contrast_layer == 'red':
            self.norm = self.rnorm
            self.rnorm = spr(*args, **kwargs)
        if self.contrast_layer == 'green':
            self.norm = self.gnorm
            self.gnorm = spr(*args, **kwargs)
        if self.contrast_layer == 'blue':
            self.norm = self.bnorm
            self.bnorm = spr(*args, **kwargs)

    def update(self, view=None, transpose=False, aspect=None):

        self.clear()

        if aspect is not None:
            self.aspect = aspect

        if self.r is None or self.g is None or self.b is None:
            return

        if view is None:
            view = self.last_view

        if view is None:
            return
        self.last_view = view

        views = view_cascade(self.layer, view)
        artists = []
        for v in views:
            extent = get_extent(v, transpose)
            # first argument = component. swap
            r = tuple([self.r] + list(v[1:]))
            g = tuple([self.g] + list(v[1:]))
            b = tuple([self.b] + list(v[1:]))
            r = self.layer[r]
            g = self.layer[g]
            b = self.layer[b]
            if transpose:
                r = r.T
                g = g.T
                b = b.T
            self.rnorm = self.rnorm or self._default_norm(r)
            self.gnorm = self.gnorm or self._default_norm(g)
            self.bnorm = self.bnorm or self._default_norm(b)
            if v is views[0]:
                self.rnorm.update_clip(small_view(self.layer, self.r))
                self.gnorm.update_clip(small_view(self.layer, self.g))
                self.bnorm.update_clip(small_view(self.layer, self.b))

            image = np.dstack((self.rnorm(r), self.gnorm(g), self.bnorm(b)))

            if not self.layer_visible['red']:
                image[:, :, 0] *= 0
            if not self.layer_visible['green']:
                image[:, :, 1] *= 0
            if not self.layer_visible['blue']:
                image[:, :, 2] *= 0

            artists.append(
                self._axes.imshow(image,
                                  interpolation='nearest',
                                  origin='lower',
                                  extent=extent,
                                  zorder=0))
            self._axes.set_aspect(self.aspect, adjustable='datalim')
        self.artists = artists
        self._sync_style()