Esempio n. 1
0
class PreferencesDialog(QtWidgets.QDialog):

    theme = CurrentComboTextProperty('ui.combo_theme')
    background = ColorProperty('ui.color_background')
    foreground = ColorProperty('ui.color_foreground')
    data_color = ColorProperty('ui.color_default_data')
    data_alpha = ValueProperty('ui.slider_alpha', value_range=(0, 1))
    data_apply = ButtonProperty('ui.checkbox_apply')
    save_to_disk = ButtonProperty('ui.checkbox_save')
    font_size = ValueProperty('ui.spinner_font_size')

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

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

        self._app = weakref.ref(application)

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

        self.ui.cancel.clicked.connect(self.reject)
        self.ui.ok.clicked.connect(self.accept)

        self.ui.combo_theme.currentIndexChanged.connect(self._update_colors_from_theme)

        self.ui.button_reset_dialogs.clicked.connect(self._reset_dialogs)

        # The following is needed because of a bug in Qt which means that
        # tab titles don't get scaled right.
        if platform.system() == 'Darwin':
            app = get_qapp()
            app_font = app.font()
            self.ui.tab_widget.setStyleSheet('font-size: {0}px'.format(app_font.pointSize()))

        from glue.config import settings
        self.background = settings.BACKGROUND_COLOR
        self.foreground = settings.FOREGROUND_COLOR
        self.data_color = settings.DATA_COLOR
        self.data_alpha = settings.DATA_ALPHA
        self.font_size = settings.FONT_SIZE

        self._update_theme_from_colors()

        self.panes = []

        from glue.config import preference_panes
        for label, widget_cls in sorted(preference_panes):
            pane = widget_cls()
            self.ui.tab_widget.addTab(pane, label)
            self.panes.append(pane)

    def _update_theme_from_colors(self, *args):
        if (rgb(self.background) == (1, 1, 1) and rgb(self.foreground) == (0, 0, 0) and
                rgb(self.data_color) == (0.35, 0.35, 0.35) and np.allclose(self.data_alpha, 0.8)):
            self.theme = 'Black on White'
        elif (rgb(self.background) == (0, 0, 0) and rgb(self.foreground) == (1, 1, 1) and
                rgb(self.data_color) == (0.75, 0.75, 0.75) and np.allclose(self.data_alpha, 0.8)):
            self.theme = 'White on Black'
        else:
            self.theme = 'Custom'

    def _update_colors_from_theme(self, *args):
        if self.theme == 'Black on White':
            self.foreground = 'black'
            self.background = 'white'
            self.data_color = '0.35'
            self.data_alpha = 0.8
        elif self.theme == 'White on Black':
            self.foreground = 'white'
            self.background = 'black'
            self.data_color = '0.75'
            self.data_alpha = 0.8
        elif self.theme != 'Custom':
            raise ValueError("Unknown theme: {0}".format(self.theme))

    def _reset_dialogs(self, *args):
        from glue.config import settings
        for key, _, _ in settings:
            if key.lower().startswith(('show_info', 'show_warn', 'show_large')):
                setattr(settings, key, True)

    def accept(self):

        # Update default settings

        from glue.config import settings
        settings.FOREGROUND_COLOR = self.foreground
        settings.BACKGROUND_COLOR = self.background
        settings.DATA_COLOR = self.data_color
        settings.DATA_ALPHA = self.data_alpha
        settings.FONT_SIZE = self.font_size

        for pane in self.panes:
            pane.finalize()

        # Save to disk if requested
        if self.save_to_disk:
            save_settings()
        else:
            settings._save_to_disk = True

        # Trigger viewers to update defaults

        app = self._app()

        if app is not None:
            app._hub.broadcast(SettingsChangeMessage(self, ('FOREGROUND_COLOR', 'BACKGROUND_COLOR', 'FONT_SIZE')))
            if self.data_apply:  # If requested, trigger data to update color
                app.set_data_color(settings.DATA_COLOR, settings.DATA_ALPHA)

        super(PreferencesDialog, self).accept()
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)
class ScatterLayerStyleWidget(QtWidgets.QWidget):

    # Size-related GUI elements
    size_mode = CurrentComboTextProperty('ui.combo_size_mode')
    size_attribute = CurrentComboProperty('ui.combo_size_attribute')
    size_vmin = FloatLineProperty('ui.value_size_vmin')
    size_vmax = FloatLineProperty('ui.value_size_vmax')
    size = FloatLineProperty('ui.value_fixed_size')

    try:
        size_scaling = ValueProperty('ui.slider_size_scaling', value_range=(0.1, 10), log=True)
    except TypeError:  # Glue < 0.8
        size_scaling = ValueProperty('ui.slider_size_scaling')

    # Color-related GUI elements
    color_mode = CurrentComboTextProperty('ui.combo_color_mode')
    cmap_attribute = CurrentComboProperty('ui.combo_cmap_attribute')
    cmap_vmin = FloatLineProperty('ui.value_cmap_vmin')
    cmap_vmax = FloatLineProperty('ui.value_cmap_vmax')
    cmap = CurrentComboProperty('ui.combo_cmap')
    alpha = ValueProperty('ui.slider_alpha')

    def __init__(self, layer_artist):

        super(ScatterLayerStyleWidget, self).__init__()

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

        self.layer_artist = layer_artist
        self.layer = layer_artist.layer

        # Set up size and color options
        self._setup_size_options()
        self._setup_color_options()
        self._connect_global()

        # Set initial values
        self.layer_artist.size = self.layer.style.markersize
        self.layer_artist.size_scaling = 1
        self.layer_artist.size_mode = 'fixed'
        self.size_mode = 'Fixed'
        self.layer_artist.color = self.layer.style.color
        self.layer_artist.alpha = self.layer.style.alpha
        self.layer_artist.color_mode = 'fixed'
        self.color_mode = 'Fixed'
        self.ui.combo_size_attribute.setCurrentIndex(0)
        self.ui.combo_cmap_attribute.setCurrentIndex(0)
        self.ui.combo_cmap.setCurrentIndex(0)
        self.layer_artist.visible = True

        self._update_size_mode()
        self._update_color_mode()

    def _connect_global(self):
        connect_float_edit(self.layer.style, 'markersize', self.ui.value_fixed_size)
        connect_color(self.layer.style, 'color', self.ui.label_color)
        connect_value(self.layer.style, 'alpha', self.ui.slider_alpha, value_range=(0, 1))

    def _disconnect_global(self):
        # FIXME: Requires the ability to disconnect connections
        pass

    def _setup_size_options(self):

        # Set up attribute list
        label_data = [(comp.label, comp) for comp in self.visible_components]
        update_combobox(self.ui.combo_size_attribute, label_data)

        # Set up connections with layer artist
        connect_float_edit(self.layer_artist, 'size', self.ui.value_fixed_size)
        connect_current_combo(self.layer_artist, 'size_attribute', self.ui.combo_size_attribute)
        connect_float_edit(self.layer_artist, 'size_vmin', self.ui.value_size_vmin)
        connect_float_edit(self.layer_artist, 'size_vmax', self.ui.value_size_vmax)
        connect_value(self.layer_artist, 'size_scaling', self.ui.slider_size_scaling, value_range=(0.1, 10), log=True)

        # Set up internal connections
        self.ui.combo_size_mode.currentIndexChanged.connect(self._update_size_mode)
        self.ui.combo_size_attribute.currentIndexChanged.connect(self._update_size_limits)
        self.ui.button_flip_size.clicked.connect(self._flip_size)

    def _update_size_mode(self):

        self.layer_artist.size_mode = self.size_mode.lower()

        if self.size_mode == "Fixed":
            self.ui.size_row_2.hide()
            self.ui.combo_size_attribute.hide()
            self.ui.value_fixed_size.show()
        else:
            self.ui.value_fixed_size.hide()
            self.ui.combo_size_attribute.show()
            self.ui.size_row_2.show()

    def _update_size_limits(self):

        if not hasattr(self, '_size_limits'):
            self._size_limits = {}

        if self.size_attribute in self._size_limits:
            self.size_vmin, self.size_vmax = self._size_limits[self.size_attribute]
        else:
            self.size_vmin, self.size_vmax = self.default_limits(self.size_attribute)
            self._size_limits[self.size_attribute] = self.size_vmin, self.size_vmax

    def _flip_size(self):
        self.size_vmin, self.size_vmax = self.size_vmax, self.size_vmin

    def _setup_color_options(self):

        # Set up attribute list
        label_data = [(comp.label, comp) for comp in self.visible_components]
        update_combobox(self.ui.combo_cmap_attribute, label_data)

        # Set up connections with layer artist
        connect_color(self.layer_artist, 'color', self.ui.label_color)
        connect_current_combo(self.layer_artist, 'cmap_attribute', self.ui.combo_cmap_attribute)
        connect_float_edit(self.layer_artist, 'cmap_vmin', self.ui.value_cmap_vmin)
        connect_float_edit(self.layer_artist, 'cmap_vmax', self.ui.value_cmap_vmax)
        connect_current_combo(self.layer_artist, 'cmap', self.ui.combo_cmap)
        connect_value(self.layer_artist, 'alpha', self.ui.slider_alpha, value_range=(0, 1))

        # Set up internal connections
        self.ui.combo_color_mode.currentIndexChanged.connect(self._update_color_mode)
        self.ui.combo_cmap_attribute.currentIndexChanged.connect(self._update_cmap_limits)
        self.ui.button_flip_cmap.clicked.connect(self._flip_cmap)

    def _update_color_mode(self):

        self.layer_artist.color_mode = self.color_mode.lower()

        if self.color_mode == "Fixed":
            self.ui.color_row_2.hide()
            self.ui.color_row_3.hide()
            self.ui.combo_cmap_attribute.hide()
            self.ui.spacer_color_label.show()
            self.ui.label_color.show()
        else:
            self.ui.label_color.hide()
            self.ui.combo_cmap_attribute.show()
            self.ui.spacer_color_label.hide()
            self.ui.color_row_2.show()
            self.ui.color_row_3.show()

    def _update_cmap_limits(self):

        if not hasattr(self, '_cmap_limits'):
            self._cmap_limits = {}

        if self.cmap_attribute in self._cmap_limits:
            self.cmap_vmin, self.cmap_vmax = self._cmap_limits[self.cmap_attribute]
        else:
            self.cmap_vmin, self.cmap_vmax = self.default_limits(self.cmap_attribute)
            self._cmap_limits[self.cmap_attribute] = self.cmap_vmin, self.cmap_vmax

    def _flip_cmap(self):
        self.cmap_vmin, self.cmap_vmax = self.cmap_vmax, self.cmap_vmin

    def default_limits(self, attribute):
        # For subsets, we want to compute the limits based on the full
        # dataset not just the subset.
        if isinstance(self.layer, Subset):
            vmin = np.nanmin(self.layer.data[attribute])
            vmax = np.nanmax(self.layer.data[attribute])
        else:
            vmin = np.nanmin(self.layer[attribute])
            vmax = np.nanmax(self.layer[attribute])
        return vmin, vmax

    @property
    def visible_components(self):
        if isinstance(self.layer, Subset):
            return self.layer.data.visible_components
        else:
            return self.layer.visible_components
