diff --git a/MDANSE_GUI/Src/MDANSE_GUI/TabbedWindow.py b/MDANSE_GUI/Src/MDANSE_GUI/TabbedWindow.py index eb624f3eff..1b11e8b9f9 100644 --- a/MDANSE_GUI/Src/MDANSE_GUI/TabbedWindow.py +++ b/MDANSE_GUI/Src/MDANSE_GUI/TabbedWindow.py @@ -161,6 +161,21 @@ def __init__( self._tabs["Instruments"]._visualiser.instrument_details_changed.connect( self._tabs["Actions"].update_action_after_instrument_change ) + self._tabs["Plot Holder"]._visualiser.current_tab_index.connect( + self._tabs["Plot Creator"]._visualiser.new_target_plot_index + ) + self._tabs["Plot Holder"]._visualiser.current_tab_count.connect( + self._tabs["Plot Creator"]._visualiser.new_target_plot_count + ) + self._tabs["Plot Holder"]._visualiser.datasets_in_plot.connect( + self._tabs["Plot Creator"]._visualiser.new_dataset_count_in_target + ) + self._tabs["Plot Holder"]._visualiser.plot_widget_type.connect( + self._tabs["Plot Creator"]._visualiser.new_plot_widget_type + ) + self._tabs["Plot Holder"].connect_external_view( + self._tabs["Plot Creator"]._visualiser.preview_table + ) # connect signal to the tab self.signal_recent_trajectory_file.connect( self._tabs["Trajectories"].load_trajectory diff --git a/MDANSE_GUI/Src/MDANSE_GUI/Tabs/PlotTab.py b/MDANSE_GUI/Src/MDANSE_GUI/Tabs/PlotTab.py index 2e1346e2e6..605d0e6c3e 100644 --- a/MDANSE_GUI/Src/MDANSE_GUI/Tabs/PlotTab.py +++ b/MDANSE_GUI/Src/MDANSE_GUI/Tabs/PlotTab.py @@ -29,7 +29,7 @@ from MDANSE_GUI.Widgets.PlotSettingsDialog import PlotSettingsEditor if TYPE_CHECKING: - from qtpy.QtWidgets import QDialog, QWidget + from qtpy.QtWidgets import QAbstractItemView, QDialog, QWidget from MDANSE_GUI.Session.Session import Session @@ -66,6 +66,12 @@ def __init__(self, *args, **kwargs): ) self.matplotlib_dialog.values_changed.connect(self._visualiser.update_plots) self._core.add_button("Change matplotlib settings", self.edit_matplotlib) + self.connected_views = [self._view] + + def connect_external_view(self, new_view: QAbstractItemView): + if new_view not in self.connected_views: + self.connected_views.append(new_view) + new_view.setModel(self.model) def launch_dialog(self, dialog: QDialog) -> None: if dialog.isVisible(): @@ -127,7 +133,10 @@ def accept_external_data(self, data_model): LOG.error(f"Visualiser failed to plot data: {e2}") else: self.tab_notification() + self._visualiser.send_plot_info() @Slot(int) def switch_model(self, tab_id): - self._view.setModel(self.model) + for view in self.connected_views: + view.setModel(self.model) + self._visualiser.send_plot_info() diff --git a/MDANSE_GUI/Src/MDANSE_GUI/Tabs/Visualisers/DataPlotter.py b/MDANSE_GUI/Src/MDANSE_GUI/Tabs/Visualisers/DataPlotter.py index c714257e4e..1e80d8aeea 100644 --- a/MDANSE_GUI/Src/MDANSE_GUI/Tabs/Visualisers/DataPlotter.py +++ b/MDANSE_GUI/Src/MDANSE_GUI/Tabs/Visualisers/DataPlotter.py @@ -17,7 +17,9 @@ from qtpy.QtCore import Signal, Slot from qtpy.QtWidgets import ( + QGroupBox, QHBoxLayout, + QLabel, QMessageBox, QPushButton, QTableView, @@ -47,31 +49,95 @@ class DataPlotter(QWidget): def __init__(self, *args, unit_lookup=None, **kwargs): super().__init__(*args, **kwargs) - + self.tab_index, self.tab_count = 0, 1 + self.dataset_count = 0 + self.plotter_type = "PlotWidget" self._unit_lookup = unit_lookup layout = QVBoxLayout(self) - button_bar = QWidget(self) - button_layout = QHBoxLayout(button_bar) + control_bar = QWidget(self) + bar_layout = QHBoxLayout(control_bar) self._selection_viewer = QTableView(self) layout.addWidget(self._selection_viewer) - layout.addWidget(button_bar) - buttons = [ - ("Plot Data", self.plot_data), - ("Clear", self.clear), - ("New Plot", self.new_plot), - ("New Data View (Text)", self.new_text), - ] - for name, function in buttons: - button = QPushButton(name, button_bar) - button_layout.addWidget(button) - if function is not None: - button.clicked.connect(function) + layout.addWidget(control_bar) + button_bar = self.create_buttons() + bar_layout.addWidget(button_bar) + plotter_preview = self.create_preview() + bar_layout.addWidget(plotter_preview) self._model = PlottingContext( unit_lookup=self._unit_lookup, ) self._selection_viewer.setModel(self._model) self.hide_columns() + def create_buttons(self) -> QWidget: + button_bar = QWidget(self) + button_layout = QVBoxLayout(button_bar) + button_groups = { + "Empty plot creation": [ + ("New empty plot", self.new_plot), + ("New empty text view", self.new_text), + ], + "Current data selection": [ + ("Send data to plotter", self.plot_data), + ("Clear data selection", self.clear), + ], + } + for group_name, buttons in button_groups.items(): + subgroup = QGroupBox(group_name, button_bar) + sublayout = QVBoxLayout(subgroup) + for name, function in buttons: + button = QPushButton(name, button_bar) + sublayout.addWidget(button) + if function is not None: + button.clicked.connect(function) + button_layout.addWidget(subgroup) + return button_bar + + def create_preview(self) -> QWidget: + previewer = QWidget(self) + previewer_layout = QVBoxLayout(previewer) + self.target_label = QLabel("Target plot in next tab.") + self.target_label.setWordWrap(True) + previewer_layout.addWidget(self.target_label) + info_label = QLabel( + f"Contents of the currently selected {self.plotter_type} in the next tab:" + ) + previewer_layout.addWidget(info_label) + self.preview_table = QTableView(previewer) + previewer_layout.addWidget(self.preview_table) + self.update_target_plot_label() + return previewer + + def update_target_plot_label(self): + self.target_label.setText( + "Datasets listed above will be sent to the Plot Holder tab.
" + f"They will appear in tab {self.tab_index + 1} out of {self.tab_count}.
" + f"It is a {self.plotter_type}, currently containing {self.dataset_count} datasets." + ) + for col_num in range(4, 10): + self.preview_table.hideColumn(col_num) + self.preview_table.resizeColumnsToContents() + + @Slot(int) + def new_target_plot_index(self, new_index: int): + self.tab_index = new_index + self.update_target_plot_label() + + @Slot(int) + def new_target_plot_count(self, new_count: int): + self.tab_count = new_count + self.update_target_plot_label() + + @Slot(int) + def new_dataset_count_in_target(self, new_dataset_count: int): + self.dataset_count = new_dataset_count + self.update_target_plot_label() + + @Slot(str) + def new_plot_widget_type(self, new_plot_widget_type: str): + self.plotter_type = new_plot_widget_type + self.update_target_plot_label() + @Slot(object) def add_dataset(self, dataset: SingleDataset): """Append a dataset to the current model.""" diff --git a/MDANSE_GUI/Src/MDANSE_GUI/Tabs/Visualisers/PlotHolder.py b/MDANSE_GUI/Src/MDANSE_GUI/Tabs/Visualisers/PlotHolder.py index 1a1f1360b2..0a9ea91652 100644 --- a/MDANSE_GUI/Src/MDANSE_GUI/Tabs/Visualisers/PlotHolder.py +++ b/MDANSE_GUI/Src/MDANSE_GUI/Tabs/Visualisers/PlotHolder.py @@ -34,6 +34,10 @@ class PlotHolder(QTabWidget): error = Signal(str) new_entry = Signal() + current_tab_index = Signal(int) + current_tab_count = Signal(int) + datasets_in_plot = Signal(int) + plot_widget_type = Signal(str) def __init__(self, *args, unit_lookup=None, **kwargs): super().__init__(*args, **kwargs) @@ -75,6 +79,7 @@ def new_plot(self, tab_name: str) -> int: self._plotter.append(plotter) self.setCurrentIndex(tab_id) self.new_entry.emit() + self.send_plot_info() return tab_id @Slot(str) @@ -91,6 +96,7 @@ def new_text(self, ignored_name: str) -> int: self._plotter.append(plotter) self.setCurrentIndex(tab_id) self.new_entry.emit() + self.send_plot_info() return tab_id @Slot(int) @@ -108,9 +114,10 @@ def clean_up_closed_tab(self, tab_id: int): self._current_id = valid_id_values[0] self.setCurrentIndex(self._current_id) self.removeTab(tab_id) + self.send_plot_info() @property - def model(self): + def model(self) -> PlottingContext: tab_id = self.currentIndex() try: pc = self._context[tab_id] @@ -122,7 +129,7 @@ def model(self): return pc @property - def plotter(self): + def plotter(self) -> DataWidget: tab_id = self.currentIndex() try: return self._plotter[tab_id] @@ -164,3 +171,11 @@ def update_plot(self, plot_number: int): except Exception: LOG.error("Plotting failed: %s", traceback.format_exc()) plotter.plot_blank() + else: + self.send_plot_info() + + def send_plot_info(self): + self.current_tab_count.emit(len(self._plotter)) + self.current_tab_index.emit(self.currentIndex()) + self.datasets_in_plot.emit(self.model.rowCount()) + self.plot_widget_type.emit(type(self.plotter).__name__)