def press(self, event): if not self.active or not event.inaxes: return self.pressed = True x_min, x_max = self._axes.get_xlim() x_range = abs(x_max - x_min) if self.state.x_min is None or self.state.x_max is None: self.mode = 'move-x-max' with delay_callback(self.state, 'x_min', 'x_max'): self.state.x_min = event.xdata self.state.x_max = event.xdata elif abs(event.xdata - self.state.x_min) / x_range < PICK_THRESH: self.mode = 'move-x-min' elif abs(event.xdata - self.state.x_max) / x_range < PICK_THRESH: self.mode = 'move-x-max' elif (event.xdata > self.state.x_min) is (event.xdata < self.state.x_max): self.mode = 'move' self.move_params = (event.xdata, self.state.x_min, self.state.x_max) else: self.mode = 'move-x-max' self.state.x_min = event.xdata
def update_view_to_bins(self, *args): """ Update the view to match the histogram interval """ with delay_callback(self, 'x_min', 'x_max'): self.x_min = self.hist_x_min self.x_max = self.hist_x_max
def limits_to_mpl(self): if self.state.x_min is not None and self.state.x_max is not None: self.axes.set_xlim(self.state.x_min, self.state.x_max) if self.state.y_min is not None and self.state.y_max is not None: self.axes.set_ylim(self.state.y_min, self.state.y_max) if self.state.aspect == 'equal': # FIXME: for a reason I don't quite understand, dataLim doesn't # get updated immediately here, which means that there are then # issues in the first draw of the image (the limits are such that # only part of the image is shown). We just set dataLim manually # to avoid this issue. self.axes.dataLim.intervalx = self.axes.get_xlim() self.axes.dataLim.intervaly = self.axes.get_ylim() # We then force the aspect to be computed straight away self.axes.apply_aspect() # And propagate any changes back to the state since we have the # @avoid_circular decorator with delay_callback(self.state, 'x_min', 'x_max', 'y_min', 'y_max'): self.state.x_min, self.state.x_max = self.axes.get_xlim() self.state.y_min, self.state.y_max = self.axes.get_ylim() self.axes.figure.canvas.draw()
def _set_wcs(self, *args): if self.state.x_att is None or self.state.y_att is None or self.state.reference_data is None: return ref_coords = self.state.reference_data.coords if hasattr(ref_coords, 'wcs'): self.axes.reset_wcs(slices=self.state.wcsaxes_slice, wcs=ref_coords.wcs) elif hasattr(ref_coords, 'wcsaxes_dict'): self.axes.reset_wcs(slices=self.state.wcsaxes_slice, **ref_coords.wcsaxes_dict) else: self.axes.reset_wcs(IDENTITY_WCS) self._update_appearance_from_settings() self._update_axes() nx = self.state.reference_data.shape[self.state.x_att.axis] ny = self.state.reference_data.shape[self.state.y_att.axis] with delay_callback(self.state, 'x_min', 'x_max', 'y_min', 'y_max'): self.state.x_min = -0.5 self.state.x_max = nx - 0.5 self.state.y_min = -0.5 self.state.y_max = ny - 0.5
def _reset_x_limits(self, *args): if self.x_att is None: return with delay_callback(self, 'hist_x_min', 'hist_x_max', 'x_min', 'x_max', 'x_log'): self.x_lim_helper.percentile = 100 self.x_lim_helper.update_values(force=True) self.update_bins_to_view()
def _on_layer_change(self, layer=None): with delay_callback(self, 'cmap_vmin', 'cmap_vmax', 'size_vmin', 'size_vmax', 'density_map'): self._update_density_map_mode() if self.layer is None: self.cmap_att_helper.set_multiple_data([]) self.size_att_helper.set_multiple_data([]) else: self.cmap_att_helper.set_multiple_data([self.layer]) self.size_att_helper.set_multiple_data([self.layer]) if self.layer is None: self.xerr_att_helper.set_multiple_data([]) self.yerr_att_helper.set_multiple_data([]) else: self.xerr_att_helper.set_multiple_data([self.layer]) self.yerr_att_helper.set_multiple_data([self.layer]) if self.layer is None: self.vx_att_helper.set_multiple_data([]) self.vy_att_helper.set_multiple_data([]) else: self.vx_att_helper.set_multiple_data([self.layer]) self.vy_att_helper.set_multiple_data([self.layer])
def _on_data_change(self, *args): links = self.visible_links with delay_callback(self, 'current_link'): LinkEditorState.current_link.set_choices(self, links) if len(links) > 0: self.current_link = links[0]
def _reset_x_limits(self, *event): # NOTE: we don't use AttributeLimitsHelper because we need to avoid # trying to get the minimum of *all* the world coordinates in the # dataset. Instead, we use the same approach as in the layer state below # and in the case of world coordinates we use online the spine of the # data. if self.reference_data is None or self.x_att_pixel is None: return data = self.reference_data if self.x_att in data.pixel_component_ids: x_min, x_max = -0.5, data.shape[self.x_att.axis] - 0.5 else: axis = data.world_component_ids.index(self.x_att) axis_view = [0] * data.ndim axis_view[axis] = slice(None) axis_values = data[self.x_att, tuple(axis_view)] x_min, x_max = np.nanmin(axis_values), np.nanmax(axis_values) with delay_callback(self, 'x_min', 'x_max'): self.x_min = x_min self.x_max = x_max
def update_limits(self, update_profile=True): with delay_callback(self, 'v_min', 'v_max'): if update_profile: self.update_profile(update_limits=False) if self._profile_cache is not None and len(self._profile_cache[1]) > 0: self.v_min = nanmin(self._profile_cache[1]) self.v_max = nanmax(self._profile_cache[1])
def _on_layer_change(self, layer=None): with delay_callback(self, 'vmin', 'vmin'): if self.layer is None: self.att_helper.set_multiple_data([]) else: self.att_helper.set_multiple_data([self.layer])
def _update_combo_att(self): with delay_callback(self, 'x_att_world', 'y_att_world'): if self.reference_data is None: self.xw_att_helper.set_multiple_data([]) self.yw_att_helper.set_multiple_data([]) else: self.xw_att_helper.set_multiple_data([self.reference_data]) self.yw_att_helper.set_multiple_data([self.reference_data])
def set_limits(self, x_min, x_max, y_min, y_max, z_min, z_max): with delay_callback(self, 'x_min', 'x_max', 'y_min', 'y_max', 'z_min', 'z_max'): self.x_min = x_min self.x_max = x_max self.y_min = y_min self.y_max = y_max self.z_min = z_min self.z_max = z_max
def _reference_data_changed(self, *args): # This signal can get emitted if just the choices but not the actual # reference data change, so we check here that the reference data has # actually changed if self.reference_data is not getattr(self, '_last_reference_data', None): self._last_reference_data = self.reference_data with delay_callback(self, 'x_att_world', 'y_att_world', 'slices'): self._update_combo_att() self._set_default_slices()
def remove_data(self, data): with delay_callback(self.state, 'layers'): for layer_state in self.state.layers[::-1]: if isinstance(layer_state.layer, BaseData): if layer_state.layer is data: self.state.layers.remove(layer_state) else: if layer_state.layer.data is data: self.state.layers.remove(layer_state)
def _on_layer_change(self, layer=None): with delay_callback(self, 'cmap_vmin', 'cmap_vmax', 'size_vmin', 'size_vmax'): if self.layer is None: self.cmap_att_helper.set_multiple_data([]) self.size_att_helper.set_multiple_data([]) else: self.cmap_att_helper.set_multiple_data([self.layer]) self.size_att_helper.set_multiple_data([self.layer])
def update_bins_to_view(self, *args): """ Update the bins to match the current view. """ with delay_callback(self, 'hist_x_min', 'hist_x_max'): if self.x_max > self.x_min: self.hist_x_min = self.x_min self.hist_x_max = self.x_max else: self.hist_x_min = self.x_max self.hist_x_max = self.x_min
def _update_att(self, *args): # Need to delay the callbacks here to make sure that we get a chance to # update both x_att and y_att otherwise could end up triggering image # slicing with two pixel components that are the same. with delay_callback(self, 'x_att', 'y_att'): if self.x_att_world is not None: index = self.reference_data.world_component_ids.index(self.x_att_world) self.x_att = self.reference_data.pixel_component_ids[index] if self.y_att_world is not None: index = self.reference_data.world_component_ids.index(self.y_att_world) self.y_att = self.reference_data.pixel_component_ids[index]
def reset_limits(self): if self.reference_data is None or self.x_att is None or self.y_att is None: return nx = self.reference_data.shape[self.x_att.axis] ny = self.reference_data.shape[self.y_att.axis] with delay_callback(self, 'x_min', 'x_max', 'y_min', 'y_max'): self.x_min = -0.5 self.x_max = nx - 0.5 self.y_min = -0.5 self.y_max = ny - 0.5
def move(self, event): if not self.active or not self.pressed or not event.inaxes: return if self.mode == 'move-x-min': self.state.x_min = event.xdata elif self.mode == 'move-x-max': self.state.x_max = event.xdata elif self.mode == 'move': orig_click, orig_x_min, orig_x_max = self.move_params with delay_callback(self.state, 'x_min', 'x_max'): self.state.x_min = orig_x_min + (event.xdata - orig_click) self.state.x_max = orig_x_max + (event.xdata - orig_click)
def _reference_data_changed(self, *args): # This signal can get emitted if just the choices but not the actual # reference data change, so we check here that the reference data has # actually changed if self.reference_data is not getattr(self, '_last_reference_data', None): self._last_reference_data = self.reference_data # Note that we deliberately use nested delay_callback here, because # we want to make sure that x_att_world and y_att_world both get # updated first, then x_att and y_att can be changed, before # subsequent events are fired. with delay_callback(self, 'x_att', 'y_att'): with delay_callback(self, 'x_att_world', 'y_att_world', 'slices'): if self._display_world: self.xw_att_helper.pixel_coord = False self.yw_att_helper.pixel_coord = False self.xw_att_helper.world_coord = True self.yw_att_helper.world_coord = True else: self.xw_att_helper.pixel_coord = True self.yw_att_helper.pixel_coord = True self.xw_att_helper.world_coord = False self.yw_att_helper.world_coord = False self._update_combo_att() self._set_default_slices()
def new_link(self, function_or_helper): if hasattr(function_or_helper, 'function'): link = EditableLinkFunctionState(function_or_helper.function, data_in=self.data1, data_out=self.data2, output_names=function_or_helper.output_labels, description=function_or_helper.info, display=function_or_helper.function.__name__) else: link = EditableLinkFunctionState(function_or_helper.helper, data_in=self.data1, data_out=self.data2) self.links.append(link) with delay_callback(self, 'current_link'): self._on_data_change() self.current_link = link
def move(self, event): """ Update bias and contrast on Right Mouse button drag. """ if event.button not in (1, 3): return x, y = self.viewer.axes.transAxes.inverted().transform((event.x, event.y)) state = self.viewer.selected_layer.state with delay_callback(state, 'bias', 'contrast'): state.bias = -(x * 2 - 1.5) state.contrast = 10. ** (y * 2 - 1) super(ContrastBiasMode, self).move(event)
def reset_limits(self): if self.reference_data is None or self.x_att is None or self.y_att is None: return nx = self.reference_data.shape[self.x_att.axis] ny = self.reference_data.shape[self.y_att.axis] with delay_callback(self, 'x_min', 'x_max', 'y_min', 'y_max'): self.x_min = -0.5 self.x_max = nx - 0.5 self.y_min = -0.5 self.y_max = ny - 0.5 # We need to adjust the limits in here to avoid triggering all # the update events then changing the limits again. self._adjust_limits_aspect()
def limits_from_mpl(self, *args): with delay_callback(self.state, 'x_min', 'x_max', 'y_min', 'y_max'): if isinstance(self.state.x_min, np.datetime64): x_min, x_max = [mpl_to_datetime64(x) for x in self.axes.get_xlim()] else: x_min, x_max = self.axes.get_xlim() self.state.x_min, self.state.x_max = x_min, x_max if isinstance(self.state.y_min, np.datetime64): y_min, y_max = [mpl_to_datetime64(y) for y in self.axes.get_ylim()] else: y_min, y_max = self.axes.get_ylim() self.state.y_min, self.state.y_max = y_min, y_max
def set(self, cache=True, **kwargs): self._in_set = True extra_kwargs = set(kwargs.keys()) - set(self.values_names) - set(self.modifiers_names) if len(extra_kwargs) > 0: raise ValueError("Invalid properties: {0}".format(extra_kwargs)) with delay_callback(self._state, *self._attribute_lookup.values()): for prop, value in kwargs.items(): setattr(self, prop, value) if cache: self._update_cache() self._in_set = False
def _on_yatt_world_change(self, *args): if self.y_att_world is not None: with delay_callback(self, 'x_att_world', 'y_att'): if self.y_att_world == self.x_att_world: world_ids = self.reference_data.world_component_ids if self.y_att_world == world_ids[-1]: self.x_att_world = world_ids[-2] else: self.x_att_world = world_ids[-1] if self._display_world: index = self.reference_data.world_component_ids.index(self.y_att_world) self.y_att = self.reference_data.pixel_component_ids[index] else: self.y_att = self.y_att_world
def __init__(self, layer_artist): super(IsosurfaceLayerStyleWidget, 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 attribute and visual options self._setup_options() self._connect_global() # Set initial values self.layer_artist.color = self.layer.style.color self.layer_artist.alpha = self.layer.style.alpha with delay_callback(self.layer_artist, 'attribute'): self.attribute = self.visible_components[0] self._update_levels() self.layer_artist.visible = True
def limits_from_mpl(self, *args, **kwargs): if getattr(self, '_skip_limits_from_mpl', False): return if isinstance(self.state.x_min, np.datetime64): x_min, x_max = [mpl_to_datetime64(x) for x in self.axes.get_xlim()] else: x_min, x_max = self.axes.get_xlim() if isinstance(self.state.y_min, np.datetime64): y_min, y_max = [mpl_to_datetime64(y) for y in self.axes.get_ylim()] else: y_min, y_max = self.axes.get_ylim() with delay_callback(self.state, 'x_min', 'x_max', 'y_min', 'y_max'): self.state.x_min = x_min self.state.x_max = x_max self.state.y_min = y_min self.state.y_max = y_max
def __init__(self, layer_artist): super(VolumeLayerStyleWidget, 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 attribute and visual options self._setup_options() self._connect_global() # Set initial values self.layer_artist.color = self.layer.style.color self.layer_artist.alpha = self.layer.style.alpha with delay_callback(self.layer_artist, 'attribute'): self.attribute = self.visible_components[0] self._update_limits() if isinstance(self.layer, Subset): self.ui.radio_subset_data.setChecked(True) self.layer_artist.visible = True
def update_from_dict(self, properties): """ Update this state using the values from a dictionary of attributes. Parameters ---------- properties : dict The dictionary containing attribute/value pairs. """ if len(properties) == 0: return # Group properties into priority so as to be able to update them in # chunks and not fire off callback events between every single one. groups = defaultdict(list) for name in properties: if self.is_callback_property(name): groups[self._update_priority(name)].append(name) for priority in sorted(groups, reverse=True): with delay_callback(self, *groups[priority]): for name in groups[priority]: setattr(self, name, properties[name])
def clear(self): with delay_callback(self.state, 'x_min', 'x_max'): self.state.x_min = None self.state.x_max = None
def limits_from_mpl(self): with delay_callback(self.state, 'x_min', 'x_max', 'y_min', 'y_max'): self.state.x_min, self.state.x_max = self.axes.get_xlim() self.state.y_min, self.state.y_max = self.axes.get_ylim()
def reset_limits(self): with delay_callback(self, 'x_min', 'x_max', 'y_min', 'y_max'): self.x_lim_helper.percentile = 100 self.x_lim_helper.update_values(force=True) self._reset_y_limits()
def flip_x(self): """ Flip the x_min/x_max limits. """ with delay_callback(self, 'x_min', 'x_max'): self.x_min, self.x_max = self.x_max, self.x_min
def flip_y(self): """ Flip the y_min/y_max limits. """ with delay_callback(self, 'y_min', 'y_max'): self.y_min, self.y_max = self.y_max, self.y_min
def _reference_data_changed(self, *args): with delay_callback(self, 'x_att_world', 'y_att_world', 'slices'): self._update_combo_att() self._set_default_slices()
def reset_contrast_bias(self): with delay_callback(self, 'contrast', 'bias'): self.contrast = 1 self.bias = 0.5
def choices(self, choices): with delay_callback(self.state, self.selection_property): prop = getattr(type(self.state), self.selection_property) prop.set_choices(self.state, choices)
def _reset_x_limits(self, *args): with delay_callback(self, 'hist_x_min', 'hist_x_max', 'x_min', 'x_max', 'x_log'): self.x_lim_helper.percentile = 100 self.x_lim_helper.update_values(force=True) self.update_bins_to_view()
def _reset_y_limits(self, *event): if self.normalize: with delay_callback(self, 'y_min', 'y_max'): self.y_min = -0.1 self.y_max = +1.1