class TextConfigEditor(QPlainTextEdit): new_config = pyqtSignal(str) def __init__(self, *args): super(TextConfigEditor, self).__init__(*args) self._timer = TimedUpdate(self._timer_event) self.textChanged.connect(lambda: self._timer.add_delayed_callback(400)) @pyqtSlot() def _timer_event(self): self.new_config.emit(self.toPlainText())
class ScientificNumberScroller(Ui_ScientificScroller, QWidget): valueChanged = pyqtSignal([float], ['QString']) def __init__(self, parent): super(ScientificNumberScroller, self).__init__(parent) self.setupUi(self) self.mantissa.valueChanged.connect(self._value_changed_cb) self.exponent.valueChanged.connect(self._value_changed_cb) self._timer = TimedUpdate(self._signal_new_value) self._update_delay = 0 @pyqtSlot() def _value_changed_cb(self): return self._timer.add_delayed_callback(self._update_delay) def set_update_delay(self, update_delay): self._update_delay = update_delay @pyqtSlot() def _signal_new_value(self): self.valueChanged.emit(self.mantissa.value() * 10**self.exponent.value()) @pyqtSlot(float) def setValue(self, value): if value == 0: self.exponent.setValue(0) self.mantissa.setValue(0) elif 1e-3 < np.abs(value) < 1e3: self.exponent.setValue(0) self.mantissa.setValue(value) else: exponent = int(np.floor(np.log10(np.abs(value)))) self.exponent.setValue(exponent) self.mantissa.setValue(value / 10**exponent) def blockSignals(self, block): self.mantissa.blockSignals(block) return self.exponent.blockSignals(block)
class TabGeneral(QWidget, Ui_TabGeneral): def __init__(self, controller, parent=None): super(TabGeneral, self).__init__(parent) self.setupUi(self) self._controller = controller self._controller.new_data.connect(self.set_new_data) self._controller.new_config.connect(self.set_new_config) self.general_display_order.setDragDropMode(QAbstractItemView.InternalMove) self.general_display_order.setSelectionMode(QAbstractItemView.SingleSelection) self.general_colormap.addItems(self._controller.get_config().get_available_colormaps()) self.general_rotate.addItems(['0', '90', '180', '270']) self.general_rotate.setCurrentText(str(self._controller.get_config().rotate)) self.general_DisplayOrder.set_collapse(True) self.general_Miscellaneous.set_collapse(True) self.general_Zoom.set_collapse(True) self.general_Font.set_collapse(True) self.general_dimension.valueChanged.connect(lambda v: self._controller.apply_action(SetDimension(v))) self.general_slice_index.valueChanged.connect(lambda v: self._controller.apply_action(SetSliceIndex(v))) self.general_volume_index.valueChanged.connect(lambda v: self._controller.apply_action(SetVolumeIndex(v))) self.general_colormap.currentIndexChanged.connect( lambda i: self._controller.apply_action(SetColormap(self.general_colormap.itemText(i)))) self.general_rotate.currentIndexChanged.connect( lambda i: self._controller.apply_action(SetRotate(int(self.general_rotate.itemText(i))))) self._map_selection_timer = TimedUpdate(self._update_maps_to_show) self.general_map_selection.itemSelectionChanged.connect( lambda: self._map_selection_timer.add_delayed_callback(500)) self.general_deselect_all_maps.clicked.connect(self._deleselect_all_maps) self.general_invert_map_selection.clicked.connect(self._invert_map_selection) self.general_zoom_x_0.valueChanged.connect(self._update_zoom) self.general_zoom_x_1.valueChanged.connect(self._update_zoom) self.general_zoom_y_0.valueChanged.connect(self._update_zoom) self.general_zoom_y_1.valueChanged.connect(self._update_zoom) self.plot_title.textEdited.connect(lambda txt: self._controller.apply_action(SetPlotTitle(txt))) self.general_zoom_reset.clicked.connect(lambda: self._controller.apply_action(SetZoom(Zoom.no_zoom()))) self.general_zoom_fit.clicked.connect(self._zoom_fit) self.general_display_order.items_reordered.connect(self._reorder_maps) self.general_show_axis.clicked.connect(lambda: self._controller.apply_action( SetShowAxis(self.general_show_axis.isChecked()))) self.general_colorbar_nmr_ticks.valueChanged.connect( lambda v: self._controller.apply_action(SetColorBarNmrTicks(v))) self.general_font_family.addItems(Font.font_names()) self.general_font_family.currentTextChanged.connect( lambda v: self._controller.apply_action(SetFont(self._controller.get_config().font.get_updated(family=v)))) self.general_font_size.valueChanged.connect( lambda: self._controller.apply_action( SetFont(self._controller.get_config().font.get_updated(size=self.general_font_size.value())))) self.general_interpolation.addItems(self._controller.get_config().get_available_interpolations()) self.general_interpolation.currentTextChanged.connect( lambda v: self._controller.apply_action(SetInterpolation(v))) self.general_flipud.clicked.connect(lambda: self._controller.apply_action( SetFlipud(self.general_flipud.isChecked()))) self.mask_name.currentIndexChanged.connect(self._update_mask_name) @pyqtSlot(DataInfo) def set_new_data(self, data_info): if data_info.directory: self.general_info_directory.setText(self._split_long_path_elements(data_info.directory)) else: self.general_info_directory.setText('-') if len(data_info.maps): self.general_info_nmr_maps.setText(str(len(data_info.maps))) else: self.general_info_nmr_maps.setText('0') with blocked_signals(self.general_map_selection): self.general_map_selection.clear() self.general_map_selection.addItems(data_info.sorted_keys) for index, map_name in enumerate(data_info.sorted_keys): item = self.general_map_selection.item(index) item.setData(Qt.UserRole, map_name) with blocked_signals(self.mask_name): self.mask_name.clear() self.mask_name.insertItem(0, '-- None --') self.mask_name.insertItems(1, data_info.sorted_keys) @pyqtSlot(MapPlotConfig) def set_new_config(self, config): data_info = self._controller.get_data() map_names = config.maps_to_show with blocked_signals(self.general_dimension): try: max_dimension = data_info.get_max_dimension(map_names) self.general_dimension.setMaximum(max_dimension) self.maximumDimension.setText(str(max_dimension)) except ValueError: self.general_dimension.setMaximum(0) self.maximumDimension.setText(str(0)) self.general_dimension.setValue(config.dimension) with blocked_signals(self.general_slice_index): try: max_slice = data_info.get_max_slice_index(config.dimension, map_names) self.general_slice_index.setMaximum(max_slice) self.maximumIndex.setText(str(max_slice)) except ValueError: self.general_slice_index.setMaximum(0) self.maximumIndex.setText(str(0)) self.general_slice_index.setValue(config.slice_index) with blocked_signals(self.general_volume_index): try: max_volume = data_info.get_max_volume_index(map_names) self.general_volume_index.setMaximum(max_volume) self.maximumVolume.setText(str(max_volume)) except ValueError: self.general_volume_index.setMaximum(0) self.maximumVolume.setText(str(0)) self.general_volume_index.setValue(config.volume_index) with blocked_signals(self.general_colormap): self.general_colormap.setCurrentText(config.colormap) with blocked_signals(self.general_rotate): self.general_rotate.setCurrentText(str(config.rotate)) if self.general_map_selection.count(): for map_name, map_config in config.map_plot_options.items(): if map_config.title: index = data_info.sorted_keys.index(map_name) item = self.general_map_selection.item(index) item.setData(Qt.DisplayRole, map_name + ' (' + map_config.title + ')') self.general_map_selection.blockSignals(True) for index, map_name in enumerate(data_info.sorted_keys): item = self.general_map_selection.item(index) if item: item.setSelected(map_name in map_names) self.general_map_selection.blockSignals(False) try: max_x = data_info.get_max_x_index(config.dimension, config.rotate, map_names) max_y = data_info.get_max_y_index(config.dimension, config.rotate, map_names) with blocked_signals(self.general_zoom_x_0, self.general_zoom_x_1, self.general_zoom_y_0, self.general_zoom_y_1): self.general_zoom_x_0.setMaximum(max_x) self.general_zoom_x_0.setValue(config.zoom.p0.x) self.general_zoom_x_1.setMaximum(max_x) self.general_zoom_x_1.setMinimum(config.zoom.p0.x) self.general_zoom_x_1.setValue(config.zoom.p1.x) self.general_zoom_y_0.setMaximum(max_y) self.general_zoom_y_0.setValue(config.zoom.p0.y) self.general_zoom_y_1.setMaximum(max_y) self.general_zoom_y_1.setMinimum(config.zoom.p0.y) self.general_zoom_y_1.setValue(config.zoom.p1.y) if config.zoom.p0.x == 0 and config.zoom.p1.x == 0: self.general_zoom_x_1.setValue(max_x) if config.zoom.p0.y == 0 and config.zoom.p1.y == 0: self.general_zoom_y_1.setValue(max_y) except ValueError: pass with blocked_signals(self.plot_title): self.plot_title.setText(config.title) with blocked_signals(self.general_display_order): self.general_display_order.clear() self.general_display_order.addItems(map_names) for index, map_name in enumerate(map_names): item = self.general_display_order.item(index) item.setData(Qt.UserRole, map_name) if map_name in config.map_plot_options and config.map_plot_options[map_name].title: title = config.map_plot_options[map_name].title item.setData(Qt.DisplayRole, map_name + ' (' + title + ')') with blocked_signals(self.general_show_axis): self.general_show_axis.setChecked(config.show_axis) with blocked_signals(self.general_colorbar_nmr_ticks): self.general_colorbar_nmr_ticks.setValue(config.colorbar_nmr_ticks) with blocked_signals(self.general_font_family): self.general_font_family.setCurrentText(config.font.family) with blocked_signals(self.general_font_size): self.general_font_size.setValue(config.font.size) with blocked_signals(self.general_interpolation): self.general_interpolation.setCurrentText(config.interpolation) with blocked_signals(self.general_flipud): self.general_flipud.setChecked(config.flipud) with blocked_signals(self.mask_name): if config.mask_name and config.mask_name in data_info.maps: for ind in range(self.mask_name.count()): if self.mask_name.itemText(ind) == config.mask_name: self.mask_name.setCurrentIndex(ind) break else: self.mask_name.setCurrentIndex(0) @pyqtSlot() def _reorder_maps(self): items = [self.general_display_order.item(ind) for ind in range(self.general_display_order.count())] map_names = [item.data(Qt.UserRole) for item in items] self._controller.apply_action(SetMapsToShow(map_names)) @pyqtSlot() def _update_maps_to_show(self): map_names = copy.copy(self._controller.get_config().maps_to_show) for item in [self.general_map_selection.item(ind) for ind in range(self.general_map_selection.count())]: map_name = item.data(Qt.UserRole) if item.isSelected(): if map_name not in map_names: self._insert_alphabetically(map_name, map_names) else: if map_name in map_names: map_names.remove(map_name) self._controller.apply_action(SetMapsToShow(map_names)) @pyqtSlot() def _deleselect_all_maps(self): self._controller.apply_action(SetMapsToShow([])) @pyqtSlot() def _invert_map_selection(self): self._controller.apply_action(SetMapsToShow( set(self._controller.get_data().maps.keys()).difference(set(self._controller.get_config().maps_to_show)))) @pyqtSlot() def _zoom_fit(self): data_info = self._controller.get_data() config = self._controller.get_config() def add_padding(bounding_box, max_x, max_y): bounding_box[0].x = max(bounding_box[0].x - 1, 0) bounding_box[0].y = max(bounding_box[0].y - 1, 0) bounding_box[1].y = min(bounding_box[1].y + 2, max_y) bounding_box[1].x = min(bounding_box[1].x + 2, max_x) return bounding_box if config.maps_to_show or len(data_info.maps): bounding_box = data_info.get_bounding_box(config.dimension, config.slice_index, config.volume_index, config.rotate, config.maps_to_show) max_y = data_info.get_max_y_index(config.dimension, rotate=config.rotate, map_names=config.maps_to_show) max_x = data_info.get_max_x_index(config.dimension, rotate=config.rotate, map_names=config.maps_to_show) if not config.flipud: # Since the renderer plots with a left top coordinate system, # we need to flip the y coordinates upside down by default. tmp = max_y - bounding_box[0].y bounding_box[0].y = max_y - bounding_box[1].y bounding_box[1].y = tmp bounding_box = add_padding(bounding_box, max_x, max_y) self._controller.apply_action(SetZoom(Zoom(*bounding_box))) @pyqtSlot() def _update_zoom(self): np0x, np0y = self.general_zoom_x_0.value(), self.general_zoom_y_0.value() np1x, np1y = self.general_zoom_x_1.value(), self.general_zoom_y_1.value() if np0x > np1x: np1x = np0x if np0y > np1y: np1y = np0y self._controller.apply_action(SetZoom(Zoom.from_coords(np0x, np0y, np1x, np1y))) @staticmethod def _insert_alphabetically(new_item, item_list): for ind, item in enumerate(item_list): if item > new_item: item_list.insert(ind, new_item) return item_list.append(new_item) @pyqtSlot(int) def _update_mask_name(self, index): if index == 0: self._controller.apply_action(SetGeneralMask(None)) else: self._controller.apply_action(SetGeneralMask(self.mask_name.itemText(index))) def _split_long_path_elements(self, original_path, max_single_element_length=25): """Split long path elements into smaller ones using spaces Args: original_path (str): the path you want to split max_single_element_length (int): the maximum length allowed per path component (folders and filename). Returns: str: the same path but with spaces in long path elements. The result will no longer be a valid path. """ def split(p): listing = [] def _split(el): if el: head, tail = os.path.split(el) if not tail: listing.append(head) else: _split(head) listing.append(tail) _split(p) return listing elements = list(split(original_path)) new_elements = [] for el in elements: if len(el) > max_single_element_length: item = '' for i in range(0, len(el), max_single_element_length): item += el[i:i + max_single_element_length] + ' ' item = item[:-1] new_elements.append(item) else: new_elements.append(el) return os.path.join(*new_elements)
class MapSpecificOptions(QWidget, Ui_MapSpecificOptions): def __init__(self, controller, parent=None): super().__init__(parent) self.setupUi(self) self._controller = controller current_model = self._controller.get_model() self._current_map = None self.colormap.addItems( ['-- Use global --'] + current_model.get_config().get_available_colormaps()) self.colormap.currentIndexChanged.connect(self._update_colormap) self.data_clipping_min.valueChanged.connect(self._update_clipping_min) self.data_clipping_max.valueChanged.connect(self._update_clipping_max) self.data_scale_min.valueChanged.connect(self._update_scale_min) self.data_scale_max.valueChanged.connect(self._update_scale_max) self.data_set_use_scale.stateChanged.connect(self._set_use_scale) self.use_data_scale_min.stateChanged.connect( self._set_use_data_scale_min) self.use_data_scale_max.stateChanged.connect( self._set_use_data_scale_max) self.data_set_use_clipping.stateChanged.connect(self._set_use_clipping) self.use_data_clipping_min.stateChanged.connect( self._set_use_data_clipping_min) self.use_data_clipping_max.stateChanged.connect( self._set_use_data_clipping_max) self._title_timer = TimedUpdate(self._update_map_title) self.map_title.textChanged.connect( lambda: self._title_timer.add_delayed_callback(500)) self.map_title.setFixedHeight( QFontMetrics(self.map_title.font()).lineSpacing() * 3) self._colorbar_label_timer = TimedUpdate(self._update_colorbar_label) self.data_colorbar_label.textChanged.connect( lambda: self._colorbar_label_timer.add_delayed_callback(500)) self.data_colorbar_label.setFixedHeight( QFontMetrics(self.data_colorbar_label.font()).lineSpacing() * 3) self.info_Clipping.set_collapse(True) self._auto_enable_scale_min = False self._auto_enable_scale_max = False self._auto_enable_clipping_min = False self._auto_enable_clipping_max = False self.reset() self._update_scaling_delays() def _update_scaling_delays(self): if self.use_data_scale_max.isChecked(): self.data_scale_max.set_update_delay(500) else: self.data_scale_max.set_update_delay(0) if self.use_data_scale_min.isChecked(): self.data_scale_min.set_update_delay(500) else: self.data_scale_min.set_update_delay(0) if self.use_data_clipping_max.isChecked(): self.data_clipping_max.set_update_delay(500) else: self.data_clipping_max.set_update_delay(0) if self.use_data_clipping_min.isChecked(): self.data_clipping_min.set_update_delay(500) else: self.data_clipping_min.set_update_delay(0) def reset(self): """Set all the values to their defaults""" self._current_map = None self.colormap.setCurrentText('hot') with blocked_signals(self.map_title): self.map_title.document().setPlainText('') with blocked_signals(self.data_colorbar_label): self.data_colorbar_label.document().setPlainText('') with blocked_signals(self.data_clipping_min): self.data_clipping_min.setValue(0) with blocked_signals(self.data_clipping_max): self.data_clipping_max.setValue(0) with blocked_signals(self.data_scale_min): self.data_scale_min.setValue(0) with blocked_signals(self.data_scale_max): self.data_scale_max.setValue(0) with blocked_signals(self.use_data_clipping_min): self.use_data_clipping_min.setChecked(False) with blocked_signals(self.use_data_clipping_max): self.use_data_clipping_max.setChecked(False) with blocked_signals(self.use_data_scale_min): self.use_data_scale_min.setChecked(False) with blocked_signals(self.use_data_scale_max): self.use_data_scale_max.setChecked(False) self.info_file_location.setText('-') self.info_maximum.setText('-') self.info_minimum.setText('-') self.info_shape.setText('-') def _get_mask(self, data_info, map_name): current_model = self._controller.get_model() plot_config = current_model.get_config() def get_map_attr(): if map_name in plot_config.map_plot_options: value = getattr(plot_config.map_plot_options[map_name], 'mask_name') if value is not None: return value return plot_config.mask_name mask_name = get_map_attr() if mask_name is not None: return data_info.get_map_data(mask_name) return None def use(self, map_name): """Load the settings of the given map""" self._current_map = map_name current_model = self._controller.get_model() try: map_info = current_model.get_config().map_plot_options[map_name] except KeyError: map_info = SingleMapConfig() data_info = current_model.get_data() vmin, vmax = data_info.get_single_map_info(map_name).min_max( mask=self._get_mask(data_info, map_name)) with blocked_signals(self.map_title): self.map_title.document().setPlainText( map_info.title if map_info.title else '') with blocked_signals(self.data_colorbar_label): self.data_colorbar_label.document().setPlainText( map_info.colorbar_label if map_info.colorbar_label else '') with blocked_signals(self.colormap): if map_info.colormap is None: self.colormap.setCurrentIndex(0) else: self.colormap.setCurrentText(map_info.colormap) with blocked_signals(self.data_clipping_min): self.data_clipping_min.setValue(map_info.clipping.vmin) with blocked_signals(self.data_clipping_max): self.data_clipping_max.setValue(map_info.clipping.vmax) with blocked_signals(self.data_scale_min): self.data_scale_min.setValue(map_info.scale.vmin) with blocked_signals(self.data_scale_max): self.data_scale_max.setValue(map_info.scale.vmax) with blocked_signals(self.data_set_use_scale): self.data_set_use_scale.setChecked(map_info.scale.use_min or map_info.scale.use_max) with blocked_signals(self.data_set_use_clipping): self.data_set_use_clipping.setChecked(map_info.clipping.use_min or map_info.clipping.use_max) with blocked_signals(self.use_data_clipping_min): self.use_data_clipping_min.setChecked(map_info.clipping.use_min) with blocked_signals(self.use_data_clipping_max): self.use_data_clipping_max.setChecked(map_info.clipping.use_max) with blocked_signals(self.use_data_scale_min): self.use_data_scale_min.setChecked(map_info.scale.use_min) with blocked_signals(self.use_data_scale_max): self.use_data_scale_max.setChecked(map_info.scale.use_max) map_filename = data_info.get_file_path(map_name) if map_filename: self.info_file_location.setText( split_long_path_elements(map_filename, 25)) self.info_minimum.setText(str(vmin)) self.info_maximum.setText(str(vmax)) self.info_has_nan.setText('Yes' if data_info.get_single_map_info( map_name).has_nan() else 'No') self.info_shape.setText( str(data_info.get_single_map_info(map_name).shape)) def _get_current_map_config(self): current_model = self._controller.get_model() current_config = current_model.get_config() current_map_config = current_config.map_plot_options.get( self._current_map, SingleMapConfig()) return current_map_config @pyqtSlot() def _update_map_title(self): string = self.map_title.toPlainText() if self._current_map: if string == '': string = None self._controller.apply_action( SetMapTitle(self._current_map, string)) @pyqtSlot() def _update_colorbar_label(self): string = self.data_colorbar_label.toPlainText() if self._current_map: if string == '': string = None self._controller.apply_action( SetMapColorbarLabel(self._current_map, string)) @pyqtSlot(int) def _update_colormap(self, index): if self._current_map: if index == 0: self._controller.apply_action( SetMapColormap(self._current_map, None)) else: self._controller.apply_action( SetMapColormap(self._current_map, self.colormap.itemText(index))) @pyqtSlot(float) def _update_scale_min(self, value): if self._current_map: current_scale = self._get_current_map_config().scale kwargs = dict(vmin=value) if current_scale.use_max and current_scale.use_min and value > current_scale.vmax: kwargs.update(dict(use_min=False)) self._auto_enable_scale_min = True elif self._auto_enable_scale_min and value < current_scale.vmax: kwargs.update(dict(use_min=True)) self._auto_enable_scale_min = False new_scale = current_scale.get_updated(**kwargs) self._controller.apply_action( SetMapScale(self._current_map, new_scale)) @pyqtSlot(float) def _update_scale_max(self, value): if self._current_map: current_scale = self._get_current_map_config().scale kwargs = dict(vmax=value) if current_scale.use_max and current_scale.use_min and value < current_scale.vmin: kwargs.update(dict(use_max=False)) self._auto_enable_scale_max = True elif self._auto_enable_scale_max and value > current_scale.vmin: kwargs.update(dict(use_max=True)) self._auto_enable_scale_max = False new_scale = current_scale.get_updated(**kwargs) self._controller.apply_action( SetMapScale(self._current_map, new_scale)) @pyqtSlot(int) def _set_use_scale(self, check_state): use_scale = check_state == 2 if self._current_map: current_scale = self._get_current_map_config().scale if use_scale and current_scale.vmax < current_scale.vmin: new_scale = current_scale.get_updated(use_min=use_scale, use_max=use_scale, vmax=current_scale.vmin) else: new_scale = current_scale.get_updated(use_min=use_scale, use_max=use_scale) self._controller.apply_action( SetMapScale(self._current_map, new_scale)) self._update_scaling_delays() @pyqtSlot(int) def _set_use_data_scale_min(self, check_state): use_scale = check_state == 2 if self._current_map: if use_scale and self._get_current_map_config().scale.use_max: self._set_use_scale(2) else: new_scale = self._get_current_map_config().scale.get_updated( use_min=use_scale) self._controller.apply_action( SetMapScale(self._current_map, new_scale)) @pyqtSlot(int) def _set_use_data_scale_max(self, check_state): use_scale = check_state == 2 if self._current_map: if use_scale and self._get_current_map_config().scale.use_min: self._set_use_scale(2) else: new_scale = self._get_current_map_config().scale.get_updated( use_max=use_scale) self._controller.apply_action( SetMapScale(self._current_map, new_scale)) @pyqtSlot(float) def _update_clipping_min(self, value): if self._current_map: current_clipping = self._get_current_map_config().clipping kwargs = dict(vmin=value) if current_clipping.use_max and current_clipping.use_min and value > current_clipping.vmax: kwargs.update(dict(use_min=False)) self._auto_enable_scale_min = True elif self._auto_enable_scale_min and value < current_clipping.vmax: kwargs.update(dict(use_min=True)) self._auto_enable_scale_min = False new_clipping = current_clipping.get_updated(**kwargs) self._controller.apply_action( SetMapClipping(self._current_map, new_clipping)) @pyqtSlot(float) def _update_clipping_max(self, value): if self._current_map: current_clipping = self._get_current_map_config().clipping kwargs = dict(vmax=value) if current_clipping.use_max and current_clipping.use_min and value < current_clipping.vmin: kwargs.update(dict(use_max=False)) self._auto_enable_scale_max = True elif self._auto_enable_scale_max and value > current_clipping.vmin: kwargs.update(dict(use_max=True)) self._auto_enable_scale_max = False new_clipping = current_clipping.get_updated(**kwargs) self._controller.apply_action( SetMapClipping(self._current_map, new_clipping)) @pyqtSlot(int) def _set_use_clipping(self, check_state): use_clipping = check_state == 2 if self._current_map: current_clipping = self._get_current_map_config().clipping if use_clipping and current_clipping.vmax < current_clipping.vmin: new_clipping = current_clipping.get_updated( use_min=use_clipping, use_max=use_clipping, vmax=current_clipping.vmin) else: new_clipping = current_clipping.get_updated( use_min=use_clipping, use_max=use_clipping) self._controller.apply_action( SetMapClipping(self._current_map, new_clipping)) self._update_scaling_delays() @pyqtSlot(int) def _set_use_data_clipping_min(self, check_state): use_clipping = check_state == 2 if self._current_map: if use_clipping and self._get_current_map_config( ).clipping.use_max: self._set_use_clipping(2) else: new_clipping = self._get_current_map_config( ).clipping.get_updated(use_min=use_clipping) self._controller.apply_action( SetMapClipping(self._current_map, new_clipping)) @pyqtSlot(int) def _set_use_data_clipping_max(self, check_state): use_clipping = check_state == 2 if self._current_map: if use_clipping and self._get_current_map_config( ).clipping.use_min: self._set_use_clipping(2) else: new_clipping = self._get_current_map_config( ).clipping.get_updated(use_max=use_clipping) self._controller.apply_action( SetMapClipping(self._current_map, new_clipping))
class TabGeneral(QWidget, Ui_TabGeneral): def __init__(self, controller, parent=None): super().__init__(parent) self.setupUi(self) self._controller = controller self._controller.model_updated.connect(self.update_model) self._previous_model = None current_model = self._controller.get_model() self.general_display_order.setDragDropMode( QAbstractItemView.InternalMove) self.general_display_order.setSelectionMode( QAbstractItemView.SingleSelection) self.general_colormap.addItems( current_model.get_config().get_available_colormaps()) self.general_rotate.addItems(['0', '90', '180', '270']) self.general_rotate.setCurrentText( str(current_model.get_config().rotate)) self.general_DisplayOrder.set_collapse(True) self.general_Miscellaneous.set_collapse(True) self.general_Zoom.set_collapse(True) self.general_Font.set_collapse(True) self.general_Colorbar.set_collapse(True) self.general_dimension.valueChanged.connect( lambda v: self._controller.apply_action(SetDimension(v))) self.general_slice_index.valueChanged.connect( lambda v: self._controller.apply_action(SetSliceIndex(v))) self.general_volume_index.valueChanged.connect( lambda v: self._controller.apply_action(SetVolumeIndex(v))) self.general_colormap.currentIndexChanged.connect( lambda i: self._controller.apply_action( SetColormap(self.general_colormap.itemText(i)))) self.general_rotate.currentIndexChanged.connect( lambda i: self._controller.apply_action( SetRotate(int(self.general_rotate.itemText(i))))) self._map_selection_timer = TimedUpdate(self._update_maps_to_show) self.general_map_selection.itemSelectionChanged.connect( lambda: self._map_selection_timer.add_delayed_callback(500)) self.general_map_selection.setContextMenuPolicy(Qt.CustomContextMenu) self.general_map_selection.customContextMenuRequested.connect( self.select_maps_context_menu) self.general_deselect_all_maps.clicked.connect( self._deleselect_all_maps) self.general_invert_map_selection.clicked.connect( self._invert_map_selection) self.general_zoom_x_0.valueChanged.connect(self._update_zoom) self.general_zoom_x_1.valueChanged.connect(self._update_zoom) self.general_zoom_y_0.valueChanged.connect(self._update_zoom) self.general_zoom_y_1.valueChanged.connect(self._update_zoom) self.plot_title.textEdited.connect( lambda txt: self._controller.apply_action(SetPlotTitle(txt))) self.general_zoom_reset.clicked.connect( lambda: self._controller.apply_action(SetZoom(Zoom.no_zoom()))) self.general_zoom_fit.clicked.connect(self._zoom_fit) self.general_display_order.items_reordered.connect(self._reorder_maps) self.general_show_axis.clicked.connect( lambda: self._controller.apply_action( SetShowAxis(self.general_show_axis.isChecked()))) self.general_font_family.addItems(Font.font_names()) self.general_font_family.currentTextChanged.connect( lambda v: self._controller.apply_action( SetFont(self._controller.get_model().get_config().font. get_updated(family=v)))) self.general_font_size.valueChanged.connect( lambda: self._controller.apply_action( SetFont(self._controller.get_model().get_config().font. get_updated(size=self.general_font_size.value())))) self.general_interpolation.addItems( current_model.get_config().get_available_interpolations()) self.general_interpolation.currentTextChanged.connect( lambda v: self._controller.apply_action(SetInterpolation(v))) self.general_flipud.clicked.connect( lambda: self._controller.apply_action( SetFlipud(self.general_flipud.isChecked()))) self.general_show_plot_titles.clicked.connect( lambda: self._controller.apply_action( SetShowPlotTitles(self.general_show_plot_titles.isChecked()))) self.mask_name.currentIndexChanged.connect(self._update_mask_name) self.general_colorbar_nmr_ticks.valueChanged.connect( lambda v: self._controller.apply_action(SetColorBarNmrTicks(v))) self.general_show_colorbar.clicked.connect( lambda: self._controller.apply_action( SetShowPlotColorbars(self.general_show_colorbar.isChecked()))) self.general_colorbar_location.currentTextChanged.connect( lambda v: self._controller.apply_action( SetColorbarLocation(v.lower()))) @pyqtSlot(DataConfigModel) def update_model(self, model): data = model.get_data() config = model.get_config() if not self._previous_model or model.get_data( ) != self._previous_model.get_data(): self._previous_model = model self._update_data(data) self._update_config(data, config) def select_maps_context_menu(self, position): global_position = self.general_map_selection.mapToGlobal(position) def get_header_action(parent, map_name): label = QLabel(map_name) font = label.font() font.setBold(True) label.setFont(font) label.setStyleSheet('color: black; margin:5px; margin-left: 15px;') action = QWidgetAction(parent) action.setDisabled(True) action.setDefaultWidget(label) return action if self.general_map_selection.count(): row = self.general_map_selection.indexAt(position) if row: element = self.general_map_selection.item(row.row()) if element: map_name = element.data(Qt.UserRole) file_path = self._controller.get_model().get_data( ).get_file_path(map_name) menu = QMenu() menu.addAction(get_header_action(menu, map_name)) menu.addSeparator() show_in_folder = menu.addAction( '&Show in folder', lambda: QDesktopServices.openUrl( QUrl.fromLocalFile(os.path.dirname(file_path)))) if file_path is None: show_in_folder.setEnabled(False) menu.addAction( 'Use as &mask', lambda: self._controller.apply_action( SetGeneralMask(map_name))) menu.addAction( 'R&emove', lambda: self._controller.apply_action( NewDataAction(self._controller.get_model( ).get_data().get_updated(removals=[map_name])))) menu.exec(global_position) @pyqtSlot() def _reorder_maps(self): items = [ self.general_display_order.item(ind) for ind in range(self.general_display_order.count()) ] map_names = [item.data(Qt.UserRole) for item in items] self._controller.apply_action(SetMapsToShow(map_names)) @pyqtSlot() def _update_maps_to_show(self): current_model = self._controller.get_model() map_names = copy.copy(current_model.get_config().maps_to_show) for item in [ self.general_map_selection.item(ind) for ind in range(self.general_map_selection.count()) ]: map_name = item.data(Qt.UserRole) if item.isSelected(): if map_name not in map_names: self._insert_alphabetically(map_name, map_names) else: if map_name in map_names: map_names.remove(map_name) self._controller.apply_action(SetMapsToShow(map_names)) @pyqtSlot() def _deleselect_all_maps(self): self._controller.apply_action(SetMapsToShow([])) @pyqtSlot() def _invert_map_selection(self): current_model = self._controller.get_model() self._controller.apply_action( SetMapsToShow( list( set(current_model.get_data().get_map_names()).difference( set(current_model.get_config().maps_to_show))))) @pyqtSlot() def _zoom_fit(self): current_model = self._controller.get_model() data_info = current_model.get_data() config = current_model.get_config() def add_padding(bounding_box, max_x, max_y): bounding_box[0].x = max(bounding_box[0].x - 1, 0) bounding_box[0].y = max(bounding_box[0].y - 1, 0) bounding_box[1].y = min(bounding_box[1].y + 2, max_y) bounding_box[1].x = min(bounding_box[1].x + 2, max_x) return bounding_box if config.maps_to_show or len(data_info.get_map_names()): bounding_box = data_info.get_bounding_box(config.dimension, config.slice_index, config.volume_index, config.rotate, config.maps_to_show) max_y = data_info.get_max_y_index(config.dimension, rotate=config.rotate, map_names=config.maps_to_show) max_x = data_info.get_max_x_index(config.dimension, rotate=config.rotate, map_names=config.maps_to_show) if not config.flipud: # Since the renderer plots with a left top coordinate system, # we need to flip the y coordinates upside down by default. tmp = max_y - bounding_box[0].y bounding_box[0].y = max_y - bounding_box[1].y bounding_box[1].y = tmp bounding_box = add_padding(bounding_box, max_x, max_y) self._controller.apply_action(SetZoom(Zoom(*bounding_box))) @pyqtSlot() def _update_zoom(self): np0x, np0y = self.general_zoom_x_0.value( ), self.general_zoom_y_0.value() np1x, np1y = self.general_zoom_x_1.value( ), self.general_zoom_y_1.value() if np0x > np1x: np1x = np0x if np0y > np1y: np1y = np0y self._controller.apply_action( SetZoom(Zoom.from_coords(np0x, np0y, np1x, np1y))) @staticmethod def _insert_alphabetically(new_item, item_list): for ind, item in enumerate(item_list): if item > new_item: item_list.insert(ind, new_item) return item_list.append(new_item) @pyqtSlot(int) def _update_mask_name(self, index): if index == 0: self._controller.apply_action(SetGeneralMask(None)) else: self._controller.apply_action( SetGeneralMask(self.mask_name.itemText(index))) def _update_data(self, data_info): sorted_keys = list(sorted(data_info.get_map_names())) if len(data_info.get_map_names()): self.general_info_nmr_maps.setText( str(len(data_info.get_map_names()))) else: self.general_info_nmr_maps.setText('0') with blocked_signals(self.general_map_selection): self.general_map_selection.clear() self.general_map_selection.addItems(sorted_keys) for index, map_name in enumerate(sorted_keys): item = self.general_map_selection.item(index) item.setData(Qt.UserRole, map_name) with blocked_signals(self.mask_name): self.mask_name.clear() self.mask_name.insertItem(0, '-- None --') self.mask_name.insertItems(1, sorted_keys) def _update_config(self, data, config): map_names = config.maps_to_show with blocked_signals(self.general_dimension): try: max_dimension = data.get_max_dimension(map_names) self.general_dimension.setMaximum(max_dimension) self.maximumDimension.setText(str(max_dimension)) except ValueError: self.general_dimension.setMaximum(0) self.maximumDimension.setText(str(0)) self.general_dimension.setValue(config.dimension) with blocked_signals(self.general_slice_index): try: max_slice = data.get_max_slice_index(config.dimension, map_names) self.general_slice_index.setMaximum(max_slice) self.maximumIndex.setText(str(max_slice)) except ValueError: self.general_slice_index.setMaximum(0) self.maximumIndex.setText(str(0)) self.general_slice_index.setValue(config.slice_index) with blocked_signals(self.general_volume_index): try: max_volume = data.get_max_volume_index(map_names) self.general_volume_index.setMaximum(max_volume) self.maximumVolume.setText(str(max_volume)) except ValueError: self.general_volume_index.setMaximum(0) self.maximumVolume.setText(str(0)) self.general_volume_index.setValue(config.volume_index) with blocked_signals(self.general_colormap): self.general_colormap.setCurrentText(config.colormap) with blocked_signals(self.general_rotate): self.general_rotate.setCurrentText(str(config.rotate)) if self.general_map_selection.count(): for map_name, map_config in config.map_plot_options.items(): if map_config.title: index = list(sorted(data.get_map_names())).index(map_name) item = self.general_map_selection.item(index) item.setData(Qt.DisplayRole, map_name + ' (' + map_config.title + ')') self.general_map_selection.blockSignals(True) for index, map_name in enumerate(list(sorted( data.get_map_names()))): item = self.general_map_selection.item(index) if item: item.setSelected(map_name in map_names) self.general_map_selection.blockSignals(False) try: max_x = data.get_max_x_index(config.dimension, config.rotate, map_names) max_y = data.get_max_y_index(config.dimension, config.rotate, map_names) with blocked_signals(self.general_zoom_x_0, self.general_zoom_x_1, self.general_zoom_y_0, self.general_zoom_y_1): self.general_zoom_x_0.setMaximum(max_x) self.general_zoom_x_0.setValue(config.zoom.p0.x) self.general_zoom_x_1.setMaximum(max_x) self.general_zoom_x_1.setMinimum(config.zoom.p0.x) self.general_zoom_x_1.setValue(config.zoom.p1.x) self.general_zoom_y_0.setMaximum(max_y) self.general_zoom_y_0.setValue(config.zoom.p0.y) self.general_zoom_y_1.setMaximum(max_y) self.general_zoom_y_1.setMinimum(config.zoom.p0.y) self.general_zoom_y_1.setValue(config.zoom.p1.y) if config.zoom.p0.x == 0 and config.zoom.p1.x == 0: self.general_zoom_x_1.setValue(max_x) if config.zoom.p0.y == 0 and config.zoom.p1.y == 0: self.general_zoom_y_1.setValue(max_y) except ValueError: pass with blocked_signals(self.plot_title): self.plot_title.setText(config.title) with blocked_signals(self.general_display_order): self.general_display_order.clear() self.general_display_order.addItems(map_names) for index, map_name in enumerate(map_names): item = self.general_display_order.item(index) item.setData(Qt.UserRole, map_name) if map_name in config.map_plot_options and config.map_plot_options[ map_name].title: title = config.map_plot_options[map_name].title item.setData(Qt.DisplayRole, map_name + ' (' + title + ')') with blocked_signals(self.general_show_axis): self.general_show_axis.setChecked(config.show_axis) with blocked_signals(self.general_colorbar_nmr_ticks): self.general_colorbar_nmr_ticks.setValue( config.colorbar_settings.get_preferred('nmr_ticks')) with blocked_signals(self.general_show_colorbar): self.general_show_colorbar.setChecked( config.colorbar_settings.get_preferred('visible')) with blocked_signals(self.general_colorbar_location): self.general_colorbar_location.setCurrentText( config.colorbar_settings.get_preferred('location').title()) with blocked_signals(self.general_show_plot_titles): self.general_show_plot_titles.setChecked(config.show_titles) with blocked_signals(self.general_font_family): self.general_font_family.setCurrentText(config.font.family) with blocked_signals(self.general_font_size): self.general_font_size.setValue(config.font.size) with blocked_signals(self.general_interpolation): self.general_interpolation.setCurrentText(config.interpolation) with blocked_signals(self.general_flipud): self.general_flipud.setChecked(config.flipud) with blocked_signals(self.mask_name): if config.mask_name and config.mask_name in data.get_map_names(): for ind in range(self.mask_name.count()): if self.mask_name.itemText(ind) == config.mask_name: self.mask_name.setCurrentIndex(ind) break else: self.mask_name.setCurrentIndex(0)
class MapSpecificOptions(QWidget, Ui_MapSpecificOptions): def __init__(self, controller, parent=None): super(MapSpecificOptions, self).__init__(parent) self.setupUi(self) self._controller = controller self._current_map = None self.colormap.addItems( ['-- Use global --'] + self._controller.get_config().get_available_colormaps()) self.colormap.currentIndexChanged.connect(self._update_colormap) self.data_clipping_min.valueChanged.connect(self._update_clipping_min) self.data_clipping_max.valueChanged.connect(self._update_clipping_max) self.data_scale_min.valueChanged.connect(self._update_scale_min) self.data_scale_max.valueChanged.connect(self._update_scale_max) self.data_set_use_scale.stateChanged.connect(self._set_use_scale) self.use_data_scale_min.stateChanged.connect( self._set_use_data_scale_min) self.use_data_scale_max.stateChanged.connect( self._set_use_data_scale_max) self.data_set_use_clipping.stateChanged.connect(self._set_use_clipping) self.use_data_clipping_min.stateChanged.connect( self._set_use_data_clipping_min) self.use_data_clipping_max.stateChanged.connect( self._set_use_data_clipping_max) self._title_timer = TimedUpdate(self._update_map_title) self.map_title.textChanged.connect( lambda v: self._title_timer.add_delayed_callback(500, v)) self._colorbar_label_timer = TimedUpdate(self._update_colorbar_label) self.data_colorbar_label.textChanged.connect( lambda v: self._colorbar_label_timer.add_delayed_callback(500, v)) self.info_Clipping.set_collapse(True) def reset(self): """Set all the values to their defaults""" self._current_map = None self.colormap.setCurrentText('hot') with blocked_signals(self.map_title): self.map_title.setText('') with blocked_signals(self.data_colorbar_label): self.data_colorbar_label.setText('') with blocked_signals(self.data_clipping_min): self.data_clipping_min.setValue(0) with blocked_signals(self.data_clipping_max): self.data_clipping_max.setValue(0) with blocked_signals(self.data_scale_min): self.data_scale_min.setValue(0) with blocked_signals(self.data_scale_max): self.data_scale_max.setValue(0) with blocked_signals(self.use_data_clipping_min): self.use_data_clipping_min.setChecked(False) with blocked_signals(self.use_data_clipping_max): self.use_data_clipping_max.setChecked(False) with blocked_signals(self.use_data_scale_min): self.use_data_scale_min.setChecked(False) with blocked_signals(self.use_data_scale_max): self.use_data_scale_max.setChecked(False) self.info_file_location.setText('-') self.info_maximum.setText('-') self.info_minimum.setText('-') self.info_shape.setText('-') def use(self, map_name): """Load the settings of the given map""" self._current_map = map_name try: map_info = self._controller.get_config().map_plot_options[map_name] except KeyError: map_info = SingleMapConfig() data_info = self._controller.get_data() vmin = data_info.maps[map_name].min() vmax = data_info.maps[map_name].max() with blocked_signals(self.map_title): self.map_title.setText(map_info.title if map_info.title else '') with blocked_signals(self.data_colorbar_label): self.data_colorbar_label.setText( map_info.colorbar_label if map_info.colorbar_label else '') with blocked_signals(self.colormap): if map_info.colormap is None: self.colormap.setCurrentIndex(0) else: self.colormap.setCurrentText(map_info.colormap) with blocked_signals(self.data_clipping_min): self.data_clipping_min.setValue(map_info.clipping.vmin) with blocked_signals(self.data_clipping_max): self.data_clipping_max.setValue(map_info.clipping.vmax) with blocked_signals(self.data_scale_min): self.data_scale_min.setValue(map_info.scale.vmin) with blocked_signals(self.data_scale_max): self.data_scale_max.setValue(map_info.scale.vmax) with blocked_signals(self.data_set_use_scale): self.data_set_use_scale.setChecked(map_info.scale.use_min or map_info.scale.use_max) with blocked_signals(self.data_set_use_clipping): self.data_set_use_clipping.setChecked(map_info.clipping.use_min or map_info.clipping.use_max) with blocked_signals(self.use_data_clipping_min): self.use_data_clipping_min.setChecked(map_info.clipping.use_min) with blocked_signals(self.use_data_clipping_max): self.use_data_clipping_max.setChecked(map_info.clipping.use_max) with blocked_signals(self.use_data_scale_min): self.use_data_scale_min.setChecked(map_info.scale.use_min) with blocked_signals(self.use_data_scale_max): self.use_data_scale_max.setChecked(map_info.scale.use_max) map_filename = data_info.get_file_name(map_name) if map_filename: self.info_file_location.setText(map_filename) self.info_maximum.setText(str(vmax)) self.info_minimum.setText(str(vmin)) self.info_shape.setText(str(data_info.maps[map_name].shape)) def _get_current_map_config(self): current_config = self._controller.get_config() current_map_config = current_config.map_plot_options.get( self._current_map, SingleMapConfig()) return current_map_config @pyqtSlot(str) def _update_map_title(self, string): if self._current_map: if string == '': string = None self._controller.apply_action( SetMapTitle(self._current_map, string)) @pyqtSlot(str) def _update_colorbar_label(self, string): if self._current_map: if string == '': string = None self._controller.apply_action( SetMapColorbarLabel(self._current_map, string)) @pyqtSlot(int) def _update_colormap(self, index): if self._current_map: if index == 0: self._controller.apply_action( SetMapColormap(self._current_map, None)) else: self._controller.apply_action( SetMapColormap(self._current_map, self.colormap.itemText(index))) @pyqtSlot(float) def _update_scale_min(self, value): if self._current_map: current_scale = self._get_current_map_config().scale if current_scale.use_min and current_scale.use_max and value > current_scale.vmax: new_scale = current_scale.get_updated(vmin=value, vmax=value) else: new_scale = current_scale.get_updated(vmin=value) self._controller.apply_action( SetMapScale(self._current_map, new_scale)) @pyqtSlot(float) def _update_scale_max(self, value): if self._current_map: current_scale = self._get_current_map_config().scale if current_scale.use_min and current_scale.use_max and value < current_scale.vmin: new_scale = current_scale.get_updated(vmin=value, vmax=value) else: new_scale = current_scale.get_updated(vmax=value) self._controller.apply_action( SetMapScale(self._current_map, new_scale)) @pyqtSlot(int) def _set_use_scale(self, use_scale): if self._current_map: current_scale = self._get_current_map_config().scale if use_scale and current_scale.vmax < current_scale.vmin: new_scale = current_scale.get_updated(use_min=use_scale, use_max=use_scale, vmax=current_scale.vmin) else: new_scale = current_scale.get_updated(use_min=use_scale, use_max=use_scale) self._controller.apply_action( SetMapScale(self._current_map, new_scale)) @pyqtSlot(int) def _set_use_data_scale_min(self, use_scale): if self._current_map: if use_scale and self._get_current_map_config().scale.use_max: self._set_use_scale(True) else: new_scale = self._get_current_map_config().scale.get_updated( use_min=use_scale) self._controller.apply_action( SetMapScale(self._current_map, new_scale)) @pyqtSlot(int) def _set_use_data_scale_max(self, use_scale): if self._current_map: if use_scale and self._get_current_map_config().scale.use_min: self._set_use_scale(True) else: new_scale = self._get_current_map_config().scale.get_updated( use_max=use_scale) self._controller.apply_action( SetMapScale(self._current_map, new_scale)) @pyqtSlot(float) def _update_clipping_min(self, value): if self._current_map: current_clipping = self._get_current_map_config().clipping if current_clipping.use_min and current_clipping.use_max and value > current_clipping.vmax: new_clipping = current_clipping.get_updated(vmin=value, vmax=value) else: new_clipping = current_clipping.get_updated(vmin=value) self._controller.apply_action( SetMapClipping(self._current_map, new_clipping)) @pyqtSlot(float) def _update_clipping_max(self, value): if self._current_map: current_clipping = self._get_current_map_config().clipping if current_clipping.use_min and current_clipping.use_max and value < current_clipping.vmin: new_clipping = current_clipping.get_updated(vmin=value, vmax=value) else: new_clipping = current_clipping.get_updated(vmax=value) self._controller.apply_action( SetMapClipping(self._current_map, new_clipping)) @pyqtSlot(int) def _set_use_clipping(self, use_clipping): if self._current_map: current_clipping = self._get_current_map_config().clipping if use_clipping and current_clipping.vmax < current_clipping.vmin: new_clipping = current_clipping.get_updated( use_min=use_clipping, use_max=use_clipping, vmax=current_clipping.vmin) else: new_clipping = current_clipping.get_updated( use_min=use_clipping, use_max=use_clipping) self._controller.apply_action( SetMapClipping(self._current_map, new_clipping)) @pyqtSlot(int) def _set_use_data_clipping_min(self, use_clipping): if self._current_map: if use_clipping and self._get_current_map_config( ).clipping.use_max: self._set_use_clipping(True) else: new_clipping = self._get_current_map_config( ).clipping.get_updated(use_min=use_clipping) self._controller.apply_action( SetMapClipping(self._current_map, new_clipping)) @pyqtSlot(int) def _set_use_data_clipping_max(self, use_clipping): if self._current_map: if use_clipping and self._get_current_map_config( ).clipping.use_min: self._set_use_clipping(True) else: new_clipping = self._get_current_map_config( ).clipping.get_updated(use_max=use_clipping) self._controller.apply_action( SetMapClipping(self._current_map, new_clipping))