Ejemplo n.º 1
0
class LayerWidget(QtGui.QWidget):

    layer = CurrentComboDataProperty('ui.combo_active_layer')

    def __init__(self, parent=None, data_viewer=None):

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

        self.ui = load_ui('layer_widget.ui', self,
                          directory=os.path.dirname(__file__))

        self._layers = []

    def __contains__(self, layer):
        return layer in self._layers

    def add_layer(self, layer):
        self._layers.append(layer)
        self._update_combobox()

    def remove_layer(self, layer):
        self._layers.remove(layer)
        self._update_combobox()

    def _update_combobox(self):
        labeldata = [(layer.label, layer) for layer in self._layers]
        update_combobox(self.ui.combo_active_layer, labeldata)
Ejemplo n.º 2
0
class OptionsWidget(QWidget):

    file_att = CurrentComboDataProperty('ui.combo_file_attribute')

    def __init__(self, parent=None, data_viewer=None):

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

        self.ui = load_ui('viewer_options.ui',
                          self,
                          directory=os.path.dirname(__file__))

        self.file_helper = ComponentIDComboHelper(self.ui.combo_file_attribute,
                                                  data_viewer._data)

        self._data_viewer = data_viewer

        self._data = None

    def set_data(self, data):
        self.file_helper.clear()

        if isinstance(data, Subset):
            self.file_helper.append_data(data.data)
        else:
            self.file_helper.append_data(data)
Ejemplo n.º 3
0
class BaseDataComboHelper(HubListener):
    """
    This is a base class for helpers for combo boxes that need to show a list
    of data objects.

    Parameters
    ----------
    data_combo : Qt combo widget
        The Qt widget for the data combo box
    """

    _data = CurrentComboDataProperty('_data_combo')

    def __init__(self, data_combo):
        super(BaseDataComboHelper, self).__init__()
        self._data_combo = data_combo
        self._component_id_helpers = []
        self._data_combo.currentIndexChanged.connect(
            self.refresh_component_ids)

    def refresh(self):
        label_data = [(data.label, data) for data in self._datasets]
        update_combobox(self._data_combo, label_data)
        self.refresh_component_ids()

    def refresh_component_ids(self):
        for helper in self._component_id_helpers:
            helper.clear()
            if self._data is not None:
                helper.append_data(self._data)
            helper.refresh()

    def add_component_id_combo(self, combo):
        helper = ComponentIDComboHelper(combo)
        self._component_id_helpers.append_data(helper)
        if self._data is not None:
            helper.append_data(self._data)

    @property
    def hub(self):
        return self._hub

    @hub.setter
    def hub(self, value):
        self._hub = value
        if value is not None:
            self.register_to_hub(value)

    def register_to_hub(self, hub):
        pass