Esempio n. 4
0
class PreferencesDialog(QtGui.QDialog):

    theme = CurrentComboTextProperty('ui.combo_theme')
    background = ColorProperty('ui.color_background')
    foreground = ColorProperty('ui.color_foreground')
    data_color = ColorProperty('ui.color_default_data')
    data_alpha = ValueProperty('ui.slider_alpha', value_range=(0, 1))
    data_apply = ButtonProperty('ui.checkbox_apply')
    save_to_disk = ButtonProperty('ui.checkbox_save')

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

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

        self.app = application

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

        self.ui.cancel.clicked.connect(self.reject)
        self.ui.ok.clicked.connect(self.accept)

        self.ui.combo_theme.currentIndexChanged.connect(
            nonpartial(self._update_colors_from_theme))

        from glue.config import settings
        self.background = settings.BACKGROUND_COLOR
        self.foreground = settings.FOREGROUND_COLOR
        self.data_color = settings.DATA_COLOR
        self.data_alpha = settings.DATA_ALPHA

        self._update_theme_from_colors()

        self.panes = []

        from glue.config import preference_panes
        for label, widget_cls in sorted(preference_panes):
            pane = widget_cls()
            self.ui.tab_widget.addTab(pane, label)
            self.panes.append(pane)

    def _update_theme_from_colors(self):
        if (rgb(self.background) == (1, 1, 1)
                and rgb(self.foreground) == (0, 0, 0)
                and rgb(self.data_color) == (0.35, 0.35, 0.35)
                and np.allclose(self.data_alpha, 0.8)):
            self.theme = 'Black on White'
        elif (rgb(self.background) == (0, 0, 0)
              and rgb(self.foreground) == (1, 1, 1)
              and rgb(self.data_color) == (0.75, 0.75, 0.75)
              and np.allclose(self.data_alpha, 0.8)):
            self.theme = 'White on Black'
        else:
            self.theme = 'Custom'

    def _update_colors_from_theme(self):
        if self.theme == 'Black on White':
            self.foreground = 'black'
            self.background = 'white'
            self.data_color = '0.35'
            self.data_alpha = 0.8
        elif self.theme == 'White on Black':
            self.foreground = 'white'
            self.background = 'black'
            self.data_color = '0.75'
            self.data_alpha = 0.8
        elif self.theme != 'Custom':
            raise ValueError("Unknown theme: {0}".format(self.theme))

    def accept(self):

        # Update default settings

        from glue.config import settings
        settings.FOREGROUND_COLOR = self.foreground
        settings.BACKGROUND_COLOR = self.background
        settings.DATA_COLOR = self.data_color
        settings.DATA_ALPHA = self.data_alpha

        for pane in self.panes:
            pane.finalize()

        # Save to disk if requested
        if self.save_to_disk:
            save_settings()

        # Trigger viewers to update defaults

        self.app._hub.broadcast(
            SettingsChangeMessage(self,
                                  ('FOREGROUND_COLOR', 'BACKGROUND_COLOR')))

        # If requested, trigger data to update color

        if self.data_apply:
            self.app.set_data_color(settings.DATA_COLOR, settings.DATA_ALPHA)

        super(PreferencesDialog, self).accept()
