class QuickLabelOptions(QuickOptions): ok_clicked = Signal() cancel_clicked = Signal() def __init__(self, label): super(QuickLabelOptions, self).__init__() self.setWindowTitle("Edit " + label.get_text()) self.line_edit = QtWidgets.QLineEdit() self.line_edit.setText(label.get_text()) self.layout.addWidget(self.line_edit) self.layout.addLayout(self.button_row) self.line_edit.show() @property def label(self): return self.line_edit.text()
class QuickLineOptions(QuickOptions): ok_clicked = Signal() cancel_clicked = Signal() def __init__(self, line_options): super(QuickLineOptions, self).__init__() self.setWindowTitle("Edit line") self.line_widget = LegendAndLineOptionsSetter(line_options, None) self.layout.addWidget(self.line_widget) self.layout.addLayout(self.button_row) self.line_widget.show() @property def error_bar(self): return self.line_widget.error_bar @property def color(self): return self.line_widget.color @property def style(self): return self.line_widget.style @property def marker(self): return self.line_widget.marker @property def width(self): return self.line_widget.width @property def label(self): return self.line_widget.label @property def shown(self): return self.line_widget.shown @property def legend(self): return self.line_widget.legend
class SlicePlotOptions(PlotOptionsDialog): cRangeEdited = Signal() cLogEdited = Signal() def __init__(self): super(SlicePlotOptions, self).__init__() self.chkXLog.hide() self.chkYLog.hide() self.cut_options.hide() self.setMaximumWidth(350) self.lneCMin.editingFinished.connect(self.cRangeEdited) self.lneCMax.editingFinished.connect(self.cRangeEdited) self.chkLogarithmic.stateChanged.connect(self.cLogEdited) @property def colorbar_range(self): try: cmin = float(str(self.lneCMin.text())) cmax = float(str(self.lneCMax.text())) except ValueError: return None, None return cmin, cmax @colorbar_range.setter def colorbar_range(self, c_range): try: cmin, cmax = c_range except ValueError: raise ValueError("pass an iterable with two items") self.lneCMin.setText(str(cmin)) self.lneCMax.setText(str(cmax)) @property def colorbar_log(self): return self.chkLogarithmic.isChecked() @colorbar_log.setter def colorbar_log(self, value): self.chkLogarithmic.setChecked(value)
class CutWidget(CutView, QWidget): error_occurred = Signal('QString') busy = Signal(bool) def __init__(self, parent=None, *args, **kwargs): QWidget.__init__(self, parent, *args, **kwargs) load_ui(__file__, 'cut.ui', self) self._command_lookup = { self.btnCutPlot: Command.Plot, self.btnCutPlotOver: Command.PlotOver, self.btnCutSaveToWorkspace: Command.SaveToWorkspace } for button in self._command_lookup.keys(): button.clicked.connect(self._btn_clicked) self._presenter = CutWidgetPresenter(self) self.cmbCutAxis.currentIndexChanged.connect(self.axis_changed) self._minimumStep = None self.lneCutStep.editingFinished.connect(self._step_edited) self.enable_integration_axis(False) self.set_validators() def _btn_clicked(self): sender = self.sender() command = self._command_lookup[sender] if self._step_edited(): self._presenter.notify(command) def _step_edited(self): """Checks that user inputted step size is not too small.""" if self._minimumStep: try: value = float(self.lneCutStep.text()) except ValueError: value = 0.0 self.display_error('Invalid cut step parameter. Using default.') if value == 0.0: self.lneCutStep.setText('%.5f' % (self._minimumStep)) self.display_error('Setting step size to default.') elif value < (self._minimumStep / 100.): self.display_error('Step size too small!') return False return True def display_error(self, error_string): self.error_occurred.emit(error_string) def axis_changed(self, _changed_index): self._presenter.notify(Command.AxisChanged) def enable_integration_axis(self, enabled): if enabled: self.integrationStack.setCurrentIndex(1) self.label_250.show() else: self.integrationStack.setCurrentIndex(0) self.label_250.hide() def integration_axis_shown(self): return self.integration_axis.current_index == 1 def get_presenter(self): return self._presenter def get_cut_axis(self): return str(self.cmbCutAxis.currentText()) def get_cut_axis_start(self): return str(self.lneCutStart.text()) def get_cut_axis_step(self): return str(self.lneCutStep.text()) def get_cut_axis_end(self): return str(self.lneCutEnd.text()) def get_integration_axis(self): if self.integration_axis_shown: return str(self.cmbIntegrationAxis.currentText()) else: return None def get_integration_start(self): return str(self.lneCutIntegrationStart.text()) def get_integration_end(self): return str(self.lneCutIntegrationEnd.text()) def get_integration_width(self): return str(self.lneCutIntegrationWidth.text()) def get_intensity_start(self): return str(self.lneEditCutIntensityStart.text()) def get_intensity_end(self): return str(self.lneCutIntensityEnd.text()) def get_intensity_is_norm_to_one(self): return self.rdoCutNormToOne.isChecked() def get_smoothing(self): return str(self.lneCutSmoothing.text()) def set_cut_axis(self, axis_name): index = [ind for ind in range(self.cmbCutAxis.count()) if str(self.cmbCutAxis.itemText(ind)) == axis_name] if index: self.cmbCutAxis.blockSignals(True) self.cmbCutAxis.setCurrentIndex(index[0]) self.cmbCutAxis.blockSignals(False) def set_minimum_step(self, value): self._minimumStep = value def get_minimum_step(self): return self._minimumStep def populate_cut_axis_options(self, options): self.cmbCutAxis.blockSignals(True) self.cmbCutAxis.clear() for option in options: self.cmbCutAxis.addItem(option) self.cmbCutAxis.blockSignals(False) def populate_integration_axis_options(self, options): self.cmbIntegrationAxis.blockSignals(True) self.cmbIntegrationAxis.clear() for option in options: self.cmbIntegrationAxis.addItem(option) self.cmbIntegrationAxis.setEnabled(len(options) > 1) self.cmbIntegrationAxis.blockSignals(False) def populate_cut_params(self, cut_start=None, cut_end=None, cut_step=None): if cut_start is not None: self.lneCutStart.setText(cut_start) if cut_end is not None: self.lneCutEnd.setText(cut_end) if cut_step is not None: self.lneCutStep.setText(cut_step) def populate_integration_params(self, integration_start=None, integration_end=None): if integration_start is not None: self.lneCutIntegrationStart.setText(integration_start) if integration_end is not None: self.lneCutIntegrationEnd.setText(integration_end) def clear_input_fields(self, **kwargs): if 'keep_axes' not in kwargs or not kwargs['keep_axes']: self.populate_cut_axis_options([]) self.populate_cut_params("", "", "") self.populate_integration_params("", "") self.lneCutIntegrationWidth.setText("") self.lneCutSmoothing.setText("") self.rdoCutNormToOne.setChecked(0) def is_fields_cleared(self): current_fields = self.get_input_fields() cleared_fields = {'cut_parameters': ['', '', ''], 'integration_range': ['', ''], 'integration_width': '', 'smoothing': '', 'normtounity': False} for k in cleared_fields: if current_fields[k] != cleared_fields[k]: return False return True def populate_input_fields(self, saved_input): self.populate_cut_params(*saved_input['cut_parameters']) self.populate_integration_params(*saved_input['integration_range']) self.lneCutIntegrationWidth.setText(saved_input['integration_width']) self.lneCutSmoothing.setText(saved_input['smoothing']) self.rdoCutNormToOne.setChecked(saved_input['normtounity']) def get_input_fields(self): saved_input = dict() saved_input['axes'] = [str(self.cmbCutAxis.itemText(ind)) for ind in range(self.cmbCutAxis.count())] saved_input['cut_parameters'] = [self.get_cut_axis_start(), self.get_cut_axis_end(), self.get_cut_axis_step()] saved_input['integration_range'] = [self.get_integration_start(), self.get_integration_end()] saved_input['integration_width'] = self.get_integration_width() saved_input['smoothing'] = self.get_smoothing() saved_input['normtounity'] = self.get_intensity_is_norm_to_one() return saved_input def enable(self): self.lneCutStart.setEnabled(True) self.lneCutEnd.setEnabled(True) self.lneCutStep.setEnabled(True) self.cmbCutAxis.setEnabled(True) self.lneCutIntegrationStart.setEnabled(True) self.lneCutIntegrationEnd.setEnabled(True) self.lneCutIntegrationWidth.setEnabled(True) self.lneEditCutIntensityStart.setEnabled(True) self.lneCutIntensityEnd.setEnabled(True) self.rdoCutNormToOne.setEnabled(True) self.btnCutSaveToWorkspace.setEnabled(False) self.btnCutPlot.setEnabled(False) self.btnCutPlotOver.setEnabled(False) self.btnCutSaveToWorkspace.setEnabled(True) self.btnCutPlot.setEnabled(True) self.btnCutPlotOver.setEnabled(True) def disable(self): self.lneCutStart.setEnabled(False) self.lneCutEnd.setEnabled(False) self.lneCutStep.setEnabled(False) self.cmbCutAxis.setEnabled(False) self.lneCutIntegrationStart.setEnabled(False) self.lneCutIntegrationEnd.setEnabled(False) self.lneCutIntegrationWidth.setEnabled(False) self.lneEditCutIntensityStart.setEnabled(False) self.lneCutIntensityEnd.setEnabled(False) self.rdoCutNormToOne.setEnabled(False) self.btnCutSaveToWorkspace.setEnabled(False) self.btnCutPlot.setEnabled(False) self.btnCutPlotOver.setEnabled(False) def plotting_params_only(self): self.disable() self.lneEditCutIntensityStart.setEnabled(True) self.lneCutIntensityEnd.setEnabled(True) self.rdoCutNormToOne.setEnabled(True) self.btnCutPlot.setEnabled(True) self.btnCutPlotOver.setEnabled(True) def set_validators(self): line_edits = [self.lneCutStart, self.lneCutEnd, self.lneCutIntegrationStart, self.lneCutIntegrationEnd, self.lneCutIntegrationWidth, self.lneEditCutIntensityStart, self.lneCutIntensityEnd] for line_edit in line_edits: line_edit.setValidator(QDoubleValidator()) def force_normalization(self): self.rdoCutNormToOne.setEnabled(False) self.rdoCutNormToOne.setChecked(True) def clear_displayed_error(self): self.display_error("")
class CutPlotOptions(PlotOptionsDialog): xLogEdited = Signal() yLogEdited = Signal() showLegendsEdited = Signal() def __init__(self): super(CutPlotOptions, self).__init__() self._line_widgets = [] self.groupBox_4.hide() self.chkXLog.stateChanged.connect(self.xLogEdited) self.chkYLog.stateChanged.connect(self.yLogEdited) self.chkShowLegends.stateChanged.connect(self.showLegendsEdited) def set_line_options(self, line_options): for line in line_options: line_widget = LegendAndLineOptionsSetter(line, self.color_validator) self.verticalLayout_legend.addWidget(line_widget) self._line_widgets.append(line_widget) def get_line_options(self): all_line_options = [] for line_widget in self._line_widgets: line_options = {} for option in [ 'shown', 'color', 'style', 'width', 'marker', 'legend', 'label', 'error_bar' ]: line_options[option] = getattr(line_widget, option) all_line_options.append(line_options) return all_line_options def color_validator(self, selected): count = 0 for line_widget in self._line_widgets: if line_widget.get_color_index() == selected: count += 1 if count <= 1: return True msg_box = QtWidgets.QMessageBox(self) msg_box.setWindowTitle("Selection Invalid") msg_box.setIcon(QtWidgets.QMessageBox.Warning) msg_box.setText("Cannot have two lines the same colour.") msg_box.exec_() return False @property def x_log(self): return self.chkXLog.isChecked() @x_log.setter def x_log(self, value): self.chkXLog.setChecked(value) @property def y_log(self): return self.chkYLog.isChecked() @y_log.setter def y_log(self, value): self.chkYLog.setChecked(value) @property def show_legends(self): return self.chkShowLegends.isChecked() @show_legends.setter def show_legends(self, value): self.chkShowLegends.setChecked(value)
class PlotOptionsDialog(QtWidgets.QDialog): titleEdited = Signal() xLabelEdited = Signal() yLabelEdited = Signal() xRangeEdited = Signal() yRangeEdited = Signal() xGridEdited = Signal() yGridEdited = Signal() def __init__(self, parent=None): QtWidgets.QDialog.__init__(self, parent) load_ui(__file__, 'plot_options.ui', self) self.lneFigureTitle.editingFinished.connect(self.titleEdited) self.lneXAxisLabel.editingFinished.connect(self.xLabelEdited) self.lneYAxisLabel.editingFinished.connect(self.yLabelEdited) self.lneXMin.editingFinished.connect(self.xRangeEdited) self.lneXMax.editingFinished.connect(self.xRangeEdited) self.lneYMin.editingFinished.connect(self.yRangeEdited) self.lneYMax.editingFinished.connect(self.yRangeEdited) self.buttonBox.accepted.connect(self.accept) self.buttonBox.rejected.connect(self.reject) self.chkXGrid.stateChanged.connect(self.xGridEdited) self.chkYGrid.stateChanged.connect(self.yGridEdited) @property def x_range(self): try: xmin = float(str(self.lneXMin.text())) xmax = float(str(self.lneXMax.text())) except ValueError: return None, None return xmin, xmax @x_range.setter def x_range(self, x_range): try: xmin, xmax = x_range except ValueError: raise ValueError("pass an iterable with two items") self.lneXMin.setText(str(xmin)) self.lneXMax.setText(str(xmax)) @property def y_range(self): try: ymin = float(str(self.lneYMin.text())) ymax = float(str(self.lneYMax.text())) except ValueError: return None, None return ymin, ymax @y_range.setter def y_range(self, yrange): try: ymin, ymax = yrange except ValueError: raise ValueError("pass an iterable with two items") self.lneYMin.setText(str(ymin)) self.lneYMax.setText(str(ymax)) @property def title(self): return self.lneFigureTitle.text() @title.setter def title(self, value): self.lneFigureTitle.setText(value) @property def x_label(self): return self.lneXAxisLabel.text() @x_label.setter def x_label(self, value): self.lneXAxisLabel.setText(value) @property def y_label(self): return self.lneYAxisLabel.text() @y_label.setter def y_label(self, value): self.lneYAxisLabel.setText(value) @property def x_grid(self): return self.chkXGrid.isChecked() @x_grid.setter def x_grid(self, value): self.chkXGrid.setChecked(value) @property def y_grid(self): return self.chkYGrid.isChecked() @y_grid.setter def y_grid(self, value): self.chkYGrid.setChecked(value)
class WorkspaceManagerWidget(WorkspaceView, QWidget): """A Widget that allows user to perform basic workspace save/load/rename/delete operations on workspaces""" error_occurred = Signal('QString') tab_changed = Signal(int) busy = Signal(bool) def __init__(self, parent=None): QWidget.__init__(self, parent) load_ui(__file__, 'workspacemanager.ui', self) self.button_mappings = {} self._main_window = None self.onscreen_workspaces = [] self.tab = None self.tab_to_list = { TAB_2D: self.listWorkspaces2D, TAB_EVENT: self.listWorkspacesEvent, TAB_HISTO: self.listWorkspacesHisto } self.tabWidget.currentChanged.connect(self.tab_changed_method) self.listWorkspaces2D.itemSelectionChanged.connect( self.list_item_changed) self.listWorkspacesEvent.itemSelectionChanged.connect( self.list_item_changed) self.listWorkspacesHisto.itemSelectionChanged.connect( self.list_item_changed) self._presenter = WorkspaceManagerPresenter(self) def _display_error(self, error_string): self.error_occurred.emit(error_string) def tab_changed_method(self, tab_index): self.clear_selection() self.tab = tab_index if self.tabWidget.tabText(tab_index)[-1:] == "*": self.tabWidget.setTabText(tab_index, self.tabWidget.tabText(tab_index)[:-1]) self.tab_changed.emit(tab_index) def clear_selection(self): for ws_list in [ self.listWorkspaces2D, self.listWorkspacesEvent, self.listWorkspacesHisto ]: ws_list.clearSelection() def current_list(self): return self.tab_to_list[self.tabWidget.currentIndex()] def current_tab(self): return self.tab if self.tab is not None else self.tabWidget.currentIndex( ) def change_tab(self, tab): self.tabWidget.setCurrentIndex(tab) def highlight_tab(self, tab): tab_text = self.tabWidget.tabText(tab) if not tab_text.endswith('*'): self.tabWidget.setTabText(tab, tab_text + '*') def _btn_clicked(self): sender = self.sender() try: command = self.button_mappings[sender] except KeyError: raise Exception('Invalid sender') self._presenter.notify(command) def add_workspace(self, workspace): item = QListWidgetItem(workspace) self.onscreen_workspaces.append(workspace) workspace = get_workspace_handle(workspace) if isinstance(workspace, PixelWorkspace): self.listWorkspacesEvent.addItem(item) elif isinstance(workspace, HistogramWorkspace): self.listWorkspacesHisto.addItem(item) elif isinstance(workspace, Workspace): self.listWorkspaces2D.addItem(item) else: raise TypeError("Loaded file is not a valid workspace") def display_loaded_workspaces(self, workspaces): for workspace in workspaces: if workspace not in self.onscreen_workspaces: self.add_workspace(workspace) for workspace in self.onscreen_workspaces: if workspace not in workspaces: self.remove_workspace(workspace) def remove_workspace(self, workspace): """Remove workspace from list. Must be done in seperate function because items are removed by index and removing an items may alter the indexes of other items""" self.onscreen_workspaces.remove(workspace) for ws_list in [ self.listWorkspaces2D, self.listWorkspacesEvent, self.listWorkspacesHisto ]: for index in range(ws_list.count()): if ws_list.item(index).text() == workspace: ws_list.takeItem(index) return def add_workspace_dialog(self): items = [] current_list = self.current_list() for i in range(current_list.count()): item = current_list.item(i).text() items.append(item) dialog = QInputDialog() dialog.setWindowTitle("Add Workspace") dialog.setLabelText("Choose a workspace to add:") dialog.setOptions(QInputDialog.UseListViewForComboBoxItems) dialog.setComboBoxItems(items) dialog.exec_() return dialog.textValue() def subtraction_input(self): sub_input = SubtractInputBox(self.listWorkspaces2D, self) if sub_input.exec_(): return sub_input.user_input() else: raise RuntimeError('dialog cancelled') def get_workspace_selected(self): selected_workspaces = [ str(x.text()) for x in self.current_list().selectedItems() ] return selected_workspaces def set_workspace_selected(self, index): current_list = self.current_list() if QT_VERSION.startswith('5'): for item_index in range(current_list.count()): current_list.item(item_index).setSelected(False) for this_index in (index if hasattr(index, "__iter__") else [index]): current_list.item(this_index).setSelected(True) else: for item_index in range(current_list.count()): current_list.setItemSelected(current_list.item(item_index), False) for this_index in (index if hasattr(index, "__iter__") else [index]): current_list.setItemSelected(current_list.item(this_index), True) def get_workspace_index(self, ws_name): current_list = self.current_list() for index in range(current_list.count()): if str(current_list.item(index).text()) == ws_name: return index return -1 def get_workspace_to_load_path(self): paths = QFileDialog.getOpenFileNames() return paths[0] if isinstance( paths, tuple) else [str(filename) for filename in paths] def get_workspace_new_name(self): name, success = QInputDialog.getText( self, "Workspace New Name", "Enter the new name for the workspace : ") # The message above was padded with spaces to allow the whole title to show up if not success: raise ValueError('No Valid Name supplied') return str(name) def error_select_only_one_workspace(self): self._display_error( 'Please select only one workspace and then try again') def error_select_one_or_more_workspaces(self): self._display_error( 'Please select one or more workspaces the try again') def error_select_one_workspace(self): self._display_error('Please select a workspace then try again') def error_select_more_than_one_workspaces(self): self._display_error( 'Please select more than one projected workspaces then try again') def error_invalid_save_path(self): self._display_error('No files were saved') def get_presenter(self): return self._presenter def list_item_changed(self): self._presenter.notify(Command.SelectionChanged) def error_unable_to_save(self): self._display_error("Something went wrong while trying to save") def clear_displayed_error(self): self._display_error("")
class PowderWidget(PowderView, QWidget): """This widget is not usable without a main window which implements mainview""" error_occurred = Signal('QString') busy = Signal(bool) def __init__(self, parent=None, *args, **kwargs): QWidget.__init__(self, parent, *args, **kwargs) load_ui(__file__, 'powder.ui', self) self.btnPowderCalculateProjection.clicked.connect(self._btn_clicked) self._presenter = PowderProjectionPresenter( self, MantidProjectionCalculator()) self.cmbPowderU1.currentIndexChanged.connect(self._u1_changed) self.cmbPowderU2.currentIndexChanged.connect(self._u2_changed) def get_presenter(self): return self._presenter def _u1_changed(self): self._presenter.notify(Command.U1Changed) def _u2_changed(self): self._presenter.notify(Command.U2Changed) def _btn_clicked(self): self._presenter.notify(Command.CalculatePowderProjection) def get_powder_u1(self): return str(self.cmbPowderU1.currentText()) def get_powder_u2(self): return str(self.cmbPowderU2.currentText()) def set_powder_u1(self, name): # Signals are blocked to prevent self._u1_changed being called here (it would be false alarm) self.cmbPowderU1.blockSignals(True) self.cmbPowderU1.setCurrentIndex(self._name_to_index[name]) self.cmbPowderU1.blockSignals(False) def set_powder_u2(self, name): # Signals are blocked to prevent self._u2_changed being called here (it would be false alarm) self.cmbPowderU2.blockSignals(True) self.cmbPowderU2.setCurrentIndex(self._name_to_index[name]) self.cmbPowderU2.blockSignals(False) def populate_powder_u1(self, u1_options): # Signals are blocked to prevent self._u1_changed being called here (it would be false alarm) self.cmbPowderU1.blockSignals(True) self.cmbPowderU1.clear() # Assuming that u1 and u2 both have the same possible units. self._name_to_index = {} for idx, value in enumerate(u1_options): self.cmbPowderU1.addItem(value) self._name_to_index[value] = idx self.cmbPowderU1.blockSignals(False) def populate_powder_u2(self, u2_options): # Signals are blocked to prevent self._u2_changed being called here (it would be false alarm) self.cmbPowderU2.blockSignals(True) self.cmbPowderU2.clear() for value in u2_options: self.cmbPowderU2.addItem(value) self.cmbPowderU2.blockSignals(False) def populate_powder_projection_units(self, powder_projection_units): self.cmbPowderUnits.clear() for unit in powder_projection_units: self.cmbPowderUnits.addItem(unit) def get_powder_units(self): return str(self.cmbPowderUnits.currentText()) def disable_calculate_projections(self, disable): self.groupBox.setDisabled(disable) def display_projection_error(self, message): self.error_msg.setText(message) def clear_displayed_error(self): self._display_error("") def _display_error(self, error_string): self.error_occurred.emit(error_string) def display_message_box(self, message): msg_box = QMessageBox() msg_box.setWindowTitle('Powder Projection Error') msg_box.setText(message) msg_box.exec_()
class SliceWidget(SliceView, QWidget): error_occurred = Signal('QString') busy = Signal(bool) def __init__(self, parent=None, *args, **kwargs): """This Widget provides basic control over displaying slices. This widget is NOT USABLE without a main window The main window must implement MainView""" QWidget.__init__(self, parent, *args, **kwargs) load_ui(__file__, 'slice.ui', self) self.btnSliceDisplay.clicked.connect(self._btn_clicked) self.display_errors_to_statusbar = True self._presenter = SliceWidgetPresenter(self) # Each time the fields are populated, set a minimum step size self._minimumStep = {} self.lneSliceXStep.editingFinished.connect( lambda: self._step_edited('x', self.lneSliceXStep)) self.lneSliceYStep.editingFinished.connect( lambda: self._step_edited('y', self.lneSliceYStep)) self.enable_units_choice(False) self.cmbSliceXAxis.currentIndexChanged.connect( lambda ind: self._change_axes(1, ind)) self.cmbSliceYAxis.currentIndexChanged.connect( lambda ind: self._change_axes(2, ind)) self.set_validators() def get_presenter(self): return self._presenter def _btn_clicked(self): if self._step_edited('x', self.lneSliceXStep) and self._step_edited( 'y', self.lneSliceXStep): self._presenter.notify(Command.DisplaySlice) def _step_edited(self, idx, lineEdit): """Checks that user inputted step size is not too small.""" if self._minimumStep: try: value = float(lineEdit.text()) except ValueError: value = 0 self._display_error( 'Invalid step parameter. Using default value.') if value == 0: lineEdit.setText(str(self._minimumStep[idx])) self._display_error('Setting step size to default.') elif value < (self._minimumStep[idx] / 100.): self._display_error('Step size too small!') return False return True def _change_axes(self, axis, idx): """Makes sure u1 and u2 are always different, and updates default limits/steps values.""" curr_axis = axis - 1 other_axis = axis % 2 axes_handle = [self.cmbSliceXAxis, self.cmbSliceYAxis] num_items = axes_handle[other_axis].count() if num_items < 2: return axes = [ self.cmbSliceXAxis.currentText(), self.cmbSliceYAxis.currentText() ] index = [ self.cmbSliceXAxis.currentIndex(), self.cmbSliceYAxis.currentIndex() ] axes_set = [ self.cmbSliceXAxis.setCurrentIndex, self.cmbSliceYAxis.setCurrentIndex ] if axes[curr_axis] == axes[other_axis]: new_index = (index[other_axis] + 1) % num_items axes_set[other_axis](new_index) self._presenter.populate_slice_params() def _display_error(self, error_string): self.error_occurred.emit(error_string) def enable_units_choice(self, enabled): if enabled: # TODO implement conversion from meV to cm-1 pass #self.cmbSliceUnits.show() #self.label_16.show() else: self.cmbSliceUnits.hide() self.label_16.hide() def get_units(self): return self.cmbSliceUnits.currentText() def get_slice_x_axis(self): return str(self.cmbSliceXAxis.currentText()) def get_slice_y_axis(self): return str(self.cmbSliceYAxis.currentText()) def get_slice_is_norm_to_one(self): return self.rdoSliceNormToOne.isChecked() def get_slice_smoothing(self): return str(self.lneSliceSmoothing.text()) def get_slice_x_start(self): return str(self.lneSliceXStart.text()) def get_slice_x_end(self): return str(self.lneSliceXEnd.text()) def get_slice_x_step(self): return str(self.lneSliceXStep.text()) def get_slice_y_start(self): return str(self.lneSliceYStart.text()) def get_slice_y_end(self): return str(self.lneSliceYEnd.text()) def get_slice_y_step(self): return str(self.lneSliceYStep.text()) def get_slice_colourmap(self): return str(self.cmbSliceColormap.currentText()) def get_slice_intensity_start(self): return str(self.lneSliceIntensityStart.text()) def get_slice_intensity_end(self): return str(self.lneSliceIntensityEnd.text()) def populate_colormap_options(self, colormaps): self.cmbSliceColormap.clear() for colormap in colormaps: self.cmbSliceColormap.addItem(colormap) def populate_slice_x_options(self, options): self.cmbSliceXAxis.clear() for option in options: self.cmbSliceXAxis.addItem(option) def populate_slice_y_options(self, options): self.cmbSliceYAxis.clear() for option in options: self.cmbSliceYAxis.addItem(option) def error_select_one_workspace(self): self._display_error('Please select a workspace to slice') def error_invalid_x_params(self): self._display_error('Invalid parameters for the x axis of the slice') def error_invalid_intensity_params(self): self._display_error( 'Invalid parameters for the intensity of the slice') def error_invalid_plot_parameters(self): self._display_error('Invalid parameters for the slice') def error_invalid_smoothing_params(self): self._display_error('Invalid value for smoothing') def error_invalid_y_units(self): self._display_error('Invalid selection of the y axis') def error_invalid_y_params(self): self._display_error('Invalid parameters for the y axis of the slice') def error_invalid_x_units(self): self._display_error('Invalid selection of the x axis') def error(self, string): self._display_error(string) def populate_slice_x_params(self, x_start, x_end, x_step): self.lneSliceXStart.setText(x_start) self.lneSliceXEnd.setText(x_end) self.lneSliceXStep.setText(x_step) if x_step: self._minimumStep['x'] = float(x_step) def populate_slice_y_params(self, y_start, y_end, y_step): self.lneSliceYStart.setText(y_start) self.lneSliceYEnd.setText(y_end) self.lneSliceYStep.setText(y_step) if y_step: self._minimumStep['y'] = float(y_step) def clear_input_fields(self): self.populate_slice_x_options([]) self.populate_slice_y_options([]) self.populate_slice_x_params("", "", "") self.populate_slice_y_params("", "", "") self.lneSliceIntensityStart.setText("") self.lneSliceIntensityEnd.setText("") self.rdoSliceNormToOne.setChecked(0) self._minimumStep = {} def disable(self): self.cmbSliceXAxis.setEnabled(False) self.cmbSliceYAxis.setEnabled(False) self.lneSliceXStart.setEnabled(False) self.lneSliceXEnd.setEnabled(False) self.lneSliceXStep.setEnabled(False) self.lneSliceYStart.setEnabled(False) self.lneSliceYEnd.setEnabled(False) self.lneSliceYStep.setEnabled(False) self.lneSliceIntensityStart.setEnabled(False) self.lneSliceIntensityEnd.setEnabled(False) self.rdoSliceNormToOne.setEnabled(False) self.btnSliceDisplay.setEnabled(False) self.cmbSliceColormap.setEnabled(False) def enable(self): self.cmbSliceXAxis.setEnabled(True) self.cmbSliceYAxis.setEnabled(True) self.lneSliceXStart.setEnabled(True) self.lneSliceXEnd.setEnabled(True) self.lneSliceXStep.setEnabled(True) self.lneSliceYStart.setEnabled(True) self.lneSliceYEnd.setEnabled(True) self.lneSliceYStep.setEnabled(True) self.lneSliceIntensityStart.setEnabled(True) self.lneSliceIntensityEnd.setEnabled(True) self.rdoSliceNormToOne.setEnabled(True) self.btnSliceDisplay.setEnabled(True) self.cmbSliceColormap.setEnabled(True) def set_validators(self): line_edits = [ self.lneSliceXStart, self.lneSliceXEnd, self.lneSliceXStep, self.lneSliceYStart, self.lneSliceYEnd, self.lneSliceYStep, self.lneSliceIntensityStart, self.lneSliceIntensityEnd ] for line_edit in line_edits: line_edit.setValidator(QDoubleValidator()) def clear_displayed_error(self): self._display_error("")
class DataLoaderWidget(QWidget): # and some view interface error_occurred = Signal('QString') busy = Signal(bool) def __init__(self, parent=None): QWidget.__init__(self, parent) load_ui(__file__, 'dataloader.ui', self) self.file_system = QFileSystemModel() self.directory = QDir(os.path.expanduser('~')) path = self.directory.absolutePath() self.root_path = path self.file_system.setRootPath(path) self.file_system.setNameFilters(MSLICE_EXTENSIONS) self.file_system.setNameFilterDisables(False) self.table_view.setModel(self.file_system) self.table_view.setRootIndex(self.file_system.index(path)) self.txtpath.setText(path) self.table_view.setColumnWidth(0, 320) self.table_view.setColumnWidth(1, 0) self.table_view.setColumnWidth(3, 140) self.table_view.setSelectionBehavior(QAbstractItemView.SelectRows) self._presenter = DataLoaderPresenter(self) self.btnload.setEnabled(False) self.btnmerge.setEnabled(False) self.table_view.activated.connect(self.activated) self.table_view.clicked.connect(self.validate_selection) self.txtpath.editingFinished.connect(self.refresh) self.btnback.clicked.connect(self.back) self.sort.currentIndexChanged.connect(self.sort_files) self.btnhome.clicked.connect(self.go_to_home) self.btnload.clicked.connect(partial(self.load, False)) self.btnmerge.clicked.connect(partial(self.load, True)) def keyPressEvent(self, event): if event.key() == Qt.Key_Backspace: self.back() else: event.accept() def activated(self, file_clicked): file_clicked = file_clicked.sibling( file_clicked.row(), 0) # so clicking anywhere on row gives filename if self.file_system.isDir(file_clicked): self.enter_dir(self.file_system.fileName(file_clicked)) else: self.load(False) def enter_dir(self, directory): self.directory.cd(directory) self._update_from_path() def refresh(self): path_entered = QDir(self.txtpath.text()) if path_entered.exists(): self.directory = path_entered self._update_from_path() else: self._display_error("Invalid file path") def _update_from_path(self): new_path = self.directory.absolutePath() try: rel_path = os.path.relpath(new_path, self.root_path) except ValueError: # We are in windows and user changed to another drive rel_path = '..' if rel_path.startswith('..'): self.file_system.setRootPath(new_path) self.root_path = new_path self.table_view.setRootIndex(self.file_system.index(new_path)) self.txtpath.setText(new_path) self._clear_displayed_error() def back(self): self.directory.cdUp() self._update_from_path() def load(self, merge): self._presenter.load_workspace(self.get_selected_file_paths(), merge) def sort_files(self, column): self.table_view.sortByColumn( column, column % 2) # descending order for size/modified, ascending for name/type def go_to_home(self): self.directory = QDir(os.path.expanduser('~')) self._update_from_path() def validate_selection(self): self.btnload.setEnabled(False) self.btnmerge.setEnabled(False) selected = self.get_selected_file_paths() for selection in selected: if self.file_system.isDir(self.file_system.index(selection)): return self.btnload.setEnabled(True) if len(selected) > 1: self.btnmerge.setEnabled(True) def get_selected_file_paths(self): selected = self.table_view.selectionModel().selectedRows() for i in range(len(selected)): selected[i] = selected[i].sibling(selected[i].row(), 0) selected[i] = str( os.path.join(self.directory.absolutePath(), self.file_system.fileName(selected[i]))) return selected def get_workspace_efixed(self, workspace, hasMultipleWS=False, default_value=None): Ef, applyToAll, success = EfInputDialog.getEf(workspace, hasMultipleWS, default_value) if not success: raise ValueError('Fixed final energy not given') return Ef, applyToAll def get_presenter(self): return self._presenter def error_unable_to_open_file(self, filename=None): self._display_error( 'MSlice was not able to load %s' % ('the selected file' if filename is None else filename)) def error_merge_different_file_formats(self): self._display_error('Cannot merge files with different formats') def no_workspace_has_been_loaded(self, filename=None): if filename is None: self._display_error('No new workspaces have been loaded') else: self._display_error('File %s has not been loaded' % filename) def confirm_overwrite_workspace(self): text = 'The workspace you want to load has the same name as an existing workspace,' \ 'Are you sure you want to overwrite it?' reply = QMessageBox.question(self, 'Confirm Overwrite', text, QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.Yes: return True else: return False def show_busy(self, is_busy): self.busy.emit(is_busy) def error_loading_workspace(self, message): self._display_error(str(message)) def _display_error(self, error_string): self.error_occurred.emit(error_string) def _clear_displayed_error(self): self._display_error("")