class AttributeLimitsHelper(object):
    """
    This class is a helper for attribute-dependent min/max level values.

    Given an attribute combo as well as line edit widgets for the min/max
    values, this helper takes care of populating the attribute combo, setting
    the initial values of the min/max values, and keeping a cache of the
    min/max values as a function of attribute. This means that if the user
    edits the min/max values and then changes attribute then changes back, the
    original min/max values will be retained.

    In addition, this helper class can optionally link a combo for the scale
    mode, for example using the min/max values or percentile values, as well as
    a button for flipping the min/max values.

    Parameters
    ----------
    attribute_combo : ``QComboBox`` instance
        The attribute combo - this will be populated once a dataset is assigned
        to the helper.
    lower_value, upper_value : ``QLineEdit`` instances
        The fields for the lower/upper levels
    mode_combo : ``QComboBox`` instance, optional
        The scale mode combo - this will be populated by presets such as
        Min/Max, various percentile levels, and Custom.
    flip_button : ``QToolButton`` instance, optional
        The flip button
    log_button : ``QToolButton`` instance, optional
        A button indicating whether the attribute should be shown in log space
    data : :class:`glue.core.data.Data`
        The dataset to attach to the helper - this will be used to populate the
        attribute combo as well as determine the limits automatically given the
        scale mode preset.

    Notes
    -----

    Once the helper is instantiated, the data associated with the helper can be
    set/changed with:

    >>> helper = AttributeLimitsHelper(...)
    >>> helper.data = data

    The data can also be passed to the initializer as described in the list of
    parameters above.
    """

    component_data = CurrentComboDataProperty('component_id_combo')
    scale_mode = CurrentComboTextProperty('mode_combo')
    percentile = CurrentComboDataProperty('mode_combo')
    vlo = FloatLineProperty('lower_value')
    vhi = FloatLineProperty('upper_value')
    vlog = ButtonProperty('log_button')

    def __init__(self,
                 attribute_combo,
                 lower_value,
                 upper_value,
                 mode_combo=None,
                 flip_button=None,
                 log_button=None,
                 data=None,
                 limits_cache=None):

        self.component_id_combo = attribute_combo
        self.mode_combo = mode_combo
        self.lower_value = lower_value
        self.upper_value = upper_value
        self.flip_button = flip_button
        self.log_button = log_button

        self.component_id_combo.currentIndexChanged.connect(
            self._update_limits)

        self.lower_value.editingFinished.connect(self._manual_edit)
        self.upper_value.editingFinished.connect(self._manual_edit)

        if self.log_button is None:
            self.log_button = QtWidgets.QToolButton()

        self.log_button.toggled.connect(self._manual_edit)

        if self.mode_combo is None:
            # Make hidden combo box to avoid having to always figure out if the
            # combo mode exists. This will then always be set to Min/Max.
            self.mode_combo = QtWidgets.QComboBox()

        self._setup_mode_combo()
        self.mode_combo.currentIndexChanged.connect(self._update_mode)

        if self.flip_button is not None:
            self.flip_button.clicked.connect(self._flip_limits)

        if limits_cache is None:
            limits_cache = {}

        self._limits = limits_cache
        self._callbacks = []

    def set_limits(self, vlo, vhi):
        self.lower_value.blockSignals(True)
        self.upper_value.blockSignals(True)
        self.vlo = vlo
        self.vhi = vhi
        self.lower_value.blockSignals(False)
        self.upper_value.blockSignals(False)
        self.lower_value.editingFinished.emit()
        self.upper_value.editingFinished.emit()

    def _setup_mode_combo(self):
        self.mode_combo.clear()
        self.mode_combo.addItem("Min/Max", userData=100)
        self.mode_combo.addItem("99.5%", userData=99.5)
        self.mode_combo.addItem("99%", userData=99)
        self.mode_combo.addItem("95%", userData=95)
        self.mode_combo.addItem("90%", userData=90)
        self.mode_combo.addItem("Custom", userData=None)
        self.mode_combo.setCurrentIndex(-1)

    def _flip_limits(self):
        self.set_limits(self.vhi, self.vlo)

    def _manual_edit(self):
        self._cache_limits()

    def _update_mode(self):
        if self.scale_mode != 'Custom':
            self._auto_limits()
            self._cache_limits()

    def _invalidate_cache(self):
        self._limits.clear()

    def _cache_limits(self):
        self._limits[
            self.component_id] = self.scale_mode, self.vlo, self.vhi, self.vlog

    def _update_limits(self):
        if self.component_id in self._limits:
            self.scale_mode, lower, upper, self.vlog = self._limits[
                self.component_id]
            self.set_limits(lower, upper)
        else:
            self.mode_combo.blockSignals(True)
            self.scale_mode = 'Min/Max'
            self.mode_combo.blockSignals(False)
            self._auto_limits()
            self.vlog = False

    @property
    def component_id(self):
        if self.component_data is not None:
            return self.component_data
        else:
            return None

    @property
    def data(self):
        if self.component_data is not None:
            return self.component_data.parent
        else:
            return None

    def _auto_limits(self):

        if self.component_data is None:
            return

        exclude = (100 - self.percentile) / 2.

        # For subsets in 'data' mode, we want to compute the limits based on
        # the full dataset, not just the subset.
        if isinstance(self.data, Subset):
            data_values = self.data.data[self.component_id]
        else:
            data_values = self.data[self.component_id]

        try:
            lower = np.nanpercentile(data_values, exclude)
            upper = np.nanpercentile(data_values, 100 - exclude)
        except AttributeError:  # Numpy < 1.9
            data_values = data_values[~np.isnan(data_values)]
            lower = np.percentile(data_values, exclude)
            upper = np.percentile(data_values, 100 - exclude)

        if isinstance(self.data, Subset):
            lower = 0

        self.set_limits(lower, upper)