Esempio n. 5
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)
Esempio n. 6
0
class PreferencesDialog(QtWidgets.QDialog):

    theme = CurrentComboTextProperty('ui.combo_theme')
    background = ColorProperty('ui.color_background')
    foreground = ColorProperty('ui.color_foreground')
    data_color = ColorProperty('ui.color_default_data')
    data_alpha = ValueProperty('ui.slider_alpha', value_range=(0, 1))
    data_apply = ButtonProperty('ui.checkbox_apply')
    show_large_data_warning = ButtonProperty(
        'ui.checkbox_show_large_data_warning')
    individual_subset_color = ButtonProperty(
        'ui.checkbox_individual_subset_color')
    save_to_disk = ButtonProperty('ui.checkbox_save')

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

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

        self.app = application

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

        self.ui.cancel.clicked.connect(self.reject)
        self.ui.ok.clicked.connect(self.accept)

        self.ui.combo_theme.currentIndexChanged.connect(
            nonpartial(self._update_colors_from_theme))

        # The following is needed because of a bug in Qt which means that
        # tab titles don't get scaled right.
        app = get_qapp()
        app_font = app.font()
        self.ui.tab_widget.setStyleSheet('font-size: {0}px'.format(
            app_font.pointSize()))

        from glue.config import settings
        self.background = settings.BACKGROUND_COLOR
        self.foreground = settings.FOREGROUND_COLOR
        self.data_color = settings.DATA_COLOR
        self.data_alpha = settings.DATA_ALPHA
        self.show_large_data_warning = settings.SHOW_LARGE_DATA_WARNING
        self.individual_subset_color = settings.INDIVIDUAL_SUBSET_COLOR

        self._update_theme_from_colors()

        self.panes = []

        from glue.config import preference_panes
        for label, widget_cls in sorted(preference_panes):
            pane = widget_cls()
            self.ui.tab_widget.addTab(pane, label)
            self.panes.append(pane)

    def _update_theme_from_colors(self):
        if (rgb(self.background) == (1, 1, 1)
                and rgb(self.foreground) == (0, 0, 0)
                and rgb(self.data_color) == (0.35, 0.35, 0.35)
                and np.allclose(self.data_alpha, 0.8)):
            self.theme = 'Black on White'
        elif (rgb(self.background) == (0, 0, 0)
              and rgb(self.foreground) == (1, 1, 1)
              and rgb(self.data_color) == (0.75, 0.75, 0.75)
              and np.allclose(self.data_alpha, 0.8)):
            self.theme = 'White on Black'
        else:
            self.theme = 'Custom'

    def _update_colors_from_theme(self):
        if self.theme == 'Black on White':
            self.foreground = 'black'
            self.background = 'white'
            self.data_color = '0.35'
            self.data_alpha = 0.8
        elif self.theme == 'White on Black':
            self.foreground = 'white'
            self.background = 'black'
            self.data_color = '0.75'
            self.data_alpha = 0.8
        elif self.theme != 'Custom':
            raise ValueError("Unknown theme: {0}".format(self.theme))

    def accept(self):

        # Update default settings

        from glue.config import settings
        settings.FOREGROUND_COLOR = self.foreground
        settings.BACKGROUND_COLOR = self.background
        settings.DATA_COLOR = self.data_color
        settings.DATA_ALPHA = self.data_alpha
        settings.SHOW_LARGE_DATA_WARNING = self.show_large_data_warning
        settings.INDIVIDUAL_SUBSET_COLOR = self.individual_subset_color

        for pane in self.panes:
            pane.finalize()

        # Save to disk if requested
        if self.save_to_disk:
            save_settings()

        # Trigger viewers to update defaults

        self.app._hub.broadcast(
            SettingsChangeMessage(self,
                                  ('FOREGROUND_COLOR', 'BACKGROUND_COLOR')))

        # If requested, trigger data to update color

        if self.data_apply:
            self.app.set_data_color(settings.DATA_COLOR, settings.DATA_ALPHA)

        super(PreferencesDialog, self).accept()