From 9fc532076b92f1cbb1c9d0bd511a4a028743a8d1 Mon Sep 17 00:00:00 2001 From: krzywon Date: Tue, 22 Nov 2022 14:58:27 -0500 Subject: [PATCH 01/44] Create custom config file if it doesn't exist in user directory rather than copying file that may not be found --- src/sas/__init__.py | 2 +- src/sas/_config.py | 33 ++++++++++++++++++++++++++++----- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/src/sas/__init__.py b/src/sas/__init__.py index 261f1cfb26..b051531c5f 100644 --- a/src/sas/__init__.py +++ b/src/sas/__init__.py @@ -41,7 +41,7 @@ def get_custom_config(): global _CUSTOM_CONFIG if not _CUSTOM_CONFIG: from ._config import setup_custom_config - _CUSTOM_CONFIG = setup_custom_config(get_app_dir(), get_user_dir()) + _CUSTOM_CONFIG = setup_custom_config(get_user_dir()) return _CUSTOM_CONFIG diff --git a/src/sas/_config.py b/src/sas/_config.py index 7c998672eb..39a57637ac 100644 --- a/src/sas/_config.py +++ b/src/sas/_config.py @@ -11,6 +11,30 @@ logger = logging.getLogger(__name__) +CUSTOM_CONFIG = r''' +""" +Application appearance custom configuration +""" +DATAPANEL_WIDTH = -1 +CLEANUP_PLOT = False +FIXED_PANEL = True +PLOPANEL_WIDTH = -1 +DATALOADER_SHOW = True +GUIFRAME_HEIGHT = -1 +GUIFRAME_WIDTH = -1 +CONTROL_WIDTH = -1 +CONTROL_HEIGHT = -1 +DEFAULT_OPEN_FOLDER = None +WELCOME_PANEL_SHOW = False +TOOLBAR_SHOW = True +DEFAULT_PERSPECTIVE = "Fitting" +SAS_OPENCL = "None" +MARKETPLACE_URL = "http://marketplace.sasview.org/" + +# Logging options +FILTER_DEBUG_LOGS = True +''' + def dirn(path, n): """ Return the directory n up from the current path @@ -83,15 +107,14 @@ def make_custom_config_path(user_dir): path = os.path.join(dirname, "custom_config.py") return path -def setup_custom_config(app_dir, user_dir): +def setup_custom_config(user_dir): path = make_custom_config_path(user_dir) if not os.path.isfile(path): try: - # if the custom config file does not exist, copy the default from - # the app dir - shutil.copyfile(os.path.join(app_dir, "custom_config.py"), path) + with open(path, 'w+') as f: + f.write(CUSTOM_CONFIG) except Exception: - logger.error("Could not copy default custom config.") + logger.error("Could not write default custom config.") #Adding SAS_OPENCL if it doesn't exist in the config file # - to support backcompability From 1f88ae6231774272edc6fbc1bbdbb7dd77459142 Mon Sep 17 00:00:00 2001 From: Piotr Rozyczko Date: Mon, 20 Jun 2022 21:09:23 +0200 Subject: [PATCH 02/44] Add id to the generated plot. #2098 --- src/sas/qtgui/Calculators/DataOperationUtilityPanel.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sas/qtgui/Calculators/DataOperationUtilityPanel.py b/src/sas/qtgui/Calculators/DataOperationUtilityPanel.py index e866dd0fb6..d7938718a9 100644 --- a/src/sas/qtgui/Calculators/DataOperationUtilityPanel.py +++ b/src/sas/qtgui/Calculators/DataOperationUtilityPanel.py @@ -160,6 +160,7 @@ def onPrepareOutputData(self): """ Prepare datasets to be added to DataExplorer and DataManager """ name = self.txtOutputData.text() self.output.name = name + self.output.id = name + str(time.time()) new_item = GuiUtils.createModelItemWithPlot( self.output, name=name) @@ -410,7 +411,6 @@ def updatePlot(self, graph, layout, data): # plot 2D data plotter2D = Plotter2DWidget(self, quickplot=True) plotter2D.scale = 'linear' - plotter2D.ax.tick_params(axis='x', labelsize=8) plotter2D.ax.tick_params(axis='y', labelsize=8) From a9d79e39a81d59700427e9f84fb9834692067525 Mon Sep 17 00:00:00 2001 From: Peter Beaucage Date: Tue, 25 Oct 2022 07:24:32 -0300 Subject: [PATCH 03/44] Add persistent legend visibility toggle --- src/sas/qtgui/Plotting/Plotter.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/sas/qtgui/Plotting/Plotter.py b/src/sas/qtgui/Plotting/Plotter.py index 0d929487e5..0169c59d48 100644 --- a/src/sas/qtgui/Plotting/Plotter.py +++ b/src/sas/qtgui/Plotting/Plotter.py @@ -86,6 +86,8 @@ def __init__(self, parent=None, manager=None, quickplot=False): self.toolbar._actions['pan'].triggered.connect(self._pan) self.toolbar._actions['zoom'].triggered.connect(self._zoom) + self.legendVisible = True + parent.geometry() @property @@ -248,7 +250,7 @@ def plot(self, data=None, color=None, marker=None, hide_error=False, transform=T self.plot_lines[data.name] = line # Now add the legend with some customizations. - if self.showLegend: + if self.showLegend and self.legendVisible: width=_legendResize(self.canvas.size().width(), self.parent) if width is not None: self.legend = ax.legend(loc='upper right', shadow=True, prop={'size':width}) @@ -301,7 +303,7 @@ def onResize(self, event): """ Resize the legend window/font on canvas resize """ - if not self.showLegend: + if not self.showLegend or not self.legendVisible: return width = _legendResize(event.width, self.parent) # resize the legend to follow the canvas width. @@ -794,8 +796,9 @@ def onToggleLegend(self): if not self.showLegend: return - visible = self.legend.get_visible() - self.legend.set_visible(not visible) + #visible = self.legend.get_visible() + self.legendVisible = not self.legendVisible + self.legend.set_visible(self.legendVisible) self.canvas.draw_idle() def onMplMouseDown(self, event): From 811a10ffb61064fc6bc1ffb6f15659c80923a750 Mon Sep 17 00:00:00 2001 From: Peter Beaucage Date: Tue, 25 Oct 2022 12:36:21 -0300 Subject: [PATCH 04/44] Change visibility, don't block re-creation. Thanks @krzywon for the suggestion! --- src/sas/qtgui/Plotting/Plotter.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sas/qtgui/Plotting/Plotter.py b/src/sas/qtgui/Plotting/Plotter.py index 0169c59d48..399090ad98 100644 --- a/src/sas/qtgui/Plotting/Plotter.py +++ b/src/sas/qtgui/Plotting/Plotter.py @@ -250,7 +250,7 @@ def plot(self, data=None, color=None, marker=None, hide_error=False, transform=T self.plot_lines[data.name] = line # Now add the legend with some customizations. - if self.showLegend and self.legendVisible: + if self.showLegend: width=_legendResize(self.canvas.size().width(), self.parent) if width is not None: self.legend = ax.legend(loc='upper right', shadow=True, prop={'size':width}) @@ -258,7 +258,7 @@ def plot(self, data=None, color=None, marker=None, hide_error=False, transform=T self.legend = ax.legend(loc='upper right', shadow=True) if self.legend: self.legend.set_picker(True) - + self.legend.set_visible(self.legendVisible) # Current labels for axes if self.yLabel and not is_fit: ax.set_ylabel(self.yLabel) From 159e876897890183914f77d2010b9093ddfdbd50 Mon Sep 17 00:00:00 2001 From: Peter Beaucage Date: Thu, 27 Oct 2022 10:13:56 +0200 Subject: [PATCH 05/44] Change plotting to use data.name rather than data.title --- src/sas/qtgui/Plotting/Plotter.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/sas/qtgui/Plotting/Plotter.py b/src/sas/qtgui/Plotting/Plotter.py index 399090ad98..53158be370 100644 --- a/src/sas/qtgui/Plotting/Plotter.py +++ b/src/sas/qtgui/Plotting/Plotter.py @@ -161,6 +161,7 @@ def plot(self, data=None, color=None, marker=None, hide_error=False, transform=T ax = self.ax x = data.view.x y = data.view.y + label = data.name # was self._title # Marker symbol. Passed marker is one of matplotlib.markers characters # Alternatively, picked up from Data1D as an int index of PlotUtilities.SHAPES dict @@ -204,22 +205,22 @@ def plot(self, data=None, color=None, marker=None, hide_error=False, transform=T l_width = markersize * 0.4 if marker == '-' or marker == '--': line = self.ax.plot(x, y, color=color, lw=l_width, marker='', - linestyle=marker, label=self._title, zorder=10)[0] + linestyle=marker, label=label, zorder=10)[0] elif marker == 'vline': y_min = min(y)*9.0/10.0 if min(y) < 0 else 0.0 line = self.ax.vlines(x=x, ymin=y_min, ymax=y, color=color, - linestyle='-', label=self._title, lw=l_width, zorder=1) + linestyle='-', label=label, lw=l_width, zorder=1) elif marker == 'step': line = self.ax.step(x, y, color=color, marker='', linestyle='-', - label=self._title, lw=l_width, zorder=1)[0] + label=label, lw=l_width, zorder=1)[0] else: # plot data with/without errorbars if hide_error: line = ax.plot(x, y, marker=marker, color=color, markersize=markersize, - linestyle='', label=self._title, picker=True) + linestyle='', label=label, picker=True) else: dy = data.view.dy # Convert tuple (lo,hi) to array [(x-lo),(hi-x)] @@ -236,7 +237,7 @@ def plot(self, data=None, color=None, marker=None, hide_error=False, transform=T markersize=markersize, lolims=False, uplims=False, xlolims=False, xuplims=False, - label=self._title, + label=label, zorder=1, picker=True) From 01886dd823e85e257820d86d7fdd467e36ab1052 Mon Sep 17 00:00:00 2001 From: Piotr Rozyczko Date: Thu, 27 Oct 2022 13:19:25 +0200 Subject: [PATCH 06/44] DIfferentiate which combobox to put the graphs in. --- src/sas/qtgui/MainWindow/DataExplorer.py | 29 ++++++++++++++++-------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/src/sas/qtgui/MainWindow/DataExplorer.py b/src/sas/qtgui/MainWindow/DataExplorer.py index 632c55d941..33c13aa93c 100644 --- a/src/sas/qtgui/MainWindow/DataExplorer.py +++ b/src/sas/qtgui/MainWindow/DataExplorer.py @@ -118,6 +118,9 @@ def __init__(self, parent=None, guimanager=None, manager=None): self.cbgraph.editTextChanged.connect(self.enableGraphCombo) self.cbgraph.currentIndexChanged.connect(self.enableGraphCombo) + self.cbgraph_2.editTextChanged.connect(self.enableGraphCombo) + self.cbgraph_2.currentIndexChanged.connect(self.enableGraphCombo) + # Proxy model for showing a subset of Data1D/Data2D content self.data_proxy = QtCore.QSortFilterProxyModel(self) self.data_proxy.setSourceModel(self.model) @@ -940,9 +943,12 @@ def updatePlotName(self, name_tuple): Modify the name of the current plot """ old_name, current_name = name_tuple - ind = self.cbgraph.findText(old_name) - self.cbgraph.setCurrentIndex(ind) - self.cbgraph.setItemText(ind, current_name) + graph = self.cbgraph + if self.current_view == self.freezeView: + graph = self.cbgraph_2 + ind = graph.findText(old_name) + graph.setCurrentIndex(ind) + graph.setItemText(ind, current_name) def add_data(self, data_list): """ @@ -972,12 +978,15 @@ def updateGraphCombo(self, graph_list): """ Modify Graph combo box on graph add/delete """ - orig_text = self.cbgraph.currentText() - self.cbgraph.clear() - self.cbgraph.insertItems(0, graph_list) - ind = self.cbgraph.findText(orig_text) + graph = self.cbgraph + if self.current_view == self.freezeView: + graph = self.cbgraph_2 + orig_text = graph.currentText() + graph.clear() + graph.insertItems(0, graph_list) + ind = graph.findText(orig_text) if ind > 0: - self.cbgraph.setCurrentIndex(ind) + graph.setCurrentIndex(ind) def updatePerspectiveCombo(self, index): """ @@ -1207,11 +1216,13 @@ def appendPlot(self): # new plot data; check which tab is currently active if self.current_view == self.treeView: new_plots = GuiUtils.plotsFromCheckedItems(self.model) + graph = self.cbgraph else: new_plots = GuiUtils.plotsFromCheckedItems(self.theory_model) + graph = self.cbgraph_2 # old plot data - plot_id = str(self.cbgraph.currentText()) + plot_id = str(graph.currentText()) try: assert plot_id in PlotHelper.currentPlots(), "No such plot: %s" % (plot_id) except: From 0abdf558a168409835ee8d444dabbf16baa79fa7 Mon Sep 17 00:00:00 2001 From: Peter Beaucage Date: Thu, 27 Oct 2022 16:59:21 +0200 Subject: [PATCH 07/44] Remove calls to pyplot.cla() --- src/sas/qtgui/Plotting/Plotter.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sas/qtgui/Plotting/Plotter.py b/src/sas/qtgui/Plotting/Plotter.py index 53158be370..3f0339ab9c 100644 --- a/src/sas/qtgui/Plotting/Plotter.py +++ b/src/sas/qtgui/Plotting/Plotter.py @@ -577,7 +577,7 @@ def onResetGraphRange(self): Resets the chart X and Y ranges to their original values """ # Clear graph and plot everything again - mpl.pyplot.cla() + # mpl.pyplot.cla() self.ax.cla() self.setRange = None for ids in self.plot_dict: @@ -654,8 +654,8 @@ def removePlot(self, id): xl = self.ax.xaxis.label.get_text() yl = self.ax.yaxis.label.get_text() - mpl.pyplot.cla() self.ax.cla() + #mpl.pyplot.cla() # Recreate Artist bindings after plot clear self.connect = BindArtist(self.figure) @@ -724,7 +724,7 @@ def onToggleHideError(self, id): self.plot_dict = {} # Clean the canvas - mpl.pyplot.cla() + # mpl.pyplot.cla() self.ax.cla() # Recreate the plots but reverse the error flag for the current From 2544817d3e6a7f4da3d500d0c6c43e996cd25634 Mon Sep 17 00:00:00 2001 From: Peter Beaucage Date: Thu, 27 Oct 2022 13:24:56 -0300 Subject: [PATCH 08/44] Remove commented out calls --- src/sas/qtgui/Plotting/Plotter.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/sas/qtgui/Plotting/Plotter.py b/src/sas/qtgui/Plotting/Plotter.py index 3f0339ab9c..18323efb33 100644 --- a/src/sas/qtgui/Plotting/Plotter.py +++ b/src/sas/qtgui/Plotting/Plotter.py @@ -577,7 +577,6 @@ def onResetGraphRange(self): Resets the chart X and Y ranges to their original values """ # Clear graph and plot everything again - # mpl.pyplot.cla() self.ax.cla() self.setRange = None for ids in self.plot_dict: @@ -655,7 +654,6 @@ def removePlot(self, id): yl = self.ax.yaxis.label.get_text() self.ax.cla() - #mpl.pyplot.cla() # Recreate Artist bindings after plot clear self.connect = BindArtist(self.figure) @@ -724,7 +722,6 @@ def onToggleHideError(self, id): self.plot_dict = {} # Clean the canvas - # mpl.pyplot.cla() self.ax.cla() # Recreate the plots but reverse the error flag for the current From 3c077421d0a456efc35f748b19391289212a6eae Mon Sep 17 00:00:00 2001 From: Peter Beaucage Date: Sat, 29 Oct 2022 11:57:04 +0200 Subject: [PATCH 09/44] Add flag for experimental data and gate plotting on it --- src/sas/qtgui/Perspectives/Fitting/FittingWidget.py | 2 +- src/sas/qtgui/Perspectives/Fitting/ModelThread.py | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/sas/qtgui/Perspectives/Fitting/FittingWidget.py b/src/sas/qtgui/Perspectives/Fitting/FittingWidget.py index b380d0c1a4..7611e10a44 100644 --- a/src/sas/qtgui/Perspectives/Fitting/FittingWidget.py +++ b/src/sas/qtgui/Perspectives/Fitting/FittingWidget.py @@ -2237,7 +2237,7 @@ def _requestPlots(self, item_name, item_model): data_shown = False item = None for item, plot in plots.items(): - if fitpage_name in plot.name: + if not hasattr(plot,'isExpData') and fitpage_name in plot.name: data_shown = True self.communicate.plotRequestedSignal.emit([item, plot], self.tab_id) # return the last data item seen, if nothing was plotted; supposed to be just data) diff --git a/src/sas/qtgui/Perspectives/Fitting/ModelThread.py b/src/sas/qtgui/Perspectives/Fitting/ModelThread.py index eacbdc5350..c36dc3cb52 100644 --- a/src/sas/qtgui/Perspectives/Fitting/ModelThread.py +++ b/src/sas/qtgui/Perspectives/Fitting/ModelThread.py @@ -100,6 +100,8 @@ def compute(self): output[index_model] = value elapsed = time.time() - self.starttime + setattr(self.data,'isExpData',True) + res = dict(image = output, data = self.data, page_id = self.page_id, model = self.model, state = self.state, toggle_mode_on = self.toggle_mode_on, elapsed = elapsed, @@ -254,6 +256,8 @@ def compute(self): elapsed = time.time() - self.starttime + setattr(self.data,'isExpData',True) + res = dict(x = self.data.x[index], y = output[index], page_id = self.page_id, state = self.state, weight = self.weight, fid = self.fid, toggle_mode_on = self.toggle_mode_on, From b515fd7e1628e95759592186dc56a9375f2e3c9b Mon Sep 17 00:00:00 2001 From: Peter Beaucage Date: Sat, 29 Oct 2022 15:58:02 +0200 Subject: [PATCH 10/44] Remove flag and replace with plot data --- src/sas/qtgui/Perspectives/Fitting/FittingWidget.py | 2 +- src/sas/qtgui/Perspectives/Fitting/ModelThread.py | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/sas/qtgui/Perspectives/Fitting/FittingWidget.py b/src/sas/qtgui/Perspectives/Fitting/FittingWidget.py index 7611e10a44..d77529bb06 100644 --- a/src/sas/qtgui/Perspectives/Fitting/FittingWidget.py +++ b/src/sas/qtgui/Perspectives/Fitting/FittingWidget.py @@ -2237,7 +2237,7 @@ def _requestPlots(self, item_name, item_model): data_shown = False item = None for item, plot in plots.items(): - if not hasattr(plot,'isExpData') and fitpage_name in plot.name: + if plot.plot_role != Data1D.ROLE_DATA and fitpage_name in plot.name: data_shown = True self.communicate.plotRequestedSignal.emit([item, plot], self.tab_id) # return the last data item seen, if nothing was plotted; supposed to be just data) diff --git a/src/sas/qtgui/Perspectives/Fitting/ModelThread.py b/src/sas/qtgui/Perspectives/Fitting/ModelThread.py index c36dc3cb52..a2b479acf4 100644 --- a/src/sas/qtgui/Perspectives/Fitting/ModelThread.py +++ b/src/sas/qtgui/Perspectives/Fitting/ModelThread.py @@ -100,8 +100,6 @@ def compute(self): output[index_model] = value elapsed = time.time() - self.starttime - setattr(self.data,'isExpData',True) - res = dict(image = output, data = self.data, page_id = self.page_id, model = self.model, state = self.state, toggle_mode_on = self.toggle_mode_on, elapsed = elapsed, From 9fd8fed22368d1f79b7ec4a8f695a99e07bb44e8 Mon Sep 17 00:00:00 2001 From: Peter Beaucage Date: Sat, 29 Oct 2022 12:40:10 -0300 Subject: [PATCH 11/44] remove unneeded isExpData flag --- src/sas/qtgui/Perspectives/Fitting/ModelThread.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/sas/qtgui/Perspectives/Fitting/ModelThread.py b/src/sas/qtgui/Perspectives/Fitting/ModelThread.py index a2b479acf4..c93115f368 100644 --- a/src/sas/qtgui/Perspectives/Fitting/ModelThread.py +++ b/src/sas/qtgui/Perspectives/Fitting/ModelThread.py @@ -253,9 +253,7 @@ def compute(self): intermediate_results = {} elapsed = time.time() - self.starttime - - setattr(self.data,'isExpData',True) - + res = dict(x = self.data.x[index], y = output[index], page_id = self.page_id, state = self.state, weight = self.weight, fid = self.fid, toggle_mode_on = self.toggle_mode_on, From f03c3389d1701f15d123f5d93b63a028fe398038 Mon Sep 17 00:00:00 2001 From: Piotr Rozyczko Date: Mon, 31 Oct 2022 21:30:21 +0100 Subject: [PATCH 12/44] Fix plot legend not updating on custom change. #2361 --- src/sas/qtgui/Plotting/Plotter.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sas/qtgui/Plotting/Plotter.py b/src/sas/qtgui/Plotting/Plotter.py index 18323efb33..9f539a7e83 100644 --- a/src/sas/qtgui/Plotting/Plotter.py +++ b/src/sas/qtgui/Plotting/Plotter.py @@ -693,7 +693,7 @@ def onModifyPlot(self, id): marker = selected_plot.symbol marker_size = selected_plot.markersize # plot name - legend = selected_plot.title + legend = selected_plot.name plotPropertiesWidget = PlotProperties(self, color=color, marker=marker, @@ -704,7 +704,7 @@ def onModifyPlot(self, id): selected_plot.markersize = plotPropertiesWidget.markersize() selected_plot.custom_color = plotPropertiesWidget.color() selected_plot.symbol = plotPropertiesWidget.marker() - selected_plot.title = plotPropertiesWidget.legend() + selected_plot.name = plotPropertiesWidget.legend() # Redraw the plot self.replacePlot(id, selected_plot) From 9e1eed0fdaba0f94a8e0b8370ffa48475bd16fb4 Mon Sep 17 00:00:00 2001 From: Piotr Rozyczko Date: Tue, 25 Oct 2022 17:05:00 +0200 Subject: [PATCH 13/44] Force maximize and be careful about the order of instantiation, so the fitting widget shows properly. #2272 --- src/sas/qtgui/MainWindow/GuiManager.py | 25 +++++++++++-------------- src/sas/qtgui/MainWindow/MainWindow.py | 5 ++--- 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/src/sas/qtgui/MainWindow/GuiManager.py b/src/sas/qtgui/MainWindow/GuiManager.py index dd93bd494d..f68a583cef 100644 --- a/src/sas/qtgui/MainWindow/GuiManager.py +++ b/src/sas/qtgui/MainWindow/GuiManager.py @@ -123,9 +123,18 @@ def addWidgets(self): """ Populate the main window with widgets """ + # Add the console window as another docked widget + self.logDockWidget = QDockWidget("Log Explorer", self._workspace) + self.logDockWidget.setObjectName("LogDockWidget") + self.logDockWidget.visibilityChanged.connect(self.updateLogContextMenus) + + + self.listWidget = QTextBrowser() + self.logDockWidget.setWidget(self.listWidget) + self._workspace.addDockWidget(Qt.BottomDockWidgetArea, self.logDockWidget) + # Preload all perspectives self.loadAllPerspectives() - # Add FileDialog widget as docked self.filesWidget = DataExplorerWindow(self._parent, self, manager=self._data_manager) ObjectLibrary.addObject('DataExplorer', self.filesWidget) @@ -133,23 +142,12 @@ def addWidgets(self): self.dockedFilesWidget = QDockWidget("Data Explorer", self._workspace) self.dockedFilesWidget.setFloating(False) self.dockedFilesWidget.setWidget(self.filesWidget) - # Modify menu items on widget visibility change self.dockedFilesWidget.visibilityChanged.connect(self.updateContextMenus) self._workspace.addDockWidget(Qt.LeftDockWidgetArea, self.dockedFilesWidget) self._workspace.resizeDocks([self.dockedFilesWidget], [305], Qt.Horizontal) - # Add the console window as another docked widget - self.logDockWidget = QDockWidget("Log Explorer", self._workspace) - self.logDockWidget.setObjectName("LogDockWidget") - self.logDockWidget.visibilityChanged.connect(self.updateLogContextMenus) - - - self.listWidget = QTextBrowser() - self.logDockWidget.setWidget(self.listWidget) - self._workspace.addDockWidget(Qt.BottomDockWidgetArea, self.logDockWidget) - # Add other, minor widgets self.ackWidget = Acknowledgements() self.aboutWidget = AboutBox() @@ -168,8 +166,7 @@ def addWidgets(self): self.results_frame.setVisible(False) self.results_panel.windowClosedSignal.connect(lambda: self.results_frame.setVisible(False)) - self._workspace.toolBar.setVisible(LocalConfig.TOOLBAR_SHOW) - self._workspace.actionHide_Toolbar.setText("Show Toolbar") + self._workspace.toolBar.setVisible(config.TOOLBAR_SHOW) # Add calculators - floating for usability self.SLDCalculator = SldPanel(self) diff --git a/src/sas/qtgui/MainWindow/MainWindow.py b/src/sas/qtgui/MainWindow/MainWindow.py index 8303a75d0d..50d2bff06b 100644 --- a/src/sas/qtgui/MainWindow/MainWindow.py +++ b/src/sas/qtgui/MainWindow/MainWindow.py @@ -10,7 +10,7 @@ from PyQt5.QtWidgets import QSplashScreen from PyQt5.QtWidgets import QApplication from PyQt5.QtGui import QPixmap -from PyQt5.QtCore import Qt +from PyQt5.QtCore import Qt, QTimer # Local UI from sas.qtgui.UI import main_resources_rc @@ -35,7 +35,7 @@ def __init__(self, screen_resolution, parent=None): self.screen_width = screen_resolution.width() self.screen_height = screen_resolution.height() self.setCentralWidget(self.workspace) - + QTimer.singleShot(100, self.showMaximized) # Temporary solution for problem with menubar on Mac if sys.platform == "darwin": # Mac self.menubar.setNativeMenuBar(False) @@ -110,7 +110,6 @@ def run_sasview(): # Show the main SV window mainwindow = MainSasViewWindow(screen_resolution) - mainwindow.showMaximized() # no more splash screen splash.finish(mainwindow) From 51293679c0a3a3acbb3dfaf481f661f6d55a0a51 Mon Sep 17 00:00:00 2001 From: Piotr Rozyczko Date: Thu, 27 Oct 2022 21:13:14 +0200 Subject: [PATCH 14/44] Set min width on squishable elements --- src/sas/qtgui/Calculators/UI/SldPanel.ui | 30 +++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/src/sas/qtgui/Calculators/UI/SldPanel.ui b/src/sas/qtgui/Calculators/UI/SldPanel.ui index e847c6af92..8686ad55ad 100644 --- a/src/sas/qtgui/Calculators/UI/SldPanel.ui +++ b/src/sas/qtgui/Calculators/UI/SldPanel.ui @@ -6,8 +6,8 @@ 0 0 - 490 - 446 + 552 + 495 @@ -308,10 +308,16 @@ + + + 0 + 0 + + 466 - 32 + 50 @@ -320,6 +326,12 @@ true + + + 0 + 28 + + Recalculate @@ -340,6 +352,12 @@ + + + 0 + 28 + + Close @@ -347,6 +365,12 @@ + + + 0 + 28 + + Help From 28f6051478954bf65ab4f1dccd3d8ad420fad7a4 Mon Sep 17 00:00:00 2001 From: Piotr Rozyczko Date: Thu, 22 Dec 2022 18:11:59 +0100 Subject: [PATCH 15/44] fix references to 'config' --- src/sas/qtgui/MainWindow/GuiManager.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sas/qtgui/MainWindow/GuiManager.py b/src/sas/qtgui/MainWindow/GuiManager.py index f68a583cef..76ad357bb9 100644 --- a/src/sas/qtgui/MainWindow/GuiManager.py +++ b/src/sas/qtgui/MainWindow/GuiManager.py @@ -166,7 +166,8 @@ def addWidgets(self): self.results_frame.setVisible(False) self.results_panel.windowClosedSignal.connect(lambda: self.results_frame.setVisible(False)) - self._workspace.toolBar.setVisible(config.TOOLBAR_SHOW) + custom_config = get_custom_config() + self._workspace.toolBar.setVisible(custom_config.TOOLBAR_SHOW) # Add calculators - floating for usability self.SLDCalculator = SldPanel(self) From b05058531608377f2ba4605d0d59d013c0650f32 Mon Sep 17 00:00:00 2001 From: krzywon Date: Tue, 1 Nov 2022 12:05:16 -0400 Subject: [PATCH 16/44] Close the bumps convergence (results) panel when data is deleted --- src/sas/qtgui/MainWindow/DataExplorer.py | 14 +++++++++++++- src/sas/qtgui/Utilities/ResultPanel.py | 15 ++++++++++++--- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/src/sas/qtgui/MainWindow/DataExplorer.py b/src/sas/qtgui/MainWindow/DataExplorer.py index 33c13aa93c..f2a59ea472 100644 --- a/src/sas/qtgui/MainWindow/DataExplorer.py +++ b/src/sas/qtgui/MainWindow/DataExplorer.py @@ -699,6 +699,11 @@ def deleteFile(self, event): # Delete corresponding open plots self.closePlotsForItem(item) + # Close result panel if results represent the deleted data item + # Results panel only stores Data1D/Data2D object + # => QStandardItems must still exist for direct comparison + self.closeResultPanelOnDelete(item) + self.model.removeRow(ind) # Decrement index since we just deleted it @@ -1894,7 +1899,14 @@ def closePlotsForItem(self, item): pass # debugger anchor - def onAnalysisUpdate(self, new_perspective=""): + def closeResultPanelOnDelete(self, items): + """ + Given a standard item, close the fitting results panel if currently populated with item + """ + # items - List[HashableStandardItem] of deleted data items + self.parent.results_panel.dataDeleted(items) + + def onAnalysisUpdate(self, new_perspective_name: str): """ Update the perspective combo index based on passed string """ diff --git a/src/sas/qtgui/Utilities/ResultPanel.py b/src/sas/qtgui/Utilities/ResultPanel.py index 5dee6fab2a..3a02889d85 100644 --- a/src/sas/qtgui/Utilities/ResultPanel.py +++ b/src/sas/qtgui/Utilities/ResultPanel.py @@ -9,7 +9,7 @@ from PyQt5 import QtGui from PyQt5 import QtWidgets -from bumps.dream.stats import var_stats, format_vars +from .GuiUtils import dataFromItem class ResultPanel(QtWidgets.QTabWidget): @@ -31,6 +31,7 @@ def __init__(self, parent, manager=None, *args, **kwargs): self.manager = manager self.communicator = self.manager.communicator() self.setMinimumSize(400, 400) + self.data_id = None self.updateBumps() # patch bumps ## TEMPORARY ## @@ -64,9 +65,10 @@ def onPlotResults(self, results, optimizer="Unknown"): self.removeTab(index) result = results[0][0] - filename = result.data.sas_data.filename + name = result.data.sas_data.name current_optimizer = optimizer - self.setWindowTitle(self.window_name + " - " + filename + " - " + current_optimizer) + self.data_id = result.data.sas_data.id + self.setWindowTitle(self.window_name + " - " + name + " - " + current_optimizer) if hasattr(result, 'convergence') and len(result.convergence) > 0: best, pop = result.convergence[:, 0], result.convergence[:, 1:] self.convergenceView.update(best, pop) @@ -95,6 +97,13 @@ def onPlotResults(self, results, optimizer="Unknown"): if self.count()==0: self.close() + def dataDeleted(self, item): + if not item or not self.isVisible(): + return + data = dataFromItem(item) + if data.id == self.data_id: + self.close() + def closeEvent(self, event): """ Overwrite QDialog close method to allow for custom widget close From ac00a94b6363fecbbd28d32713bfaac81b0ee6ed Mon Sep 17 00:00:00 2001 From: krzywon Date: Tue, 1 Nov 2022 12:15:26 -0400 Subject: [PATCH 17/44] Pass data object to result panel to limit imports --- src/sas/qtgui/MainWindow/DataExplorer.py | 7 +++---- src/sas/qtgui/Utilities/ResultPanel.py | 7 ++----- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/sas/qtgui/MainWindow/DataExplorer.py b/src/sas/qtgui/MainWindow/DataExplorer.py index f2a59ea472..7bd62cf8c6 100644 --- a/src/sas/qtgui/MainWindow/DataExplorer.py +++ b/src/sas/qtgui/MainWindow/DataExplorer.py @@ -702,8 +702,7 @@ def deleteFile(self, event): # Close result panel if results represent the deleted data item # Results panel only stores Data1D/Data2D object # => QStandardItems must still exist for direct comparison - self.closeResultPanelOnDelete(item) - + self.closeResultPanelOnDelete(GuiUtils.dataFromItem(item)) self.model.removeRow(ind) # Decrement index since we just deleted it @@ -1899,12 +1898,12 @@ def closePlotsForItem(self, item): pass # debugger anchor - def closeResultPanelOnDelete(self, items): + def closeResultPanelOnDelete(self, data): """ Given a standard item, close the fitting results panel if currently populated with item """ # items - List[HashableStandardItem] of deleted data items - self.parent.results_panel.dataDeleted(items) + self.parent.results_panel.dataDeleted(data) def onAnalysisUpdate(self, new_perspective_name: str): """ diff --git a/src/sas/qtgui/Utilities/ResultPanel.py b/src/sas/qtgui/Utilities/ResultPanel.py index 3a02889d85..81ff9e996d 100644 --- a/src/sas/qtgui/Utilities/ResultPanel.py +++ b/src/sas/qtgui/Utilities/ResultPanel.py @@ -9,8 +9,6 @@ from PyQt5 import QtGui from PyQt5 import QtWidgets -from .GuiUtils import dataFromItem - class ResultPanel(QtWidgets.QTabWidget): """ @@ -97,10 +95,9 @@ def onPlotResults(self, results, optimizer="Unknown"): if self.count()==0: self.close() - def dataDeleted(self, item): - if not item or not self.isVisible(): + def dataDeleted(self, data): + if not data or not self.isVisible(): return - data = dataFromItem(item) if data.id == self.data_id: self.close() From 60206eced266f10b19f64c3c92ce29b69ccde5c9 Mon Sep 17 00:00:00 2001 From: krzywon Date: Wed, 2 Nov 2022 09:09:26 -0400 Subject: [PATCH 18/44] Change dataDeleted to onDataDeleted for semantic reasons, and remove data from results panel when data deleted --- src/sas/qtgui/MainWindow/DataExplorer.py | 6 +++--- src/sas/qtgui/Utilities/ResultPanel.py | 26 ++++++++++++++++-------- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/sas/qtgui/MainWindow/DataExplorer.py b/src/sas/qtgui/MainWindow/DataExplorer.py index 7bd62cf8c6..e4cff872cf 100644 --- a/src/sas/qtgui/MainWindow/DataExplorer.py +++ b/src/sas/qtgui/MainWindow/DataExplorer.py @@ -1900,10 +1900,10 @@ def closePlotsForItem(self, item): def closeResultPanelOnDelete(self, data): """ - Given a standard item, close the fitting results panel if currently populated with item + Given a data1d/2d object, close the fitting results panel if currently populated with the data """ - # items - List[HashableStandardItem] of deleted data items - self.parent.results_panel.dataDeleted(data) + # data - Single data1d/2d object to be deleted + self.parent.results_panel.onDataDeleted(data) def onAnalysisUpdate(self, new_perspective_name: str): """ diff --git a/src/sas/qtgui/Utilities/ResultPanel.py b/src/sas/qtgui/Utilities/ResultPanel.py index 81ff9e996d..378c81244b 100644 --- a/src/sas/qtgui/Utilities/ResultPanel.py +++ b/src/sas/qtgui/Utilities/ResultPanel.py @@ -54,13 +54,9 @@ def updateBumps(self): sys.modules['bumps.gui.plot_view'] = PlotView def onPlotResults(self, results, optimizer="Unknown"): - # Clear up previous results - for view in (self.convergenceView, self.correlationView, - self.uncertaintyView, self.traceView): - view.close() - # close all tabs. REMEMBER TO USE REVERSED RANGE!!! - for index in reversed(range(self.count())): - self.removeTab(index) + # import moved here due to its cost + from bumps.dream.stats import var_stats, format_vars + self.clearAnyData() result = results[0][0] name = result.data.sas_data.name @@ -95,12 +91,26 @@ def onPlotResults(self, results, optimizer="Unknown"): if self.count()==0: self.close() - def dataDeleted(self, data): + def onDataDeleted(self, data): + """ Check if the data set is shown in the window and close tabs as needed. """ if not data or not self.isVisible(): return if data.id == self.data_id: + self.setWindowTitle(self.window_name) + self.clearAnyData() self.close() + def clearAnyData(self): + """ Clear any previous results and reset window to its base state. """ + self.data_id = None + # Clear up previous results + for view in (self.convergenceView, self.correlationView, + self.uncertaintyView, self.traceView): + view.close() + # close all tabs. REMEMBER TO USE REVERSED RANGE!!! + for index in reversed(range(self.count())): + self.removeTab(index) + def closeEvent(self, event): """ Overwrite QDialog close method to allow for custom widget close From 340459ba502d0d517bd54739ae5088a988056fed Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Fri, 28 Oct 2022 15:41:58 +0100 Subject: [PATCH 19/44] Fix for plotting not updating --- src/sas/qtgui/Perspectives/Fitting/FittingWidget.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sas/qtgui/Perspectives/Fitting/FittingWidget.py b/src/sas/qtgui/Perspectives/Fitting/FittingWidget.py index d77529bb06..a34b4ed40d 100644 --- a/src/sas/qtgui/Perspectives/Fitting/FittingWidget.py +++ b/src/sas/qtgui/Perspectives/Fitting/FittingWidget.py @@ -1879,7 +1879,7 @@ def prepareFitters(self, fitter=None, fit_id=0): # Data going in data = self.logic.data - model = copy.deepcopy(self.kernel_module) + model = self.kernel_module qmin = self.q_range_min qmax = self.q_range_max From 1126ac8d093491bc6643ca3b106edbe8970e5de0 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Fri, 28 Oct 2022 15:41:58 +0100 Subject: [PATCH 20/44] Fix for plotting not updating Co-Authored-By: caitwolf From 2d1ca121bb91746b469eb4f8b2a596fc26a2cd36 Mon Sep 17 00:00:00 2001 From: Caitlyn Wolf Date: Sat, 29 Oct 2022 12:28:19 +0200 Subject: [PATCH 21/44] Fixed bug in smearing implementation of sesans data that was passing x data in correlation space to the calculator which requires x data in q space --- .../qtgui/Perspectives/Fitting/ModelThread.py | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/sas/qtgui/Perspectives/Fitting/ModelThread.py b/src/sas/qtgui/Perspectives/Fitting/ModelThread.py index c93115f368..d8b97f1228 100644 --- a/src/sas/qtgui/Perspectives/Fitting/ModelThread.py +++ b/src/sas/qtgui/Perspectives/Fitting/ModelThread.py @@ -171,10 +171,20 @@ def compute(self): unsmeared_error = None ##smearer the ouput of the plot if self.smearer is not None: - first_bin, last_bin = self.smearer.get_bin_range(self.qmin, - self.qmax) - mask = self.data.x[first_bin:last_bin+1] - unsmeared_output = numpy.zeros((len(self.data.x))) + if self.data.isSesans: + # For SESANS, data.x, qmin and qmax, and therefore get_bin_range are in correlation space, and + # the Hankel transform from q space to correlation space is set up as a resolution function, i.e., + # the "unsmeared" data is in q space and the "smeared" data is in correlation space. + # Therefore, q_calc needs to be used here to calculate the unsmeared_out rather than data.x. + mask = self.smearer.resolution.q_calc + first_bin = 0 + last_bin = len(mask) + unsmeared_output = numpy.zeros((len(mask))) + else: + first_bin, last_bin = self.smearer.get_bin_range(self.qmin, + self.qmax) + mask = self.data.x[first_bin:last_bin+1] + unsmeared_output = numpy.zeros((len(self.data.x))) return_data = self.model.calculate_Iq(mask) if isinstance(return_data, tuple): @@ -183,11 +193,11 @@ def compute(self): return_data, intermediate_results = return_data unsmeared_output[first_bin:last_bin+1] = return_data output = self.smearer(unsmeared_output, first_bin, last_bin) - # Rescale data to unsmeared model # Check that the arrays are compatible. If we only have a model but no data, # the length of data.y will be zero. - if isinstance(self.data.y, numpy.ndarray) and output.shape == self.data.y.shape: + # does not apply to SESANS where Hankel was implemented as resolution function + if isinstance(self.data.y, numpy.ndarray) and output.shape == self.data.y.shape and not self.data.isSesans: unsmeared_data = numpy.zeros((len(self.data.x))) unsmeared_error = numpy.zeros((len(self.data.x))) unsmeared_data[first_bin:last_bin+1] = self.data.y[first_bin:last_bin+1]\ From eb213feb790200f5f003a32e777f70ad2f4edf03 Mon Sep 17 00:00:00 2001 From: Caitlyn Wolf Date: Sat, 29 Oct 2022 14:47:04 +0200 Subject: [PATCH 22/44] minor text changes in comment for clarity --- src/sas/qtgui/Perspectives/Fitting/ModelThread.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sas/qtgui/Perspectives/Fitting/ModelThread.py b/src/sas/qtgui/Perspectives/Fitting/ModelThread.py index d8b97f1228..104777bf71 100644 --- a/src/sas/qtgui/Perspectives/Fitting/ModelThread.py +++ b/src/sas/qtgui/Perspectives/Fitting/ModelThread.py @@ -172,9 +172,9 @@ def compute(self): ##smearer the ouput of the plot if self.smearer is not None: if self.data.isSesans: - # For SESANS, data.x, qmin and qmax, and therefore get_bin_range are in correlation space, and - # the Hankel transform from q space to correlation space is set up as a resolution function, i.e., - # the "unsmeared" data is in q space and the "smeared" data is in correlation space. + # For SESANS, data.x, qmin and qmax, and therefore get_bin_range are in real space, and + # the Hankel transform from q space to real space is set up as a resolution function, i.e., + # the "unsmeared" data is in q space and the "smeared" data is in real space. # Therefore, q_calc needs to be used here to calculate the unsmeared_out rather than data.x. mask = self.smearer.resolution.q_calc first_bin = 0 From c42a7dfb20ddb9b5a3c48001044111a837519d9d Mon Sep 17 00:00:00 2001 From: Caitlyn Wolf Date: Sun, 30 Oct 2022 16:45:46 +0100 Subject: [PATCH 23/44] fixed bug that was swtiching the Use dQ Data slit length and width --- src/sas/qtgui/Perspectives/Fitting/FittingWidget.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sas/qtgui/Perspectives/Fitting/FittingWidget.py b/src/sas/qtgui/Perspectives/Fitting/FittingWidget.py index a34b4ed40d..cd1f5468f3 100644 --- a/src/sas/qtgui/Perspectives/Fitting/FittingWidget.py +++ b/src/sas/qtgui/Perspectives/Fitting/FittingWidget.py @@ -4207,12 +4207,12 @@ def updatePageWithParameters(self, line_dict, warn_user=True): pass if 'smearing_min' in line_dict.keys(): try: - self.smearing_widget.dq_l = float(line_dict['smearing_min'][0]) + self.smearing_widget.dq_r = float(line_dict['smearing_min'][0]) except ValueError: pass if 'smearing_max' in line_dict.keys(): try: - self.smearing_widget.dq_r = float(line_dict['smearing_max'][0]) + self.smearing_widget.dq_l = float(line_dict['smearing_max'][0]) except ValueError: pass From e7adcded2696d377d1590a1cbf6b2d6d71d21f93 Mon Sep 17 00:00:00 2001 From: Caitlyn Wolf Date: Sun, 30 Oct 2022 18:29:32 +0100 Subject: [PATCH 24/44] fixed sesans residuals plots to show in real space rather than q space --- src/sas/qtgui/Perspectives/Fitting/FittingUtilities.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/sas/qtgui/Perspectives/Fitting/FittingUtilities.py b/src/sas/qtgui/Perspectives/Fitting/FittingUtilities.py index 6166bbf713..985663293a 100644 --- a/src/sas/qtgui/Perspectives/Fitting/FittingUtilities.py +++ b/src/sas/qtgui/Perspectives/Fitting/FittingUtilities.py @@ -547,8 +547,12 @@ def residualsData1D(reference_data, current_data, weights): residuals.dxl = None residuals.dxw = None residuals.ytransform = 'y' + if reference_data.isSesans: + residuals.xtransform = 'x' + residuals.xaxis('\\rm{z} ', 'A') # For latter scale changes - residuals.xaxis('\\rm{Q} ', 'A^{-1}') + else: + residuals.xaxis('\\rm{Q} ', 'A^{-1}') residuals.yaxis('\\rm{Residuals} ', 'normalized') return residuals From 4434dc67f360b7e831f47463c02aaabf252f782a Mon Sep 17 00:00:00 2001 From: Wojciech Potrzebowski Date: Tue, 1 Nov 2022 16:52:52 +0100 Subject: [PATCH 25/44] Reverting read-only for Qmin and Qmax in invariant params --- .../Invariant/UnitTesting/InvariantPerspectiveTest.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/sas/qtgui/Perspectives/Invariant/UnitTesting/InvariantPerspectiveTest.py b/src/sas/qtgui/Perspectives/Invariant/UnitTesting/InvariantPerspectiveTest.py index 29794bfafd..06c1254a4a 100644 --- a/src/sas/qtgui/Perspectives/Invariant/UnitTesting/InvariantPerspectiveTest.py +++ b/src/sas/qtgui/Perspectives/Invariant/UnitTesting/InvariantPerspectiveTest.py @@ -452,6 +452,9 @@ def checkFakeDataState(self): self.assertFalse(self.widget.txtNptsLowQ.isReadOnly()) self.assertFalse(self.widget.txtNptsHighQ.isReadOnly()) + self.assertTrue(self.widget.txtTotalQMin.isReadOnly()) + self.assertTrue(self.widget.txtTotalQMax.isReadOnly()) + # content of line edits self.assertEqual(self.widget.txtName.text(), 'data') self.assertEqual(self.widget.txtTotalQMin.text(), '0.009') From b8548f78797ab9c7b85fcdc5e4aa01f0e893f6b2 Mon Sep 17 00:00:00 2001 From: Jordan_E_Berger Date: Fri, 21 May 2021 15:58:40 -0400 Subject: [PATCH 26/44] removes error bars, previously set to one, from normalized residuals --- src/sas/qtgui/Perspectives/Fitting/FittingUtilities.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sas/qtgui/Perspectives/Fitting/FittingUtilities.py b/src/sas/qtgui/Perspectives/Fitting/FittingUtilities.py index 6166bbf713..1551db783e 100644 --- a/src/sas/qtgui/Perspectives/Fitting/FittingUtilities.py +++ b/src/sas/qtgui/Perspectives/Fitting/FittingUtilities.py @@ -542,7 +542,7 @@ def residualsData1D(reference_data, current_data, weights): pass residuals.x = current_data.x[index][0] - residuals.dy = numpy.ones(len(residuals.y)) + residuals.dy = None residuals.dx = None residuals.dxl = None residuals.dxw = None @@ -572,7 +572,7 @@ def residualsData2D(reference_data, current_data, weight): residuals.qx_data = current_data.qx_data residuals.qy_data = current_data.qy_data residuals.q_data = current_data.q_data - residuals.err_data = numpy.ones(len(residuals.data)) + residuals.err_data = None residuals.xmin = min(residuals.qx_data) residuals.xmax = max(residuals.qx_data) residuals.ymin = min(residuals.qy_data) From dfd02088d476d52b5d07416c95e482957488264e Mon Sep 17 00:00:00 2001 From: butlerpd Date: Sun, 30 Oct 2022 15:21:04 +0000 Subject: [PATCH 27/44] up default points on slicer and fix a few related slicer bugs the slicers defaulted to 20 or 36 points (depending on the slicer). This is almost never a reasonable number and there have been several request for that to be made larger. Upped it to 100 here. Noted in testing that the box slicer validators were not properly checking for 0 points (the other slicers did) . In fact they were do nothing functions so fixed that by adding the appropriate code snipet. Also all the slicers seemed to have the default set in a couple of different places which caused some odd behavior in at least on case. that was cleaned up as well. --- .../qtgui/Plotting/Slicers/AnnulusSlicer.py | 10 +++++---- .../qtgui/Plotting/Slicers/AzimutSlicer.py | 2 +- src/sas/qtgui/Plotting/Slicers/BoxSlicer.py | 21 ++++++++++++++++--- .../qtgui/Plotting/Slicers/SectorSlicer.py | 4 ++-- 4 files changed, 27 insertions(+), 10 deletions(-) diff --git a/src/sas/qtgui/Plotting/Slicers/AnnulusSlicer.py b/src/sas/qtgui/Plotting/Slicers/AnnulusSlicer.py index cca448235d..06ba3cb003 100644 --- a/src/sas/qtgui/Plotting/Slicers/AnnulusSlicer.py +++ b/src/sas/qtgui/Plotting/Slicers/AnnulusSlicer.py @@ -28,7 +28,7 @@ def __init__(self, base, axes, item=None, color='black', zorder=3): self.connect = self.base.connect # Number of points on the plot - self.nbins = 36 + self.nbins = 100 # Cursor position of Rings (Left(-1) or Right(1)) self.xmaxd = self.data.xmax self.xmind = self.data.xmin @@ -104,9 +104,11 @@ def _post_data(self, nbins=None): numpy.fabs(self.outer_circle.get_radius())) # If the user does not specify the numbers of points to plot # the default number will be nbins= 36 - if nbins is None: - self.nbins = 36 - else: +# if nbins is None: +# self.nbins = 20 +# else: +# self.nbins = nbins + if nbins is not None: self.nbins = nbins # Create the data1D Q average of data2D sect = Ring(r_min=rmin, r_max=rmax, nbins=self.nbins) diff --git a/src/sas/qtgui/Plotting/Slicers/AzimutSlicer.py b/src/sas/qtgui/Plotting/Slicers/AzimutSlicer.py index 9f8e8fbb58..dc58315657 100644 --- a/src/sas/qtgui/Plotting/Slicers/AzimutSlicer.py +++ b/src/sas/qtgui/Plotting/Slicers/AzimutSlicer.py @@ -22,7 +22,7 @@ def __init__(self, base, axes, color='black', zorder=3): self.connect = self.base.connect # # Number of points on the plot - self.nbins = 20 + self.nbins = 100 theta1 = 2 * np.pi / 3 theta2 = -2 * np.pi / 3 diff --git a/src/sas/qtgui/Plotting/Slicers/BoxSlicer.py b/src/sas/qtgui/Plotting/Slicers/BoxSlicer.py index 72d9929c16..6028ce6c88 100644 --- a/src/sas/qtgui/Plotting/Slicers/BoxSlicer.py +++ b/src/sas/qtgui/Plotting/Slicers/BoxSlicer.py @@ -31,7 +31,7 @@ def __init__(self, base, axes, item=None, color='black', zorder=3): self.qmax = max(self.data.xmax, self.data.xmin, self.data.ymax, self.data.ymin) # Number of points on the plot - self.nbins = 30 + self.nbins = 100 # If True, I(|Q|) will be return, otherwise, # negative q-values are allowed self.fold = True @@ -494,7 +494,14 @@ def validate(self, param_name, param_value): Validate input from user. Values get checked at apply time. """ - return True + isValid = True + + if param_name == 'nbins': + # Can't be 0 + if param_value < 1: + print("Number of bins cannot be less than or equal to 0. Please adjust.") + isValid = False + return isValid class BoxInteractorY(BoxInteractor): @@ -518,4 +525,12 @@ def validate(self, param_name, param_value): Validate input from user Values get checked at apply time. """ - return True + isValid = True + + if param_name == 'nbins': + # Can't be 0 + if param_value < 1: + print("Number of bins cannot be less than or equal to 0. Please adjust.") + isValid = False + return isValid + \ No newline at end of file diff --git a/src/sas/qtgui/Plotting/Slicers/SectorSlicer.py b/src/sas/qtgui/Plotting/Slicers/SectorSlicer.py index 27ec99f2ff..10c847348d 100644 --- a/src/sas/qtgui/Plotting/Slicers/SectorSlicer.py +++ b/src/sas/qtgui/Plotting/Slicers/SectorSlicer.py @@ -34,7 +34,7 @@ def __init__(self, base, axes, item=None, color='black', zorder=3): numpy.fabs(self.data.ymin)), 2) self.qmax = numpy.sqrt(x + y) # Number of points on the plot - self.nbins = 20 + self.nbins = 100 # Angle of the middle line self.theta2 = numpy.pi / 3 # Absolute value of the Angle between the middle line and any side line @@ -133,7 +133,7 @@ def _post_data(self, nbins=None): phimin = -self.left_line.phi + self.main_line.theta phimax = self.left_line.phi + self.main_line.theta if nbins is None: - nbins = 20 + nbins = self.nbins sect = SectorQ(r_min=0.0, r_max=radius, phi_min=phimin + numpy.pi, phi_max=phimax + numpy.pi, nbins=nbins) From 2e79d6ba5617aaa8c031db559fcb8baf6f56b652 Mon Sep 17 00:00:00 2001 From: butlerpd Date: Mon, 31 Oct 2022 09:39:06 +0000 Subject: [PATCH 28/44] Change polydispersity plot to linear linear --- src/sas/qtgui/Perspectives/Fitting/FittingUtilities.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sas/qtgui/Perspectives/Fitting/FittingUtilities.py b/src/sas/qtgui/Perspectives/Fitting/FittingUtilities.py index 1551db783e..581ff7baf1 100644 --- a/src/sas/qtgui/Perspectives/Fitting/FittingUtilities.py +++ b/src/sas/qtgui/Perspectives/Fitting/FittingUtilities.py @@ -632,6 +632,8 @@ def plotPolydispersities(model): # similar to FittingLogic._create1DPlot() but different data/axes data1d = Data1D(x=xarr, y=yarr) xunit = model.details[name][0] + data1d.xtransform = 'x' + data1d.ytransform = 'y' data1d.xaxis(r'\rm{{{}}}'.format(name.replace('_', '\_')), xunit) data1d.yaxis(r'\rm{probability}', 'normalized') data1d.scale = 'linear' From 557acbfd62ed73be7b8ca3836388da02ed53d0b3 Mon Sep 17 00:00:00 2001 From: Piotr Rozyczko Date: Mon, 31 Oct 2022 11:05:36 +0100 Subject: [PATCH 29/44] Enable plot redraw on linear/log scale change Modified initial state to be log scale --- src/sas/qtgui/Perspectives/Fitting/FittingWidget.py | 3 ++- src/sas/qtgui/Perspectives/Fitting/OptionsWidget.py | 8 +++----- src/sas/qtgui/Perspectives/Fitting/UI/OptionsWidgetUI.ui | 3 +++ 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/sas/qtgui/Perspectives/Fitting/FittingWidget.py b/src/sas/qtgui/Perspectives/Fitting/FittingWidget.py index d77529bb06..ae2d9a2dd0 100644 --- a/src/sas/qtgui/Perspectives/Fitting/FittingWidget.py +++ b/src/sas/qtgui/Perspectives/Fitting/FittingWidget.py @@ -264,7 +264,7 @@ def initializeGlobals(self): self.q_range_min = OptionsWidget.QMIN_DEFAULT self.q_range_max = OptionsWidget.QMAX_DEFAULT self.npts = OptionsWidget.NPTS_DEFAULT - self.log_points = False + self.log_points = True self.weighting = 0 self.chi2 = None # Does the control support UNDO/REDO @@ -2252,6 +2252,7 @@ def onOptionsUpdate(self): # set Q range labels on the main tab self.lblMinRangeDef.setText(GuiUtils.formatNumber(self.q_range_min, high=True)) self.lblMaxRangeDef.setText(GuiUtils.formatNumber(self.q_range_max, high=True)) + self.recalculatePlotData() def setDefaultStructureCombo(self): """ diff --git a/src/sas/qtgui/Perspectives/Fitting/OptionsWidget.py b/src/sas/qtgui/Perspectives/Fitting/OptionsWidget.py index 0d69997413..5e21f6126c 100644 --- a/src/sas/qtgui/Perspectives/Fitting/OptionsWidget.py +++ b/src/sas/qtgui/Perspectives/Fitting/OptionsWidget.py @@ -40,8 +40,7 @@ class OptionsWidget(QtWidgets.QWidget, Ui_tabOptions): 'MIN_RANGE', 'MAX_RANGE', 'NPTS', - 'NPTS_FIT', - 'LOG_SPACED'] + 'NPTS_FIT'] def __init__(self, parent=None, logic=None): super(OptionsWidget, self).__init__() @@ -125,13 +124,12 @@ def initMapper(self): self.mapper.addMapping(self.txtMaxRange, self.MODEL.index('MAX_RANGE')) self.mapper.addMapping(self.txtNpts, self.MODEL.index('NPTS')) self.mapper.addMapping(self.txtNptsFit, self.MODEL.index('NPTS_FIT')) - self.mapper.addMapping(self.chkLogData, self.MODEL.index('LOG_SPACED')) self.mapper.toFirst() def toggleLogData(self, isChecked): """ Toggles between log and linear data sets """ - pass + self.plot_signal.emit() def onMaskEdit(self): """ @@ -256,7 +254,7 @@ def state(self): q_range_max = float(self.model.item(self.MODEL.index('MAX_RANGE')).text()) npts = int(self.model.item(self.MODEL.index('NPTS')).text()) npts_fit = int(self.model.item(self.MODEL.index('NPTS_FIT')).text()) - log_points = str(self.model.item(self.MODEL.index('LOG_SPACED')).text()) == 'true' + log_points = self.chkLogData.isChecked() return (q_range_min, q_range_max, npts, log_points, self.weighting) diff --git a/src/sas/qtgui/Perspectives/Fitting/UI/OptionsWidgetUI.ui b/src/sas/qtgui/Perspectives/Fitting/UI/OptionsWidgetUI.ui index e2f9cdf67c..9e6bfe60cc 100755 --- a/src/sas/qtgui/Perspectives/Fitting/UI/OptionsWidgetUI.ui +++ b/src/sas/qtgui/Perspectives/Fitting/UI/OptionsWidgetUI.ui @@ -164,6 +164,9 @@ Log spaced points + + true + From 1b1e80cc212d4004f64f11d1613fdf89e5cb0962 Mon Sep 17 00:00:00 2001 From: butlerpd Date: Mon, 31 Oct 2022 11:27:25 +0000 Subject: [PATCH 30/44] Change defaults for theory plot. from 50 to 150 points and default to log spacing of data --- src/sas/qtgui/Perspectives/Fitting/FittingWidget.py | 2 ++ src/sas/qtgui/Perspectives/Fitting/OptionsWidget.py | 6 ++++-- src/sas/qtgui/Plotting/Slicers/BoxSlicer.py | 1 - 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/sas/qtgui/Perspectives/Fitting/FittingWidget.py b/src/sas/qtgui/Perspectives/Fitting/FittingWidget.py index ae2d9a2dd0..857532d491 100644 --- a/src/sas/qtgui/Perspectives/Fitting/FittingWidget.py +++ b/src/sas/qtgui/Perspectives/Fitting/FittingWidget.py @@ -267,6 +267,7 @@ def initializeGlobals(self): self.log_points = True self.weighting = 0 self.chi2 = None + # Does the control support UNDO/REDO # temporarily off self.undo_supported = False @@ -330,6 +331,7 @@ def initializeWidgets(self): self.options_widget = OptionsWidget(self, self.logic) layout.addWidget(self.options_widget) self.tabOptions.setLayout(layout) + self.options_widget.setLogScale(self.log_points) # Smearing widget layout = QtWidgets.QGridLayout() diff --git a/src/sas/qtgui/Perspectives/Fitting/OptionsWidget.py b/src/sas/qtgui/Perspectives/Fitting/OptionsWidget.py index 5e21f6126c..7c95b95774 100644 --- a/src/sas/qtgui/Perspectives/Fitting/OptionsWidget.py +++ b/src/sas/qtgui/Perspectives/Fitting/OptionsWidget.py @@ -35,7 +35,7 @@ class OptionsWidget(QtWidgets.QWidget, Ui_tabOptions): plot_signal = QtCore.pyqtSignal() QMIN_DEFAULT = 0.0005 QMAX_DEFAULT = 0.5 - NPTS_DEFAULT = 50 + NPTS_DEFAULT = 150 MODEL = [ 'MIN_RANGE', 'MAX_RANGE', @@ -127,6 +127,9 @@ def initMapper(self): self.mapper.toFirst() + def setLogScale(self, log_scale): + self.chkLogData.setChecked(log_scale) + def toggleLogData(self, isChecked): """ Toggles between log and linear data sets """ self.plot_signal.emit() @@ -255,7 +258,6 @@ def state(self): npts = int(self.model.item(self.MODEL.index('NPTS')).text()) npts_fit = int(self.model.item(self.MODEL.index('NPTS_FIT')).text()) log_points = self.chkLogData.isChecked() - return (q_range_min, q_range_max, npts, log_points, self.weighting) def npts2fit(self, data=None, qmin=None, qmax=None, npts=None): diff --git a/src/sas/qtgui/Plotting/Slicers/BoxSlicer.py b/src/sas/qtgui/Plotting/Slicers/BoxSlicer.py index 6028ce6c88..96c9dcf4d0 100644 --- a/src/sas/qtgui/Plotting/Slicers/BoxSlicer.py +++ b/src/sas/qtgui/Plotting/Slicers/BoxSlicer.py @@ -533,4 +533,3 @@ def validate(self, param_name, param_value): print("Number of bins cannot be less than or equal to 0. Please adjust.") isValid = False return isValid - \ No newline at end of file From f75521b6937a7d02ade52ae0f7116425cd650379 Mon Sep 17 00:00:00 2001 From: butlerpd Date: Mon, 31 Oct 2022 15:36:24 +0000 Subject: [PATCH 31/44] Fix issue with residual y scaling called twice. Keep it in the residuals creation and delete from residuals plotting --- src/sas/qtgui/Perspectives/Fitting/FittingUtilities.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sas/qtgui/Perspectives/Fitting/FittingUtilities.py b/src/sas/qtgui/Perspectives/Fitting/FittingUtilities.py index 581ff7baf1..d5fbdb906d 100644 --- a/src/sas/qtgui/Perspectives/Fitting/FittingUtilities.py +++ b/src/sas/qtgui/Perspectives/Fitting/FittingUtilities.py @@ -604,7 +604,6 @@ def plotResiduals(reference_data, current_data, weights): res_name = reference_data.name if reference_data.name else reference_data.filename residuals.name = "Residuals for " + str(theory_name) + "[" + res_name + "]" residuals.title = residuals.name - residuals.ytransform = 'y' # when 2 data have the same id override the 1 st plotted # include the last part if keeping charts for separate models is required From e8f643125eab8310e9160b350ed374ef22367329 Mon Sep 17 00:00:00 2001 From: butlerpd Date: Mon, 31 Oct 2022 21:07:05 +0000 Subject: [PATCH 32/44] Cleaned up incorrect comment lines after review --- src/sas/qtgui/Plotting/Slicers/AnnulusSlicer.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/sas/qtgui/Plotting/Slicers/AnnulusSlicer.py b/src/sas/qtgui/Plotting/Slicers/AnnulusSlicer.py index 06ba3cb003..6a82a929c7 100644 --- a/src/sas/qtgui/Plotting/Slicers/AnnulusSlicer.py +++ b/src/sas/qtgui/Plotting/Slicers/AnnulusSlicer.py @@ -102,12 +102,6 @@ def _post_data(self, nbins=None): numpy.fabs(self.outer_circle.get_radius())) rmax = max(numpy.fabs(self.inner_circle.get_radius()), numpy.fabs(self.outer_circle.get_radius())) - # If the user does not specify the numbers of points to plot - # the default number will be nbins= 36 -# if nbins is None: -# self.nbins = 20 -# else: -# self.nbins = nbins if nbins is not None: self.nbins = nbins # Create the data1D Q average of data2D From 13f19cbb81ab68d493e3ca47e757486e2509f4d1 Mon Sep 17 00:00:00 2001 From: Piotr Rozyczko Date: Tue, 25 Oct 2022 17:51:25 +0200 Subject: [PATCH 33/44] A few more or less obvious fixes for speed of SasView startup --- src/sas/logger_config.py | 2 ++ src/sas/qtgui/Utilities/ReportDialog.py | 3 ++- src/sas/qtgui/Utilities/ResultPanel.py | 1 - src/sas/sascalc/calculator/sas_gen.py | 3 +-- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/sas/logger_config.py b/src/sas/logger_config.py index 8a182c2b43..03eb004dc9 100644 --- a/src/sas/logger_config.py +++ b/src/sas/logger_config.py @@ -31,6 +31,7 @@ def config_production(self): logging.captureWarnings(True) logger = logging.getLogger(self.name) logging.getLogger('matplotlib').setLevel(logging.WARN) + logging.getLogger('numba').setLevel(logging.WARN) return logger def config_development(self): @@ -42,6 +43,7 @@ def config_development(self): logging.captureWarnings(True) self._disable_debug_from_config() logging.getLogger('matplotlib').setLevel(logging.WARN) + logging.getLogger('numba').setLevel(logging.WARN) return logger def _disable_debug_from_config(self): diff --git a/src/sas/qtgui/Utilities/ReportDialog.py b/src/sas/qtgui/Utilities/ReportDialog.py index 4998b29ba8..e6154607ef 100644 --- a/src/sas/qtgui/Utilities/ReportDialog.py +++ b/src/sas/qtgui/Utilities/ReportDialog.py @@ -3,7 +3,6 @@ import re import logging import traceback -from xhtml2pdf import pisa from PyQt5 import QtWidgets, QtCore from PyQt5 import QtPrintSupport @@ -187,6 +186,8 @@ def HTML2PDF(data, filename): : data: html string : filename: name of file to be saved """ + # import moved from top due to cost + from xhtml2pdf import pisa try: # open output file for writing (truncated binary) with open(filename, "w+b") as resultFile: diff --git a/src/sas/qtgui/Utilities/ResultPanel.py b/src/sas/qtgui/Utilities/ResultPanel.py index 378c81244b..80230e02a9 100644 --- a/src/sas/qtgui/Utilities/ResultPanel.py +++ b/src/sas/qtgui/Utilities/ResultPanel.py @@ -9,7 +9,6 @@ from PyQt5 import QtGui from PyQt5 import QtWidgets - class ResultPanel(QtWidgets.QTabWidget): """ FitPanel class contains fields allowing to fit models and data diff --git a/src/sas/sascalc/calculator/sas_gen.py b/src/sas/sascalc/calculator/sas_gen.py index 2ed4351651..c9535e6884 100644 --- a/src/sas/sascalc/calculator/sas_gen.py +++ b/src/sas/sascalc/calculator/sas_gen.py @@ -15,8 +15,6 @@ from scipy.spatial.transform import Rotation from periodictable import formula, nsf -from .geni import Iq, Iqxy - if sys.version_info[0] < 3: def decode(s): return s @@ -179,6 +177,7 @@ def calculate_Iq(self, qx, qy=None): :Param y: array of y-values :return: function value """ + from .geni import Iq, Iqxy # transform position data from sample to beamline coords x, y, z = self.transform_positions() sld = self.data_sldn - self.params['solvent_SLD'] From 49a1c36b45347f29386706c5e49054a8ef077fd0 Mon Sep 17 00:00:00 2001 From: Piotr Rozyczko Date: Wed, 26 Oct 2022 10:06:29 +0200 Subject: [PATCH 34/44] optimize imports in the inversion calculator --- src/sas/sascalc/pr/p_invertor.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/sas/sascalc/pr/p_invertor.py b/src/sas/sascalc/pr/p_invertor.py index de72132ca6..78e380be4d 100644 --- a/src/sas/sascalc/pr/p_invertor.py +++ b/src/sas/sascalc/pr/p_invertor.py @@ -9,7 +9,6 @@ import numpy as np -from . import calc logger = logging.getLogger(__name__) @@ -50,6 +49,7 @@ def residuals(self, pars): :param pars: input parameters. :return: residuals - list of residuals. """ + from . import calc pars = np.float64(pars) residuals = [] @@ -66,6 +66,7 @@ def pr_residuals(self, pars): :param pars: input parameters. :return: residuals - list of residuals. """ + from . import calc pars = np.float64(pars) residuals = [] @@ -329,6 +330,7 @@ def iq(self, pars, q): :return: I(q) """ + from . import calc q = np.float64(q) pars = np.float64(pars) pars = np.atleast_1d(pars) @@ -349,6 +351,7 @@ def get_iq_smeared(self, pars, q): :return: I(q), either scalar or vector depending on q. """ + from . import calc q = np.float64(q) q = np.atleast_1d(q) pars = np.float64(pars) @@ -370,6 +373,7 @@ def pr(self, pars, r): :return: P(r) """ + from . import calc r = np.float64(r) pars = np.float64(pars) pars = np.atleast_1d(pars) @@ -390,7 +394,7 @@ def get_pr_err(self, pars, pars_err, r): :return: (P(r), dP(r)) """ - + from . import calc pars = np.atleast_1d(np.float64(pars)) r = np.atleast_1d(np.float64(r)) @@ -421,6 +425,7 @@ def basefunc_ft(self, d_max, n, q): :return: nth Fourier transformed base function, evaluated at q. """ + from . import calc d_max = np.float64(d_max) n = int(n) q = np.float64(q) @@ -441,6 +446,7 @@ def oscillations(self, pars): :param pars: c-parameters. :return: oscillation figure of merit. """ + from . import calc nslice = 100 pars = np.float64(pars) pars = np.atleast_1d(pars) @@ -459,6 +465,7 @@ def get_peaks(self, pars): :param pars: c-parameters. :return: number of P(r) peaks. """ + from . import calc nslice = 100 pars = np.float64(pars) count = calc.npeaks(pars, self.d_max, nslice) @@ -473,6 +480,7 @@ def get_positive(self, pars): :param pars: c-parameters. :return: fraction of P(r) that is positive. """ + from . import calc nslice = 100 pars = np.float64(pars) pars = np.atleast_1d(pars) @@ -490,6 +498,7 @@ def get_pos_err(self, pars, pars_err): :return: fraction of P(r) that is positive. """ + from . import calc nslice = 51 pars = np.float64(pars) pars = np.atleast_1d(pars) @@ -505,6 +514,7 @@ def rg(self, pars): :param pars: c-parameters. :return: Rg. """ + from . import calc nslice = 101 pars = np.float64(pars) pars = np.atleast_1d(pars) @@ -519,6 +529,7 @@ def iq0(self, pars): :param pars: c-parameters. :return: I(q=0) """ + from . import calc nslice = 101 pars = np.float64(pars) pars = np.atleast_1d(pars) @@ -550,6 +561,7 @@ def _get_matrix(self, nfunc, nr): :return: 0 """ + from . import calc nfunc = int(nfunc) nr = int(nr) a_obj = np.zeros([self.npoints + nr, nfunc]) From cec67015d68b125b7c9e835e82418f9faf87f19e Mon Sep 17 00:00:00 2001 From: krzywon Date: Wed, 18 Jan 2023 10:04:12 -0500 Subject: [PATCH 35/44] Clip fitting values set outside the fit range --- src/sas/qtgui/Perspectives/Fitting/FittingWidget.py | 3 +++ src/sas/sascalc/fit/BumpsFitting.py | 7 +++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/sas/qtgui/Perspectives/Fitting/FittingWidget.py b/src/sas/qtgui/Perspectives/Fitting/FittingWidget.py index 5e9b372ad0..658d40cfe6 100644 --- a/src/sas/qtgui/Perspectives/Fitting/FittingWidget.py +++ b/src/sas/qtgui/Perspectives/Fitting/FittingWidget.py @@ -1807,6 +1807,9 @@ def paramDictFromResults(self, results): logger.error(msg) return + if results.mesg: + logger.warning(results.mesg) + param_list = results.param_list # ['radius', 'radius.width'] param_values = results.pvec # array([ 0.36221662, 0.0146783 ]) param_stderr = results.stderr # array([ 1.71293015, 1.71294233]) diff --git a/src/sas/sascalc/fit/BumpsFitting.py b/src/sas/sascalc/fit/BumpsFitting.py index 3cebf45e73..4a812b36b5 100644 --- a/src/sas/sascalc/fit/BumpsFitting.py +++ b/src/sas/sascalc/fit/BumpsFitting.py @@ -417,6 +417,7 @@ def abort_test(): return True return False + errors = [] fitclass, options = get_fitter() steps = options.get('steps', 0) if steps == 0: @@ -432,16 +433,18 @@ def abort_test(): ] fitdriver = fitters.FitDriver(fitclass, problem=problem, abort_test=abort_test, **options) + clipped = fitdriver.clip() + if clipped: + errors.append(f"The initial value for {clipped} was outside the fitting range and was coerced.") omp_threads = int(os.environ.get('OMP_NUM_THREADS', '0')) mapper = MPMapper if omp_threads == 1 else SerialMapper fitdriver.mapper = mapper.start_mapper(problem, None) #import time; T0 = time.time() try: best, fbest = fitdriver.fit() - errors = [] except Exception as exc: best, fbest = None, np.NaN - errors = [str(exc), traceback.format_exc()] + errors.extend([str(exc), traceback.format_exc()]) finally: mapper.stop_mapper(fitdriver.mapper) From 405f51e1ea9e2d4ae2642aa27ee63b0023de2a58 Mon Sep 17 00:00:00 2001 From: Stuart Prescott Date: Sun, 25 Sep 2022 05:18:40 +0000 Subject: [PATCH 36/44] Fix incorrect escape sequences Embedded latex ends up with lots of backslashes that need to be protected as raw strings rather than (mis)interpreted as standard C-style escape sequences. --- src/sas/qtgui/Utilities/GuiUtils.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/sas/qtgui/Utilities/GuiUtils.py b/src/sas/qtgui/Utilities/GuiUtils.py index 287c520758..7838ee900d 100644 --- a/src/sas/qtgui/Utilities/GuiUtils.py +++ b/src/sas/qtgui/Utilities/GuiUtils.py @@ -947,7 +947,7 @@ def xyTransform(data, xLabel="", yLabel=""): xLabel = "%s^{4}(%s)" % (xname, xunits) if xLabel == "ln(x)": data.transformX(DataTransform.toLogX, DataTransform.errToLogX) - xLabel = "\ln{(%s)}(%s)" % (xname, xunits) + xLabel = r"\ln{(%s)}(%s)" % (xname, xunits) if xLabel == "log10(x)": data.transformX(DataTransform.toX_pos, DataTransform.errToX_pos) xscale = 'log' @@ -961,7 +961,7 @@ def xyTransform(data, xLabel="", yLabel=""): # Y if yLabel == "ln(y)": data.transformY(DataTransform.toLogX, DataTransform.errToLogX) - yLabel = "\ln{(%s)}(%s)" % (yname, yunits) + yLabel = r"\ln{(%s)}(%s)" % (yname, yunits) if yLabel == "y": data.transformY(DataTransform.toX, DataTransform.errToX) yLabel = "%s(%s)" % (yname, yunits) @@ -980,31 +980,31 @@ def xyTransform(data, xLabel="", yLabel=""): if yLabel == "y*x^(2)": data.transformY(DataTransform.toYX2, DataTransform.errToYX2) xunits = convertUnit(2, xunits) - yLabel = "%s \ \ %s^{2}(%s%s)" % (yname, xname, yunits, xunits) + yLabel = r"%s \ \ %s^{2}(%s%s)" % (yname, xname, yunits, xunits) if yLabel == "y*x^(4)": data.transformY(DataTransform.toYX4, DataTransform.errToYX4) xunits = convertUnit(4, xunits) - yLabel = "%s \ \ %s^{4}(%s%s)" % (yname, xname, yunits, xunits) + yLabel = r"%s \ \ %s^{4}(%s%s)" % (yname, xname, yunits, xunits) if yLabel == "1/sqrt(y)": data.transformY(DataTransform.toOneOverSqrtX, DataTransform.errOneOverSqrtX) yunits = convertUnit(-0.5, yunits) - yLabel = "1/\sqrt{%s}(%s)" % (yname, yunits) + yLabel = r"1/\sqrt{%s}(%s)" % (yname, yunits) if yLabel == "ln(y*x)": data.transformY(DataTransform.toLogXY, DataTransform.errToLogXY) - yLabel = "\ln{(%s \ \ %s)}(%s%s)" % (yname, xname, yunits, xunits) + yLabel = r"\ln{(%s \ \ %s)}(%s%s)" % (yname, xname, yunits, xunits) if yLabel == "ln(y*x^(2))": data.transformY(DataTransform.toLogYX2, DataTransform.errToLogYX2) xunits = convertUnit(2, xunits) - yLabel = "\ln (%s \ \ %s^{2})(%s%s)" % (yname, xname, yunits, xunits) + yLabel = r"\ln (%s \ \ %s^{2})(%s%s)" % (yname, xname, yunits, xunits) if yLabel == "ln(y*x^(4))": data.transformY(DataTransform.toLogYX4, DataTransform.errToLogYX4) xunits = convertUnit(4, xunits) - yLabel = "\ln (%s \ \ %s^{4})(%s%s)" % (yname, xname, yunits, xunits) + yLabel = r"\ln (%s \ \ %s^{4})(%s%s)" % (yname, xname, yunits, xunits) if yLabel == "log10(y*x^(4))": data.transformY(DataTransform.toYX4, DataTransform.errToYX4) xunits = convertUnit(4, xunits) yscale = 'log' - yLabel = "%s \ \ %s^{4}(%s%s)" % (yname, xname, yunits, xunits) + yLabel = r"%s \ \ %s^{4}(%s%s)" % (yname, xname, yunits, xunits) # Perform the transformation of data in data1d->View data.transformView() From 28b0fcc47f8a4e2b193892d3b5cfea70c2b5aeea Mon Sep 17 00:00:00 2001 From: Wojciech Potrzebowski Date: Sun, 29 Jan 2023 13:17:38 +0100 Subject: [PATCH 37/44] Updated version numbers --- LICENSE.TXT | 3 ++- installers/license.txt | 2 +- src/sas/sasview/__init__.py | 6 +++--- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/LICENSE.TXT b/LICENSE.TXT index a2f0710800..8875774787 100644 --- a/LICENSE.TXT +++ b/LICENSE.TXT @@ -1,4 +1,5 @@ -Copyright (c) 2009-2022, SasView Developers +Copyright (c) 2009-2023, SasView Developers + All rights reserved. diff --git a/installers/license.txt b/installers/license.txt index 75ba6ea5b9..cdf6e5d125 100644 --- a/installers/license.txt +++ b/installers/license.txt @@ -18,4 +18,4 @@ sentence: *"This work benefited from the use of the SasView application, originally developed under NSF award DMR-0520547."* -Copyright (c) 2009-2022 UTK, UMD, ESS, NIST, ORNL, ISIS, ILL, DLS, DUT, BAM, ANSTO +Copyright (c) 2009-2023 UTK, UMD, ESS, NIST, ORNL, ISIS, ILL, DLS, DUT, BAM diff --git a/src/sas/sasview/__init__.py b/src/sas/sasview/__init__.py index e451e2feb2..d21667779b 100644 --- a/src/sas/sasview/__init__.py +++ b/src/sas/sasview/__init__.py @@ -1,8 +1,8 @@ from distutils.version import StrictVersion # The version number must follow StrictVersion rules as outlined # in http://epydoc.sourceforge.net/stdlib/distutils.version.StrictVersion-class.html -__version__ = "5.0.5" +__version__ = "5.0.6" StrictVersion(__version__) -__DOI__ = "Zenodo, 10.5281/zenodo.6331344" -__release_date__ = "2022" +__DOI__ = "Zenodo, " +__release_date__ = "2023" __build__ = "GIT_COMMIT" From e32e2bcb2ba0cf24627c07e3ec95684c8ba890f9 Mon Sep 17 00:00:00 2001 From: Wojciech Potrzebowski Date: Sun, 29 Jan 2023 13:32:13 +0100 Subject: [PATCH 38/44] Updated zenodo version --- src/sas/sasview/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sas/sasview/__init__.py b/src/sas/sasview/__init__.py index d21667779b..d2c058d6da 100644 --- a/src/sas/sasview/__init__.py +++ b/src/sas/sasview/__init__.py @@ -3,6 +3,6 @@ # in http://epydoc.sourceforge.net/stdlib/distutils.version.StrictVersion-class.html __version__ = "5.0.6" StrictVersion(__version__) -__DOI__ = "Zenodo, " +__DOI__ = "Zenodo, 10.5281/zenodo.7581379" __release_date__ = "2023" __build__ = "GIT_COMMIT" From f7268e42fd8f2436747bd83b56db9b14807f9ba9 Mon Sep 17 00:00:00 2001 From: Wojciech Potrzebowski Date: Sun, 29 Jan 2023 21:32:39 +0100 Subject: [PATCH 39/44] Update conf.py --- docs/sphinx-docs/source/conf.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/sphinx-docs/source/conf.py b/docs/sphinx-docs/source/conf.py index f695e6dfff..0f9a0e02b7 100644 --- a/docs/sphinx-docs/source/conf.py +++ b/docs/sphinx-docs/source/conf.py @@ -70,7 +70,7 @@ # General information about the project. project = u'SasView' -copyright = u'2022, The SasView Project' +copyright = u'2023, The SasView Project' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -81,7 +81,7 @@ # The short X.Y version. version = '5.0' # The full version, including e.g. alpha tags (a1). -release = '5.0.5' +release = '5.0.6' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. From 42e8bd332e0cafb3c972ab21ca91cdfd73ca232e Mon Sep 17 00:00:00 2001 From: Wojciech Potrzebowski Date: Sun, 29 Jan 2023 21:36:03 +0100 Subject: [PATCH 40/44] Update LocalConfig.py --- src/sas/qtgui/Utilities/LocalConfig.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sas/qtgui/Utilities/LocalConfig.py b/src/sas/qtgui/Utilities/LocalConfig.py index 25847aa6e7..747266e5b5 100644 --- a/src/sas/qtgui/Utilities/LocalConfig.py +++ b/src/sas/qtgui/Utilities/LocalConfig.py @@ -85,7 +85,7 @@ _diamond_url = "http://www.diamond.ac.uk" _corner_image = os.path.join(icon_path, "angles_flat.png") _welcome_image = os.path.join(icon_path, "SVwelcome.png") -_copyright = "Copyright (c) 2009-2022 UTK, UMD, ESS, NIST, ORNL, ISIS, ILL, DLS, TUD, BAM and ANSTO" +_copyright = "Copyright (c) 2009-2023 UTK, UMD, ESS, NIST, ORNL, ISIS, ILL, DLS, TUD, BAM and ANSTO" #edit the list of file state your plugin can read From 13fa9d523abc72e91dd111d9d3f0f55abe0eaed6 Mon Sep 17 00:00:00 2001 From: Wojciech Potrzebowski Date: Sun, 29 Jan 2023 21:38:26 +0100 Subject: [PATCH 41/44] Update license.txt --- installers/license.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/installers/license.txt b/installers/license.txt index cdf6e5d125..e8438c49f2 100644 --- a/installers/license.txt +++ b/installers/license.txt @@ -18,4 +18,4 @@ sentence: *"This work benefited from the use of the SasView application, originally developed under NSF award DMR-0520547."* -Copyright (c) 2009-2023 UTK, UMD, ESS, NIST, ORNL, ISIS, ILL, DLS, DUT, BAM +Copyright (c) 2009-2023 UTK, UMD, ESS, NIST, ORNL, ISIS, ILL, DLS, DUT, BAM, ANSTO From 3fc87b86393f5e5c193de53c9fadd1fcaaf16194 Mon Sep 17 00:00:00 2001 From: Wojciech Potrzebowski Date: Tue, 31 Jan 2023 16:38:41 +0100 Subject: [PATCH 42/44] Reading from bumps master --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2254c260ac..f747751ceb 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -104,7 +104,7 @@ jobs: - name: Fetch sources for sibling projects run: | git clone --depth=1 --branch=release_1.0.7 https://github.com/SasView/sasmodels.git ../sasmodels - git clone --depth=1 --branch=v0.9.0 https://github.com/bumps/bumps.git ../bumps + git clone --depth=1 --branch=master https://github.com/bumps/bumps.git ../bumps - name: Build and install sasmodels run: | From 4c834dbf9836c9d3f393b725875afa26ae7cb0dd Mon Sep 17 00:00:00 2001 From: Paul Butler Date: Wed, 1 Feb 2023 16:20:01 -0500 Subject: [PATCH 43/44] add beta version Beta version should be 5.0.6b1 not 5.0.6 --- docs/sphinx-docs/source/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sphinx-docs/source/conf.py b/docs/sphinx-docs/source/conf.py index 0f9a0e02b7..ab38579cfd 100644 --- a/docs/sphinx-docs/source/conf.py +++ b/docs/sphinx-docs/source/conf.py @@ -81,7 +81,7 @@ # The short X.Y version. version = '5.0' # The full version, including e.g. alpha tags (a1). -release = '5.0.6' +release = '5.0.6b1' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. From ec6cf79844787d9666765878f164892440421514 Mon Sep 17 00:00:00 2001 From: Paul Butler Date: Wed, 1 Feb 2023 16:23:17 -0500 Subject: [PATCH 44/44] Update version number in 2nd place Besides the sphinx docs we need to update the version in __init.py__ --- src/sas/sasview/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sas/sasview/__init__.py b/src/sas/sasview/__init__.py index d2c058d6da..e20fffeae9 100644 --- a/src/sas/sasview/__init__.py +++ b/src/sas/sasview/__init__.py @@ -1,7 +1,7 @@ from distutils.version import StrictVersion # The version number must follow StrictVersion rules as outlined # in http://epydoc.sourceforge.net/stdlib/distutils.version.StrictVersion-class.html -__version__ = "5.0.6" +__version__ = "5.0.6b1" StrictVersion(__version__) __DOI__ = "Zenodo, 10.5281/zenodo.7581379" __release_date__ = "2023"