Ejemplo n.º 5
0
class WWTOptionPanel(QtWidgets.QWidget):

    ra_att = CurrentComboDataProperty('ui.combo_ra_att')
    dec_att = CurrentComboDataProperty('ui.combo_dec_att')

    background = CurrentComboDataProperty('ui.combo_background')
    opacity = ValueProperty('ui.value_opacity')
    foreground = CurrentComboDataProperty('ui.combo_foreground')

    galactic_plane = ButtonProperty('ui.checkbox_galactic_plane')

    def __init__(self, viewer, parent=None):

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

        self.viewer = viewer
        self.ui = load_ui('options_widget.ui',
                          self,
                          directory=os.path.dirname(__file__))

        self._setup_combos()
        self._connect()

    @property
    def ra(self):
        if self.ra_att is None:
            return None
        else:
            return self.ra_att[0]

    @property
    def dec(self):
        if self.dec_att is None:
            return None
        else:
            return self.dec_att[0]

    def _setup_combos(self):
        layers = [
            'Digitized Sky Survey (Color)',
            'VLSS: VLA Low-frequency Sky Survey (Radio)',
            'WMAP ILC 5-Year Cosmic Microwave Background',
            'SFD Dust Map (Infrared)', 'WISE All Sky (Infrared)',
            'GLIMPSE/MIPSGAL', 'Hydrogen Alpha Full Sky Map'
        ]
        labels = ['DSS', 'VLSS', 'WMAP', 'SFD', 'WISE', 'GLIMPSE', 'H Alpha']
        thumbnails = [
            'DSS', 'VLA', 'wmap5yr_ilc_200uk', 'dust', 'glimpsemipsgaltn',
            'halpha'
        ]
        base = ('http://www.worldwidetelescope.org/wwtweb/'
                'thumbnail.aspx?name=%s')

        for i, row in enumerate(zip(layers, labels, thumbnails)):
            layer, text, thumb = row
            url = base % thumb
            data = urlopen(url).read()
            pm = QtGui.QPixmap()
            pm.loadFromData(data)
            icon = QtGui.QIcon(pm)

            self.ui.combo_foreground.addItem(icon, text, layer)
            self.ui.combo_foreground.setItemData(i, layer, role=Qt.ToolTipRole)
            self.ui.combo_background.addItem(icon, text, layer)
            self.ui.combo_background.setItemData(i, layer, role=Qt.ToolTipRole)

        self.ui.combo_foreground.setIconSize(QtCore.QSize(60, 60))
        self.ui.combo_background.setIconSize(QtCore.QSize(60, 60))

        self.ra_att_helper = ComponentIDComboHelper(self.ui.combo_ra_att,
                                                    self.viewer._data,
                                                    categorical=False,
                                                    numeric=True)

        self.dec_att_helper = ComponentIDComboHelper(self.ui.combo_dec_att,
                                                     self.viewer._data,
                                                     categorical=False,
                                                     numeric=True)

    def add_data(self, data):
        # TODO: the following logic should go in the component ID helpers. It
        # isn't quite right at the moment because if there are multiple
        # datasets/subsets with the same components, we only want to show those
        # once.
        if isinstance(data, Subset):
            self.ra_att_helper.append(data.data)
            self.dec_att_helper.append(data.data)
        else:
            self.ra_att_helper.append(data)
            self.dec_att_helper.append(data)

    def remove_data(self, data):
        if isinstance(data, Subset):
            self.ra_att_helper.remove(data.data)
            self.dec_att_helper.remove(data.data)
        else:
            self.ra_att_helper.remove(data)
            self.dec_att_helper.remove(data)

    def _connect(self):

        self.ui.combo_ra_att.currentIndexChanged.connect(
            self.viewer._update_all)
        self.ui.combo_dec_att.currentIndexChanged.connect(
            self.viewer._update_all)

        self.ui.combo_foreground.currentIndexChanged.connect(
            self.viewer._update_foreground)
        self.ui.combo_background.currentIndexChanged.connect(
            self.viewer._update_background)
        self.ui.value_opacity.valueChanged.connect(self.viewer._update_opacity)
        self.ui.checkbox_galactic_plane.toggled.connect(
            self.viewer._update_galactic_plane_mode)

        self.opacity = 100
