diff --git a/docs/sphinx-docs/source/user/menu_bar.rst b/docs/sphinx-docs/source/user/menu_bar.rst index 6cc104257b..0ab0c88d84 100644 --- a/docs/sphinx-docs/source/user/menu_bar.rst +++ b/docs/sphinx-docs/source/user/menu_bar.rst @@ -15,6 +15,8 @@ onto Data Explorer. A *SasView* session can also be saved and reloaded as an 'Analysis' (an individual model fit or invariant calculation, etc), or as a 'Project' (everything you have done since starting your *SasView* session). +Finally, a session can be closed so a new project can be created. This will clear all plots, data and +content in all the perspectives, even those which are not currently visible. Edit ---- diff --git a/src/sas/qtgui/MainWindow/DataExplorer.py b/src/sas/qtgui/MainWindow/DataExplorer.py index d3266304d9..0869b9d9a7 100644 --- a/src/sas/qtgui/MainWindow/DataExplorer.py +++ b/src/sas/qtgui/MainWindow/DataExplorer.py @@ -2150,3 +2150,12 @@ def setCheckItems(self, status=QtCore.Qt.Unchecked): if item.isCheckable(): item.setCheckState(status) model.blockSignals(False) + + def reset(self): + """ + Reset the data explorer to an empty state + """ + self.closeAllPlots() + self.model.clear() + self.theory_model.clear() + diff --git a/src/sas/qtgui/MainWindow/GuiManager.py b/src/sas/qtgui/MainWindow/GuiManager.py index 04e34dcd70..ac4018174d 100644 --- a/src/sas/qtgui/MainWindow/GuiManager.py +++ b/src/sas/qtgui/MainWindow/GuiManager.py @@ -8,7 +8,7 @@ from packaging.version import Version from PySide6.QtCore import QLocale, Qt from PySide6.QtGui import QStandardItem -from PySide6.QtWidgets import QDockWidget, QLabel, QProgressBar, QTextBrowser +from PySide6.QtWidgets import QDockWidget, QLabel, QMessageBox, QProgressBar, QTextBrowser from twisted.internet import reactor import sas @@ -706,6 +706,7 @@ def addTriggers(self): self._workspace.actionOpen_Analysis.triggered.connect(self.actionOpen_Analysis) self._workspace.actionSave.triggered.connect(self.actionSave_Project) self._workspace.actionSave_Analysis.triggered.connect(self.actionSave_Analysis) + self._workspace.actionClose_Project.triggered.connect(self.actionClose_Project) self._workspace.actionPreferences.triggered.connect(self.actionOpen_Preferences) self._workspace.actionQuit.triggered.connect(self.actionQuit) # Edit @@ -809,13 +810,14 @@ def actionOpen_Analysis(self): self.filesWidget.loadAnalysis() - def actionSave_Project(self): + def actionSave_Project(self) -> bool: """ Menu Save Project + return: True if save was successful, False otherwise """ filename = self.filesWidget.saveProject() if not filename: - return + return False # datasets all_data = self.filesWidget.getSerializedData() @@ -840,6 +842,7 @@ def actionSave_Project(self): with open(filename, 'w') as outfile: GuiUtils.saveData(outfile, final_data) + return True def actionSave_Analysis(self): """ @@ -1319,6 +1322,24 @@ def actionAbout(self): about = About() about.exec() + def actionClose_Project(self): + """ + Menu File/Close Project + """ + # Make sure this is what the user really wants + reply = QMessageBox.question(self._parent, 'Close Project', + "Do you want to save the project before closing?\n" + "All unsaved changes will be lost if you don't save.", + QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel, + QMessageBox.Cancel) + if reply == QMessageBox.Save: + saved = self.actionSave_Project() + if saved: + self.resetProject() + elif reply == QMessageBox.Discard: + self.resetProject() + # else Cancel, do nothing + def actionCheck_for_update(self): """ Menu Help/Check for Update @@ -1396,3 +1417,15 @@ def saveCustomConfig(self): Save the config file based on current session values """ config.save() + + def resetProject(self): + """ + Reset the project to an empty state + """ + # perspectives + for per in self.loadedPerspectives.values(): + if hasattr(per, 'reset'): + per.reset() + # file manager + self.filesWidget.reset() + diff --git a/src/sas/qtgui/MainWindow/UI/MainWindowUI.ui b/src/sas/qtgui/MainWindow/UI/MainWindowUI.ui index 3829434509..c374f50ef0 100755 --- a/src/sas/qtgui/MainWindow/UI/MainWindowUI.ui +++ b/src/sas/qtgui/MainWindow/UI/MainWindowUI.ui @@ -24,7 +24,7 @@ 0 0 915 - 20 + 26 @@ -42,6 +42,8 @@ + + @@ -632,13 +634,18 @@ MuMag Fitter (Experimental) - + What's New + + + Close Project + + diff --git a/src/sas/qtgui/Perspectives/Corfunc/CorfuncPerspective.py b/src/sas/qtgui/Perspectives/Corfunc/CorfuncPerspective.py index a4833d6dcb..fd361b6258 100644 --- a/src/sas/qtgui/Perspectives/Corfunc/CorfuncPerspective.py +++ b/src/sas/qtgui/Perspectives/Corfunc/CorfuncPerspective.py @@ -989,3 +989,10 @@ def getReport(self) -> ReportData | None: report.add_plot(self.idf_figure) return report.report_data + + def reset(self): + """ + Reset the corfunc perspective to an empty state + """ + self.removeData([self._model_item] if self._model_item else None) + diff --git a/src/sas/qtgui/Perspectives/Fitting/FittingPerspective.py b/src/sas/qtgui/Perspectives/Fitting/FittingPerspective.py index 8ed2013dd6..1f680a60c9 100644 --- a/src/sas/qtgui/Perspectives/Fitting/FittingPerspective.py +++ b/src/sas/qtgui/Perspectives/Fitting/FittingPerspective.py @@ -624,6 +624,15 @@ def getTabByName(self, name): return tab return None + def reset(self): + """ + Reset the fitting perspective to an empty state + """ + while self.count() > 0: + self.closeTabByIndex(0) + # Add an empty fit tab + self.addFit(None) + @property def supports_reports(self) -> bool: return True diff --git a/src/sas/qtgui/Perspectives/Invariant/InvariantPerspective.py b/src/sas/qtgui/Perspectives/Invariant/InvariantPerspective.py index 341b7bfb61..fcaa507fcf 100644 --- a/src/sas/qtgui/Perspectives/Invariant/InvariantPerspective.py +++ b/src/sas/qtgui/Perspectives/Invariant/InvariantPerspective.py @@ -1151,3 +1151,9 @@ def allowSwap(self): Tell the caller that we can't swap data """ return False + + def reset(self): + """ + Reset the fitting perspective to an empty state + """ + self.removeData([self._model_item] if self._model_item else None) diff --git a/src/sas/qtgui/Perspectives/Inversion/InversionPerspective.py b/src/sas/qtgui/Perspectives/Inversion/InversionPerspective.py index e53e4f0fe8..578aefde66 100644 --- a/src/sas/qtgui/Perspectives/Inversion/InversionPerspective.py +++ b/src/sas/qtgui/Perspectives/Inversion/InversionPerspective.py @@ -126,7 +126,6 @@ def closeTabByIndex(self, index): # The tab might have already been deleted previously pass - def closeTabByName(self, tab_name): """ Given name of the tab - close it @@ -435,3 +434,12 @@ def updateFromParameters(self, params): inversion_widget = self.currentWidget() if isinstance(inversion_widget, InversionWidget): inversion_widget.updateFromParameters(params) + + def reset(self): + """ + Reset the Inversion perspective to an empty state + """ + self.tabs.clear() + self.clear() + self.maxIndex = 1 + self.addData(None) diff --git a/src/sas/qtgui/Perspectives/SizeDistribution/SizeDistributionPerspective.py b/src/sas/qtgui/Perspectives/SizeDistribution/SizeDistributionPerspective.py index f8da4c8ba9..0c70220c1d 100644 --- a/src/sas/qtgui/Perspectives/SizeDistribution/SizeDistributionPerspective.py +++ b/src/sas/qtgui/Perspectives/SizeDistribution/SizeDistributionPerspective.py @@ -813,3 +813,10 @@ def clearStatistics(self): self.txtDiameterMean.setText("") self.txtDiameterMode.setText("") self.txtDiameterMedian.setText("") + + def reset(self): + """ + Reset the size distribution perspective to an empty state + """ + self.removeData([self._model_item] if self._model_item else None) + self.resetWindow()