From 847424031ea7303455b57747467987ea887731eb Mon Sep 17 00:00:00 2001 From: Despi <114145714+Despiix@users.noreply.github.com> Date: Mon, 27 Jan 2025 10:23:57 +0000 Subject: [PATCH] Surface Plotting from Multiple Single-Spectrum Workspaces (#38599) * Enable Plot All and Okay button * If any workspace contains only 1 spectra then set default value to 1. and minor file reformat * Fix plotting * Add release note * Adjust buttons to correct enable and disable + Adjust Test * Adjust function based on review * Remove Hardcoded "1" Set the default value as the first valid spectrum number * Fix plotting multiple wireframe workspaces * Clean up * Release Note Update * Enable `Plot_All` for Contour plots * Handle Error * Handle Error * If there is already a valid selection (wksp_indices or spectra), just ski * Update docs/source/release/v6.12.0/Workbench/New_features/38171.rst Co-authored-by: James Clarke <139879523+jclarkeSTFC@users.noreply.github.com> * Update docs/source/release/v6.12.0/Workbench/New_features/38171.rst Co-authored-by: James Clarke <139879523+jclarkeSTFC@users.noreply.github.com> * Changes based on suggestions * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Delete duplicate code * Delete duplicate code --------- Co-authored-by: James Clarke <139879523+jclarkeSTFC@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .../v6.12.0/Workbench/New_features/38171.rst | 2 + .../mantidqt/dialogs/spectraselectordialog.py | 55 +++++++++++++++---- .../test/test_spectraselectiondialog.py | 8 +-- .../mantidqt/mantidqt/plotting/functions.py | 36 ++++++++---- 4 files changed, 75 insertions(+), 26 deletions(-) create mode 100644 docs/source/release/v6.12.0/Workbench/New_features/38171.rst diff --git a/docs/source/release/v6.12.0/Workbench/New_features/38171.rst b/docs/source/release/v6.12.0/Workbench/New_features/38171.rst new file mode 100644 index 000000000000..99519f46119c --- /dev/null +++ b/docs/source/release/v6.12.0/Workbench/New_features/38171.rst @@ -0,0 +1,2 @@ +- Enabled surface and contour plotting in the `Plot Advanced` dialog when multiple single-spectrum workspaces are selected. +- Fixed a bug where selecting multiple workspaces in the ADS and plotting a wireframe would only result in one of the workspaces being plotted. diff --git a/qt/python/mantidqt/mantidqt/dialogs/spectraselectordialog.py b/qt/python/mantidqt/mantidqt/dialogs/spectraselectordialog.py index 288c5f6cd02b..5b60e59ba645 100644 --- a/qt/python/mantidqt/mantidqt/dialogs/spectraselectordialog.py +++ b/qt/python/mantidqt/mantidqt/dialogs/spectraselectordialog.py @@ -100,11 +100,35 @@ def __init__(self, workspaces, parent=None, show_colorfill_btn=False, overplot=F self._on_specnums_changed() self._on_wkspindices_changed() + # Check if workspace only has a single spectra, if true set it as first valid spectrum number + def update_spectrum_number_text(self): + if self.selection and ( + (self.selection.wksp_indices and len(self.selection.wksp_indices) > 0) + or (self.selection.spectra and len(self.selection.spectra) > 0) + ): + return + + if not self._ui.specNums.text(): + for ws in self._workspaces: + if ws.getNumberHistograms() == 1: + spectrum_number = ws.getSpectrum(0).getSpectrumNo() + self._ui.specNums.setText(str(spectrum_number)) + self._parse_spec_nums() + break + def on_ok_clicked(self): + self.update_spectrum_number_text() + + if self.selection is None: + QMessageBox.warning(self, "Invalid Input", "Please enter a valid workspace index or spectrum number.") + return + if self._check_number_of_plots(self.selection): self.accept() def on_plot_all_clicked(self): + self.update_spectrum_number_text() + selection = SpectraSelection(self._workspaces) selection.spectra = self._plottable_spectra selection.plot_type = self._ui.plotType.currentIndex() @@ -252,21 +276,29 @@ def _on_specnums_changed(self): ui.wkspIndices.clear() ui.wkspIndicesValid.hide() - self._parse_spec_nums() - ui.specNumsValid.setVisible(not self._is_input_valid()) - if self._is_input_valid() or ui.specNums.text() == "": - ui.specNumsValid.setVisible(False) - ui.specNumsValid.setToolTip("") - elif ui.plotType.currentText() == SURFACE or ui.plotType.currentText() == CONTOUR: + try: + self._parse_spec_nums() + except Exception as e: + logger.error(f"Error parsing spectrum numbers: {e}") + ui.specNumsValid.setToolTip("Invalid spectrum number input.") ui.specNumsValid.setVisible(True) - ui.specNumsValid.setToolTip("Enter one spectrum number in " + ui.specNums.placeholderText()) + ui.buttonBox.button(QDialogButtonBox.Ok).setEnabled(False) + + spectrum_numbers = [ws.getSpectrumNumbers() for ws in self._workspaces] + unique_spectra = {tuple(numbers) for numbers in spectrum_numbers} + + if len(unique_spectra) > 1: + ui.specNums.setEnabled(False) + ui.specNumsValid.setToolTip("Spectrum numbers differ across workspaces. Use 'Plot All' or workspace indices instead.") else: - ui.specNumsValid.setVisible(True) - ui.specNumsValid.setToolTip("Not in " + ui.specNums.placeholderText()) + ui.specNums.setEnabled(True) + ui.specNumsValid.setToolTip("") + + ui.specNumsValid.setVisible(not self._is_input_valid()) ui.buttonBox.button(QDialogButtonBox.Ok).setEnabled(self._is_input_valid()) if self._advanced: - ui.advanced_options_widget._validate_custom_logs(self._ui.advanced_options_widget.ui.custom_log_line_edit.text()) + ui.advanced_options_widget._validate_custom_logs(ui.advanced_options_widget.ui.custom_log_line_edit.text()) def _on_plot_type_changed(self, new_index): if self._overplot: @@ -290,7 +322,8 @@ def _on_plot_type_changed(self, new_index): if self._ui.advanced_options_widget.ui.plot_axis_label_line_edit.text() == WORKSPACE_NAME: self._ui.advanced_options_widget.ui.plot_axis_label_line_edit.setText(WORKSPACE_REFERENCE_NUMBER) - self._ui.buttonBox.button(QDialogButtonBox.YesToAll).setEnabled(False) + self._ui.buttonBox.button(QDialogButtonBox.YesToAll).setEnabled(True) + else: self._ui.advanced_options_widget.ui.error_bars_check_box.setEnabled(True) self._ui.advanced_options_widget.ui.plot_axis_label_line_edit.setEnabled(False) diff --git a/qt/python/mantidqt/mantidqt/dialogs/test/test_spectraselectiondialog.py b/qt/python/mantidqt/mantidqt/dialogs/test/test_spectraselectiondialog.py index 6e1e9f570ab2..27675ead1718 100644 --- a/qt/python/mantidqt/mantidqt/dialogs/test/test_spectraselectiondialog.py +++ b/qt/python/mantidqt/mantidqt/dialogs/test/test_spectraselectiondialog.py @@ -281,17 +281,17 @@ def test_ok_button_disabled_when_custom_log_values_contains_decreasing_values(se ssd._ui.advanced_options_widget.ui.custom_log_line_edit.setText("2,1,3") self.assertFalse(ssd._ui.buttonBox.button(QDialogButtonBox.Ok).isEnabled()) - def test_plot_all_button_disabled_when_plot_type_is_surface(self): + def test_plot_all_button_enabled_when_plot_type_is_surface(self): workspaces = [self._single_spec_ws] * 3 ssd = SpectraSelectionDialog(workspaces, advanced=True) ssd._ui.plotType.setCurrentIndex(3) - self.assertFalse(ssd._ui.buttonBox.button(QDialogButtonBox.YesToAll).isEnabled()) + self.assertTrue(ssd._ui.buttonBox.button(QDialogButtonBox.YesToAll).isEnabled()) - def test_plot_all_button_disabled_when_plot_type_is_contour(self): + def test_plot_all_button_enabled_when_plot_type_is_contour(self): workspaces = [self._single_spec_ws] * 3 ssd = SpectraSelectionDialog(workspaces, advanced=True) ssd._ui.plotType.setCurrentIndex(4) - self.assertFalse(ssd._ui.buttonBox.button(QDialogButtonBox.YesToAll).isEnabled()) + self.assertTrue(ssd._ui.buttonBox.button(QDialogButtonBox.YesToAll).isEnabled()) def test_ok_button_disabled_when_plot_type_is_surface_and_more_than_one_spectrum_number_entered(self): workspaces = [self._multi_spec_ws] * 3 diff --git a/qt/python/mantidqt/mantidqt/plotting/functions.py b/qt/python/mantidqt/mantidqt/plotting/functions.py index 6268e81f7cd1..22283dfe00be 100644 --- a/qt/python/mantidqt/mantidqt/plotting/functions.py +++ b/qt/python/mantidqt/mantidqt/plotting/functions.py @@ -22,7 +22,7 @@ from mantid.kernel import Logger, ConfigService from mantid.plots.datafunctions import add_colorbar_label -from mantid.plots.utility import get_single_workspace_log_value +from mantid.plots.utility import get_single_workspace_log_value, legend_set_draggable from mantidqt.plotting.figuretype import figure_type, FigureType from mantidqt.dialogs.spectraselectorutils import get_spectra_selection from mantid.api import IMDHistoWorkspace @@ -365,18 +365,32 @@ def plot_surface(workspaces, fig=None): @manage_workspace_names def plot_wireframe(workspaces, fig=None): import matplotlib.pyplot as plt + from matplotlib import colormaps - for ws in workspaces: - if fig: - fig.clf() - ax = fig.add_subplot(111, projection="mantid3d") - else: - fig, ax = plt.subplots(subplot_kw={"projection": "mantid3d"}) + cmap = colormaps["tab10"] + colors = [cmap(i / max(1, len(workspaces) - 1)) for i in range(len(workspaces))] - fig.set_layout_engine(layout="tight") - ax.plot_wireframe(ws) - ax.set_title(ws.name()) - fig.show() + if fig: + fig.clf() + ax = fig.add_subplot(111, projection="mantid3d") + else: + fig, ax = plt.subplots(subplot_kw={"projection": "mantid3d"}) + + for i, ws in enumerate(workspaces): + try: + ax.plot_wireframe(ws, color=colors[i], label=ws.name()) + except Exception as e: + LOGGER.error(f"Failed to plot workspace {ws.name()}: {e}") + + if len(workspaces) > 1: + legend = ax.legend(loc="upper right", title="Workspaces") + legend_set_draggable(legend, True) + + workspace_names = ", ".join(ws.name() for ws in workspaces) + ax.set_title(f"Wireframe Plot: {workspace_names}") + + fig.set_layout_engine(layout="tight") + fig.show() return fig