Ejemplo n.º 6
0
class LinkEquation(QtWidgets.QWidget):
    """ Interactively define ComponentLinks from existing functions

    This widget inspects the calling signatures of helper functions,
    and presents the user with an interface for assigning componentIDs
    to the input and output arguments. It also generates ComponentLinks
    from this information.

    ComponentIDs are assigned to arguments via drag and drop. This
    widget is used within the LinkEditor dialog

    Usage::

       widget = LinkEquation()
    """

    category = CurrentComboTextProperty('_ui.category')
    function = CurrentComboDataProperty('_ui.function')

    def __init__(self, parent=None):
        super(LinkEquation, self).__init__(parent)

        # Set up mapping of function/helper name -> function/helper tuple. For the helpers, we use the 'display' name if available.
        self._argument_widgets = []
        self._output_widget = ArgumentWidget("")

        # pyqt4 can't take self as second argument here
        # for some reason. Manually embed
        self._ui = load_ui('link_equation.ui',
                           None,
                           directory=os.path.dirname(__file__))
        l = QtWidgets.QHBoxLayout()
        l.addWidget(self._ui)
        self.setLayout(l)

        self._ui.outputs_layout.addWidget(self._output_widget)

        self._populate_category_combo()
        self.category = 'General'
        self._populate_function_combo()
        self._connect()
        self._setup_editor()

    def set_result_visible(self, state):
        self._output_widget.setVisible(state)
        self._ui.output_label.setVisible(state)

    def is_helper(self):
        return self.function is not None and \
            type(self.function).__name__ == 'LinkHelper'

    def is_function(self):
        return self.function is not None and \
            type(self.function).__name__ == 'LinkFunction'

    @property
    def signature(self):
        """ Returns the ComponentIDs assigned to the input and output arguments

        :rtype: tuple of (input, output). Input is a list of ComponentIDs.
                output is a ComponentID
        """
        inp = [a.component_id for a in self._argument_widgets]
        out = self._output_widget.component_id
        return inp, out

    @signature.setter
    def signature(self, inout):
        inp, out = inout
        for i, a in zip(inp, self._argument_widgets):
            a.component_id = i
        self._output_widget.component_id = out

    @messagebox_on_error("Failed to create links")
    def links(self):
        """ Create ComponentLinks from the state of the widget

        :rtype: list of ComponentLinks that can be created.

        If no links can be created (e.g. because of missing input),
        the empty list is returned
        """
        inp, out = self.signature
        if self.is_function():
            using = self.function.function
            if not all(inp) or not out:
                return []
            link = core.component_link.ComponentLink(inp, out, using)
            return [link]
        if self.is_helper():
            helper = self.function.helper
            if not all(inp):
                return []
            return helper(*inp)

    def _update_add_enabled(self):
        state = True
        for a in self._argument_widgets:
            state = state and a.component_id is not None
        if self.is_function():
            state = state and self._output_widget.component_id is not None

    def _connect(self):
        signal = self._ui.function.currentIndexChanged
        signal.connect(nonpartial(self._setup_editor))
        signal.connect(nonpartial(self._update_add_enabled))
        self._output_widget.editor.textChanged.connect(
            nonpartial(self._update_add_enabled))
        self._ui.category.currentIndexChanged.connect(
            self._populate_function_combo)

    def clear_inputs(self):
        for w in self._argument_widgets:
            w.clear()
        self._output_widget.clear()

    def _setup_editor(self):
        if self.is_function():
            self._setup_editor_function()
        else:
            self._setup_editor_helper()

    def _setup_editor_function(self):
        """ Prepare the widget for the active function."""
        assert self.is_function()
        self.set_result_visible(True)
        func = self.function.function
        args = getfullargspec(func)[0]
        label = function_label(self.function)
        self._ui.info.setText(label)
        self._output_widget.label = self.function.output_labels[0]
        self._clear_input_canvas()
        for a in args:
            self._add_argument_widget(a)

    def _setup_editor_helper(self):
        """Setup the editor for the selected link helper"""
        assert self.is_helper()
        self.set_result_visible(False)
        label = helper_label(self.function)
        args = self.function.input_labels
        self._ui.info.setText(label)

        self._clear_input_canvas()
        for a in args:
            self._add_argument_widget(a)

    def _add_argument_widget(self, argument):
        """ Create and add a single argument widget to the input canvas
        :param arguement: The argument name (string)
        """
        widget = ArgumentWidget(argument)
        widget.editor.textChanged.connect(nonpartial(self._update_add_enabled))
        self._ui.inputs_layout.addWidget(widget)
        self._argument_widgets.append(widget)

    def _clear_input_canvas(self):
        """ Remove all widgets from the input canvas """
        layout = self._ui.inputs_layout
        for a in self._argument_widgets:
            layout.removeWidget(a)
            a.close()

        self._argument_widgets = []

    def _populate_category_combo(self):
        f = [f for f in link_function.members if len(f.output_labels) == 1]
        categories = sorted(set(l.category for l in f + link_helper.members))
        update_combobox(self._ui.category, list(zip(categories, categories)))

    def _populate_function_combo(self):
        """ Add name of functions to function combo box """
        f = [f for f in link_function.members if len(f.output_labels) == 1]
        functions = ((get_function_name(l[0]), l)
                     for l in f + link_helper.members
                     if l.category == self.category)
        update_combobox(self._ui.function, functions)