Changes in / [3c6ecd9:80468f6] in sasview
- Files:
-
- 3 deleted
- 18 edited
Legend:
- Unmodified
- Added
- Removed
-
.gitignore
r846a063 r948484f 29 29 default_categories.json 30 30 /setup.cfg 31 **/UI/*.py32 !**/UI/__init__.py33 31 34 32 # doc build -
run.py
ra3221b6 ra3221b6 141 141 sys.path.append(build_path) 142 142 143 # Run the UI conversion tool144 import sas.qtgui.convertUI145 146 147 143 if __name__ == "__main__": 148 144 # Need to add absolute path before actual prepare call, -
src/sas/qtgui/Perspectives/Inversion/DMaxExplorerWidget.py
rb0ba43e rf4480f0 8 8 9 9 # global 10 import sys 11 import os 10 12 import logging 11 13 import numpy as np … … 40 42 super(DmaxWindow, self).__init__() 41 43 self.setupUi(self) 42 self.parent = parent43 44 44 45 self.setWindowTitle("Dââ Explorer") … … 49 50 50 51 self.plot = PlotterWidget(self, self) 51 self.hasPlot = False52 self.hasPlot = None 52 53 self.verticalLayout.insertWidget(0, self.plot) 53 54 54 55 # Let's choose the Standard Item Model. 55 56 self.model = QtGui.QStandardItemModel(self) 56 self.mapper = QtWidgets.QDataWidgetMapper(self)57 self.mapper = None 57 58 58 59 # Add validators on line edits 59 60 self.setupValidators() 60 61 61 # Connect buttons to slots.62 # Needs to be done early so default values propagate properly.62 # # Connect buttons to slots. 63 # # Needs to be done early so default values propagate properly. 63 64 self.setupSlots() 64 65 … … 81 82 82 83 def setupModel(self): 83 self.model.blockSignals(True)84 84 self.model.setItem(W.NPTS, QtGui.QStandardItem(str(self.nfunc))) 85 self.model.blockSignals(False)86 self.model.blockSignals(True)87 85 self.model.setItem(W.DMIN, QtGui.QStandardItem("{:.1f}".format(0.9*self.pr_state.d_max))) 88 self.model.blockSignals(False)89 self.model.blockSignals(True)90 86 self.model.setItem(W.DMAX, QtGui.QStandardItem("{:.1f}".format(1.1*self.pr_state.d_max))) 91 self.model.blockSignals(False)92 self.model.blockSignals(True)93 87 self.model.setItem(W.VARIABLE, QtGui.QStandardItem( "ϲ/dof")) 94 self.model.blockSignals(False)95 88 96 89 def setupMapper(self): 90 self.mapper = QtWidgets.QDataWidgetMapper(self) 97 91 self.mapper.setOrientation(QtCore.Qt.Vertical) 98 92 self.mapper.setModel(self.model) … … 117 111 chi2 = [] 118 112 119 try: 120 dmin = float(self.model.item(W.DMIN).text()) 121 dmax = float(self.model.item(W.DMAX).text()) 122 npts = float(self.model.item(W.NPTS).text()) 123 xs = np.linspace(dmin, dmax, npts) 124 except ValueError as e: 125 msg = ("An input value is not correctly formatted. Please check {}" 126 .format(e.message)) 127 logger.error(msg) 113 xs = np.linspace(float(self.model.item(W.DMIN).text()), 114 float(self.model.item(W.DMAX).text()), 115 float(self.model.item(W.NPTS).text())) 128 116 129 117 original = self.pr_state.d_max … … 144 132 msg = "ExploreDialog: inversion failed " 145 133 msg += "for D_max=%s\n%s" % (str(x), ex) 134 print(msg) 146 135 logger.error(msg) 147 136 … … 153 142 msg = "ExploreDialog: inversion failed " 154 143 msg += "for D_max=%s\n%s" % (str(x), ex) 144 print(msg) 155 145 logger.error(msg) 156 146 … … 161 151 if plotter == "ϲ/dof": 162 152 ys = chi2 163 y_label = "\ \chi^2/dof"153 y_label = "\chi^2/dof" 164 154 y_unit = "a.u." 165 155 elif plotter == "I(Q=0)": 166 156 ys = iq0 167 157 y_label = "I(q=0)" 168 y_unit = "\ \AA^{-1}"158 y_unit = "\AA^{-1}" 169 159 elif plotter == "Rg": 170 160 ys = rg 171 161 y_label = "R_g" 172 y_unit = "\ \AA"162 y_unit = "\AA" 173 163 elif plotter == "Oscillation parameter": 174 164 ys = osc … … 178 168 ys = bck 179 169 y_label = "Bckg" 180 y_unit = "\ \AA^{-1}"170 y_unit = "\AA^{-1}" 181 171 elif plotter == "Positive Fraction": 182 172 ys = pos … … 185 175 else: 186 176 ys = pos_err 187 y_label = "P^{+}_{1\ \sigma}"177 y_label = "P^{+}_{1\sigma}" 188 178 y_unit = "a.u." 189 179 … … 198 188 data._yunit = y_unit 199 189 self.plot.plot(data=data, marker="-") 200 201 def closeEvent(self, event):202 """Override close event"""203 self.parent.dmaxWindow = None204 event.accept() -
src/sas/qtgui/Perspectives/Inversion/InversionLogic.py
- Property mode changed from 100644 to 100755
r6da860a rfa81e94 1 1 import math 2 import logging2 import pylab 3 3 import numpy as np 4 4 … … 12 12 GROUP_ID_IQ_DATA = r"$I_{obs}(q)$" 13 13 GROUP_ID_PR_FIT = r"$P_{fit}(r)$" 14 PR_PLOT_PTS = 5115 16 logger = logging.getLogger(__name__)17 14 18 15 … … 23 20 """ 24 21 22 # TODO: Add way to change this value 23 _pr_n_pts = 51 24 25 25 def __init__(self, data=None): 26 26 self._data = data … … 37 37 """ data setter """ 38 38 self._data = value 39 self.data_is_loaded = (self._data is not None)39 self.data_is_loaded = True 40 40 41 41 def isLoadedData(self): … … 63 63 maxq = pr.q_max 64 64 65 x = np.arange(minq, maxq, maxq / 301.0)65 x = pylab.arange(minq, maxq, maxq / 301.0) 66 66 y = np.zeros(len(x)) 67 67 err = np.zeros(len(x)) … … 73 73 except: 74 74 err[i] = 1.0 75 logger.log(("Error getting error", value, x[i]))75 print(("Error getting error", value, x[i])) 76 76 77 77 new_plot = Data1D(x, y) … … 89 89 # If we have used slit smearing, plot the smeared I(q) too 90 90 if pr.slit_width > 0 or pr.slit_height > 0: 91 x = np.arange(minq, maxq, maxq / 301.0)91 x = pylab.arange(minq, maxq, maxq / 301.0) 92 92 y = np.zeros(len(x)) 93 93 err = np.zeros(len(x)) … … 99 99 except: 100 100 err[i] = 1.0 101 logger.log(("Error getting error", value, x[i]))101 print(("Error getting error", value, x[i])) 102 102 103 103 new_plot = Data1D(x, y) … … 113 113 return new_plot 114 114 115 def update1DPlot(self, plot, out, pr, q=None): 116 """ 117 Create a new 1D data instance based on fitting results 118 """ 119 120 qtemp = pr.x 121 if q is not None: 122 qtemp = q 123 124 # Make a plot 125 maxq = max(qtemp) 126 127 minq = min(qtemp) 128 129 # Check for user min/max 130 if pr.q_min is not None and maxq >= pr.q_min >= minq: 131 minq = pr.q_min 132 if pr.q_max is not None and maxq >= pr.q_max >= minq: 133 maxq = pr.q_max 134 135 x = pylab.arange(minq, maxq, maxq / 301.0) 136 y = np.zeros(len(x)) 137 err = np.zeros(len(x)) 138 for i in range(len(x)): 139 value = pr.iq(out, x[i]) 140 y[i] = value 141 try: 142 err[i] = math.sqrt(math.fabs(value)) 143 except: 144 err[i] = 1.0 145 print(("Error getting error", value, x[i])) 146 147 plot.x = x 148 plot.y = y 149 150 # If we have used slit smearing, plot the smeared I(q) too 151 if pr.slit_width > 0 or pr.slit_height > 0: 152 x = pylab.arange(minq, maxq, maxq / 301.0) 153 y = np.zeros(len(x)) 154 err = np.zeros(len(x)) 155 for i in range(len(x)): 156 value = pr.iq_smeared(pr.out, x[i]) 157 y[i] = value 158 try: 159 err[i] = math.sqrt(math.fabs(value)) 160 except: 161 err[i] = 1.0 162 print(("Error getting error", value, x[i])) 163 164 plot.x = x 165 plot.y = y 166 167 return plot 168 115 169 def newPRPlot(self, out, pr, cov=None): 116 170 """ 117 171 """ 118 172 # Show P(r) 119 x = np.arange(0.0, pr.d_max, pr.d_max / PR_PLOT_PTS)173 x = pylab.arange(0.0, pr.d_max, pr.d_max / self._pr_n_pts) 120 174 121 175 y = np.zeros(len(x)) … … 138 192 139 193 y[i] = value 194 195 # if self._normalize_output == True: 196 # y = y / total 197 # dy = dy / total 198 # elif self._scale_output_unity == True: 199 # y = y / pmax 200 # dy = dy / pmax 140 201 141 202 if cov2 is None: … … 148 209 new_plot.title = "P(r) fit" 149 210 new_plot.id = PR_FIT_LABEL 150 new_plot.scale = "linear" 211 # Make sure that the plot is linear 212 new_plot.xtransform = "x" 213 new_plot.ytransform = "y" 151 214 new_plot.group_id = GROUP_ID_PR_FIT 152 215 153 216 return new_plot 217 218 def updatePRPlot(self, plot, out, pr, cov=None): 219 x = pylab.arange(0.0, pr.d_max, pr.d_max / self._pr_n_pts) 220 221 y = np.zeros(len(x)) 222 dy = np.zeros(len(x)) 223 224 total = 0.0 225 pmax = 0.0 226 cov2 = np.ascontiguousarray(cov) 227 228 for i in range(len(x)): 229 if cov2 is None: 230 value = pr.pr(out, x[i]) 231 else: 232 (value, dy[i]) = pr.pr_err(out, cov2, x[i]) 233 total += value * pr.d_max / len(x) 234 235 # keep track of the maximum P(r) value 236 if value > pmax: 237 pmax = value 238 239 y[i] = value 240 241 # if self._normalize_output == True: 242 # y = y / total 243 # dy = dy / total 244 # elif self._scale_output_unity == True: 245 # y = y / pmax 246 # dy = dy / pmax 247 plot.x = x 248 plot.y = y 249 250 if cov2 is not None: 251 plot.dy = dy 252 253 return plot 154 254 155 255 def computeDataRange(self): -
src/sas/qtgui/Perspectives/Inversion/InversionPerspective.py
rb0ba43e raed0532 1 import sys 1 2 import logging 3 import pylab 2 4 import numpy as np 3 5 … … 13 15 14 16 # pr inversion calculation elements 17 from sas.sascalc.dataloader.data_info import Data1D 15 18 from sas.sascalc.pr.invertor import Invertor 16 # Batch calculation display17 from sas.qtgui.Utilities.GridPanel import BatchInversionOutputPanel18 19 19 20 20 def is_float(value): … … 25 25 return 0.0 26 26 27 28 27 NUMBER_OF_TERMS = 10 29 28 REGULARIZATION = 0.0001 30 29 BACKGROUND_INPUT = 0.0 31 30 MAX_DIST = 140.0 32 DICT_KEYS = ["Calculator", "PrPlot", "DataPlot"] 33 34 logger = logging.getLogger(__name__) 35 36 31 32 # TODO: Modify plot references, don't just send new 33 # TODO: Update help with batch capabilities 34 # TODO: Method to export results in some meaningful way 37 35 class InversionWindow(QtWidgets.QDialog, Ui_PrInversion): 38 36 """ … … 52 50 53 51 self._manager = parent 54 self. communicate = parent.communicator()55 self.communicate .dataDeletedSignal.connect(self.removeData)52 self._model_item = QtGui.QStandardItem() 53 self.communicate = GuiUtils.Communicate() 56 54 57 55 self.logic = InversionLogic() 58 56 57 # Reference to Dmax window 58 self.dmaxWindow = None 59 59 60 # The window should not close 60 self._allowClose = False 61 62 # Visible data items 61 self._allow_close = False 62 63 63 # current QStandardItem showing on the panel 64 64 self._data = None 65 # Reference to Dmax window for self._data 66 self.dmaxWindow = None 67 # p(r) calculator for self._data 65 # current Data1D as referenced by self._data 66 self._data_set = None 67 68 # p(r) calculator 68 69 self._calculator = Invertor() 69 # Default to background estimate 70 self._calculator.est_bck = True 71 # plots of self._data 72 self.prPlot = None 73 self.dataPlot = None 74 # suggested nTerms 75 self.nTermsSuggested = NUMBER_OF_TERMS 76 77 # Calculation threads used by all data items 78 self.calcThread = None 79 self.estimationThread = None 80 self.estimationThreadNT = None 81 self.isCalculating = False 82 83 # Mapping for all data items 84 # Dictionary mapping data to all parameters 85 self._dataList = {} 70 self._last_calculator = None 71 self.calc_thread = None 72 self.estimation_thread = None 73 74 # Current data object in view 75 self._data_index = 0 76 # list mapping data to p(r) calculation 77 self._data_list = {} 86 78 if not isinstance(data, list): 87 79 data_list = [data] 88 80 if data is not None: 89 81 for datum in data_list: 90 self.updateDataList(datum) 91 92 self.dataDeleted = False 82 self._data_list[datum] = self._calculator.clone() 83 84 # dict of models for quick update after calculation 85 # {item:model} 86 self._models = {} 87 88 self.calculateAllButton.setEnabled(False) 89 self.calculateThisButton.setEnabled(False) 90 91 # plots for current data 92 self.pr_plot = None 93 self.data_plot = None 94 # plot references for all data in perspective 95 self.pr_plot_list = {} 96 self.data_plot_list = {} 93 97 94 98 self.model = QtGui.QStandardItemModel(self) 95 99 self.mapper = QtWidgets.QDataWidgetMapper(self) 96 97 # Batch fitting parameters98 self.isBatch = False99 self.batchResultsWindow = None100 self.batchResults = {}101 self.batchComplete = []102 100 103 101 # Add validators … … 126 124 """ 127 125 assert isinstance(value, bool) 128 self._allowClose = value 129 130 def isClosable(self): 131 """ 132 Allow outsiders close this widget 133 """ 134 return self._allowClose 126 self._allow_close = value 135 127 136 128 def closeEvent(self, event): … … 138 130 Overwrite QDialog close method to allow for custom widget close 139 131 """ 140 # Close report widgets before closing/minimizing main widget 141 self.closeDMax() 142 self.closeBatchResults() 143 if self._allowClose: 132 if self._allow_close: 144 133 # reset the closability flag 145 134 self.setClosable(value=False) … … 152 141 self.setWindowState(QtCore.Qt.WindowMinimized) 153 142 154 def closeDMax(self):155 if self.dmaxWindow is not None:156 self.dmaxWindow.close()157 158 def closeBatchResults(self):159 if self.batchResultsWindow is not None:160 self.batchResultsWindow.close()161 162 143 ###################################################################### 163 144 # Initialization routines … … 168 149 self.calculateAllButton.clicked.connect(self.startThreadAll) 169 150 self.calculateThisButton.clicked.connect(self.startThread) 170 self.stopButton.clicked.connect(self.stopCalculation)171 151 self.removeButton.clicked.connect(self.removeData) 172 152 self.helpButton.clicked.connect(self.help) … … 177 157 self.explorerButton.clicked.connect(self.openExplorerWindow) 178 158 179 self.backgroundInput. textChanged.connect(180 lambda: self. set_background(self.backgroundInput.text()))181 self.minQInput. textChanged.connect(159 self.backgroundInput.editingFinished.connect( 160 lambda: self._calculator.set_est_bck(int(is_float(self.backgroundInput.text())))) 161 self.minQInput.editingFinished.connect( 182 162 lambda: self._calculator.set_qmin(is_float(self.minQInput.text()))) 183 self.regularizationConstantInput. textChanged.connect(163 self.regularizationConstantInput.editingFinished.connect( 184 164 lambda: self._calculator.set_alpha(is_float(self.regularizationConstantInput.text()))) 185 self.maxDistanceInput. textChanged.connect(165 self.maxDistanceInput.editingFinished.connect( 186 166 lambda: self._calculator.set_dmax(is_float(self.maxDistanceInput.text()))) 187 self.maxQInput. textChanged.connect(167 self.maxQInput.editingFinished.connect( 188 168 lambda: self._calculator.set_qmax(is_float(self.maxQInput.text()))) 189 self.slitHeightInput. textChanged.connect(169 self.slitHeightInput.editingFinished.connect( 190 170 lambda: self._calculator.set_slit_height(is_float(self.slitHeightInput.text()))) 191 self.slitWidthInput. textChanged.connect(192 lambda: self._calculator.set_slit_width(is_float(self.slit WidthInput.text())))171 self.slitWidthInput.editingFinished.connect( 172 lambda: self._calculator.set_slit_width(is_float(self.slitHeightInput.text()))) 193 173 194 174 self.model.itemChanged.connect(self.model_changed) … … 247 227 Update boxes with initial values 248 228 """ 249 bgd_item = QtGui.QStandardItem(str(BACKGROUND_INPUT)) 250 self.model.setItem(WIDGETS.W_BACKGROUND_INPUT, bgd_item) 251 blank_item = QtGui.QStandardItem("") 252 self.model.setItem(WIDGETS.W_QMIN, blank_item) 253 blank_item = QtGui.QStandardItem("") 254 self.model.setItem(WIDGETS.W_QMAX, blank_item) 255 blank_item = QtGui.QStandardItem("") 256 self.model.setItem(WIDGETS.W_SLIT_WIDTH, blank_item) 257 blank_item = QtGui.QStandardItem("") 258 self.model.setItem(WIDGETS.W_SLIT_HEIGHT, blank_item) 259 no_terms_item = QtGui.QStandardItem(str(NUMBER_OF_TERMS)) 260 self.model.setItem(WIDGETS.W_NO_TERMS, no_terms_item) 261 reg_item = QtGui.QStandardItem(str(REGULARIZATION)) 262 self.model.setItem(WIDGETS.W_REGULARIZATION, reg_item) 263 max_dist_item = QtGui.QStandardItem(str(MAX_DIST)) 264 self.model.setItem(WIDGETS.W_MAX_DIST, max_dist_item) 265 blank_item = QtGui.QStandardItem("") 266 self.model.setItem(WIDGETS.W_RG, blank_item) 267 blank_item = QtGui.QStandardItem("") 268 self.model.setItem(WIDGETS.W_I_ZERO, blank_item) 269 bgd_item = QtGui.QStandardItem(str(BACKGROUND_INPUT)) 270 self.model.setItem(WIDGETS.W_BACKGROUND_OUTPUT, bgd_item) 271 blank_item = QtGui.QStandardItem("") 272 self.model.setItem(WIDGETS.W_COMP_TIME, blank_item) 273 blank_item = QtGui.QStandardItem("") 274 self.model.setItem(WIDGETS.W_CHI_SQUARED, blank_item) 275 blank_item = QtGui.QStandardItem("") 276 self.model.setItem(WIDGETS.W_OSCILLATION, blank_item) 277 blank_item = QtGui.QStandardItem("") 278 self.model.setItem(WIDGETS.W_POS_FRACTION, blank_item) 279 blank_item = QtGui.QStandardItem("") 280 self.model.setItem(WIDGETS.W_SIGMA_POS_FRACTION, blank_item) 229 item = QtGui.QStandardItem("") 230 self.model.setItem(WIDGETS.W_FILENAME, item) 231 item = QtGui.QStandardItem(str(BACKGROUND_INPUT)) 232 self.model.setItem(WIDGETS.W_BACKGROUND_INPUT, item) 233 item = QtGui.QStandardItem("") 234 self.model.setItem(WIDGETS.W_QMIN, item) 235 item = QtGui.QStandardItem("") 236 self.model.setItem(WIDGETS.W_QMAX, item) 237 item = QtGui.QStandardItem("") 238 self.model.setItem(WIDGETS.W_SLIT_WIDTH, item) 239 item = QtGui.QStandardItem("") 240 self.model.setItem(WIDGETS.W_SLIT_HEIGHT, item) 241 item = QtGui.QStandardItem(str(NUMBER_OF_TERMS)) 242 self.model.setItem(WIDGETS.W_NO_TERMS, item) 243 item = QtGui.QStandardItem(str(REGULARIZATION)) 244 self.model.setItem(WIDGETS.W_REGULARIZATION, item) 245 item = QtGui.QStandardItem(str(MAX_DIST)) 246 self.model.setItem(WIDGETS.W_MAX_DIST, item) 247 item = QtGui.QStandardItem("") 248 self.model.setItem(WIDGETS.W_RG, item) 249 item = QtGui.QStandardItem("") 250 self.model.setItem(WIDGETS.W_I_ZERO, item) 251 item = QtGui.QStandardItem("") 252 self.model.setItem(WIDGETS.W_BACKGROUND_OUTPUT, item) 253 item = QtGui.QStandardItem("") 254 self.model.setItem(WIDGETS.W_COMP_TIME, item) 255 item = QtGui.QStandardItem("") 256 self.model.setItem(WIDGETS.W_CHI_SQUARED, item) 257 item = QtGui.QStandardItem("") 258 self.model.setItem(WIDGETS.W_OSCILLATION, item) 259 item = QtGui.QStandardItem("") 260 self.model.setItem(WIDGETS.W_POS_FRACTION, item) 261 item = QtGui.QStandardItem("") 262 self.model.setItem(WIDGETS.W_SIGMA_POS_FRACTION, item) 281 263 282 264 def setupWindow(self): … … 302 284 Enable buttons when data is present, else disable them 303 285 """ 304 self.calculateAllButton.setEnabled(len(self._dataList) > 1 305 and not self.isBatch 306 and not self.isCalculating) 307 self.calculateThisButton.setEnabled(self.logic.data_is_loaded 308 and not self.isBatch 309 and not self.isCalculating) 286 self.calculateAllButton.setEnabled(self.logic.data_is_loaded) 287 self.calculateThisButton.setEnabled(self.logic.data_is_loaded) 310 288 self.removeButton.setEnabled(self.logic.data_is_loaded) 311 self.explorerButton.setEnabled(self.logic.data_is_loaded 312 and np.all(self.logic.data.dy != 0)) 313 self.stopButton.setVisible(self.isCalculating) 314 self.regConstantSuggestionButton.setEnabled( 315 self.logic.data_is_loaded and 316 self._calculator.suggested_alpha != self._calculator.alpha) 317 self.noOfTermsSuggestionButton.setEnabled( 318 self.logic.data_is_loaded and 319 self._calculator.nfunc != self.nTermsSuggested) 289 self.explorerButton.setEnabled(self.logic.data_is_loaded) 320 290 321 291 def populateDataComboBox(self, filename, data_ref): … … 337 307 self.regConstantSuggestionButton.text())) 338 308 339 def displayChange(self, data_index=0): 340 """Switch to another item in the data list""" 341 if self.dataDeleted: 342 return 343 self.updateDataList(self._data) 344 self.setCurrentData(self.dataList.itemData(data_index)) 309 def displayChange(self): 310 ref_item = self.dataList.itemData(self.dataList.currentIndex()) 311 self._model_item = ref_item 312 self.setCurrentData(ref_item) 313 self.setCurrentModel(ref_item) 314 315 def removeData(self): 316 """Remove the existing data reference from the P(r) Persepective""" 317 self._data_list.pop(self._data) 318 self.pr_plot_list.pop(self._data) 319 self.data_plot_list.pop(self._data) 320 if self.dmaxWindow is not None: 321 self.dmaxWindow = None 322 self.dataList.removeItem(self.dataList.currentIndex()) 323 self.dataList.setCurrentIndex(0) 324 # Last file removed 325 if not self._data_list: 326 self._data = None 327 self.pr_plot = None 328 self._data_set = None 329 self.calculateThisButton.setEnabled(False) 330 self.calculateAllButton.setEnabled(False) 331 self.explorerButton.setEnabled(False) 345 332 346 333 ###################################################################### 347 334 # GUI Interaction Events 348 335 349 def updateCalculator(self): 336 def setCurrentModel(self, ref_item): 337 '''update the current model with stored values''' 338 if ref_item in self._models: 339 self.model = self._models[ref_item] 340 341 def update_calculator(self): 350 342 """Update all p(r) params""" 351 self._calculator.set_x(self.logic.data.x) 352 self._calculator.set_y(self.logic.data.y) 353 self._calculator.set_err(self.logic.data.dy) 354 self.set_background(self.backgroundInput.text()) 355 356 def set_background(self, value): 357 self._calculator.background = is_float(value) 343 self._calculator.set_x(self._data_set.x) 344 self._calculator.set_y(self._data_set.y) 345 self._calculator.set_err(self._data_set.dy) 358 346 359 347 def model_changed(self): … … 362 350 msg = "Unable to update P{r}. The connection between the main GUI " 363 351 msg += "and P(r) was severed. Attempting to restart P(r)." 364 logg er.warning(msg)352 logging.warning(msg) 365 353 self.setClosable(True) 366 354 self.close() 367 InversionWindow.__init__(self.parent(), list(self._data List.keys()))355 InversionWindow.__init__(self.parent(), list(self._data_list.keys())) 368 356 exit(0) 357 # TODO: Only send plot first time - otherwise, update in complete 358 if self.pr_plot is not None: 359 title = self.pr_plot.name 360 GuiUtils.updateModelItemWithPlot(self._data, self.pr_plot, title) 361 if self.data_plot is not None: 362 title = self.data_plot.name 363 GuiUtils.updateModelItemWithPlot(self._data, self.data_plot, title) 369 364 if self.dmaxWindow is not None: 370 self.dmaxWindow.nfunc = self.getNFunc() 371 self.dmaxWindow.pr_state = self._calculator 372 self.mapper.toLast() 365 self.dmaxWindow.pr_state = self._calculator 366 self.dmaxWindow.nfunc = self.getNFunc() 367 368 self.mapper.toFirst() 373 369 374 370 def help(self): … … 387 383 Toggle the background between manual and estimated 388 384 """ 389 if self.estimateBgd.isChecked():390 self.manualBgd.setChecked(False)385 sender = self.sender() 386 if sender is self.estimateBgd: 391 387 self.backgroundInput.setEnabled(False) 392 self._calculator.set_est_bck = True 393 elif self.manualBgd.isChecked(): 394 self.estimateBgd.setChecked(False) 388 else: 395 389 self.backgroundInput.setEnabled(True) 396 self._calculator.set_est_bck = False397 else:398 pass399 390 400 391 def openExplorerWindow(self): … … 403 394 """ 404 395 from .DMaxExplorerWidget import DmaxWindow 405 self.dmaxWindow = DmaxWindow(pr_state=self._calculator, 406 nfunc=self.getNFunc(), 407 parent=self) 396 self.dmaxWindow = DmaxWindow(self._calculator, self.getNFunc(), self) 408 397 self.dmaxWindow.show() 409 410 def showBatchOutput(self):411 """412 Display the batch output in tabular form413 :param output_data: Dictionary mapping filename -> P(r) instance414 """415 if self.batchResultsWindow is None:416 self.batchResultsWindow = BatchInversionOutputPanel(417 parent=self, output_data=self.batchResults)418 else:419 self.batchResultsWindow.setupTable(self.batchResults)420 self.batchResultsWindow.show()421 422 def stopCalculation(self):423 """ Stop all threads, return to the base state and update GUI """424 self.stopCalcThread()425 self.stopEstimationThread()426 self.stopEstimateNTThread()427 # Show any batch calculations that successfully completed428 if self.isBatch and self.batchResultsWindow is not None:429 self.showBatchOutput()430 self.isBatch = False431 self.isCalculating = False432 self.updateGuiValues()433 398 434 399 ###################################################################### … … 445 410 if not isinstance(data_item, list): 446 411 msg = "Incorrect type passed to the P(r) Perspective" 447 raise AttributeError (msg)412 raise AttributeError 448 413 449 414 for data in data_item: 450 if data in self._data List.keys():415 if data in self._data_list.keys(): 451 416 # Don't add data if it's already in 452 continue417 return 453 418 # Create initial internal mappings 454 self.logic.data = GuiUtils.dataFromItem(data) 419 self._data_list[data] = self._calculator.clone() 420 self._data_set = GuiUtils.dataFromItem(data) 421 self.data_plot_list[data] = self.data_plot 422 self.pr_plot_list[data] = self.pr_plot 423 self.populateDataComboBox(self._data_set.filename, data) 424 self.setCurrentData(data) 425 426 # Estimate initial values from data 427 self.performEstimate() 428 self.logic = InversionLogic(self._data_set) 429 455 430 # Estimate q range 456 431 qmin, qmax = self.logic.computeDataRange() 457 self._calculator.set_qmin(qmin) 458 self._calculator.set_qmax(qmax) 459 self.updateDataList(data) 460 self.populateDataComboBox(self.logic.data.filename, data) 461 self.dataList.setCurrentIndex(len(self.dataList) - 1) 462 self.setCurrentData(data) 463 464 def updateDataList(self, dataRef): 465 """Save the current data state of the window into self._data_list""" 466 if dataRef is None: 467 return 468 self._dataList[dataRef] = { 469 DICT_KEYS[0]: self._calculator, 470 DICT_KEYS[1]: self.prPlot, 471 DICT_KEYS[2]: self.dataPlot 472 } 473 # Update batch results window when finished 474 self.batchResults[self.logic.data.filename] = self._calculator 475 if self.batchResultsWindow is not None: 476 self.showBatchOutput() 432 self.model.setItem(WIDGETS.W_QMIN, QtGui.QStandardItem("{:.4g}".format(qmin))) 433 self.model.setItem(WIDGETS.W_QMAX, QtGui.QStandardItem("{:.4g}".format(qmax))) 434 self._models[data] = self.model 435 self.model_item = data 436 437 self.enableButtons() 477 438 478 439 def getNFunc(self): … … 481 442 nfunc = int(self.noOfTermsInput.text()) 482 443 except ValueError: 483 logger.error("Incorrect number of terms specified: %s" 484 %self.noOfTermsInput.text()) 444 logging.error("Incorrect number of terms specified: %s" %self.noOfTermsInput.text()) 485 445 self.noOfTermsInput.setText(str(NUMBER_OF_TERMS)) 486 446 nfunc = NUMBER_OF_TERMS … … 488 448 489 449 def setCurrentData(self, data_ref): 490 """Get the data by reference and display as necessary""" 450 """Get the current data and display as necessary""" 451 491 452 if data_ref is None: 492 453 return 454 493 455 if not isinstance(data_ref, QtGui.QStandardItem): 494 456 msg = "Incorrect type passed to the P(r) Perspective" 495 raise AttributeError(msg) 457 raise AttributeError 458 496 459 # Data references 497 460 self._data = data_ref 498 self.logic.data = GuiUtils.dataFromItem(data_ref) 499 self._calculator = self._dataList[data_ref].get(DICT_KEYS[0]) 500 self.prPlot = self._dataList[data_ref].get(DICT_KEYS[1]) 501 self.dataPlot = self._dataList[data_ref].get(DICT_KEYS[2]) 502 self.performEstimate() 503 504 def updateGuiValues(self): 505 pr = self._calculator 506 out = self._calculator.out 507 cov = self._calculator.cov 508 elapsed = self._calculator.elapsed 509 alpha = self._calculator.suggested_alpha 510 self.model.setItem(WIDGETS.W_QMIN, 511 QtGui.QStandardItem("{:.4g}".format(pr.get_qmin()))) 512 self.model.setItem(WIDGETS.W_QMAX, 513 QtGui.QStandardItem("{:.4g}".format(pr.get_qmax()))) 514 self.model.setItem(WIDGETS.W_BACKGROUND_INPUT, 515 QtGui.QStandardItem("{:.3g}".format(pr.background))) 516 self.model.setItem(WIDGETS.W_BACKGROUND_OUTPUT, 517 QtGui.QStandardItem("{:.3g}".format(pr.background))) 518 self.model.setItem(WIDGETS.W_COMP_TIME, 519 QtGui.QStandardItem("{:.4g}".format(elapsed))) 520 self.model.setItem(WIDGETS.W_MAX_DIST, 521 QtGui.QStandardItem("{:.4g}".format(pr.get_dmax()))) 522 self.regConstantSuggestionButton.setText("{:-3.2g}".format(alpha)) 523 self.noOfTermsSuggestionButton.setText( 524 "{:n}".format(self.nTermsSuggested)) 525 526 if isinstance(pr.chi2, np.ndarray): 527 self.model.setItem(WIDGETS.W_CHI_SQUARED, 528 QtGui.QStandardItem("{:.3g}".format(pr.chi2[0]))) 529 if out is not None: 530 self.model.setItem(WIDGETS.W_RG, 531 QtGui.QStandardItem("{:.3g}".format(pr.rg(out)))) 532 self.model.setItem(WIDGETS.W_I_ZERO, 533 QtGui.QStandardItem( 534 "{:.3g}".format(pr.iq0(out)))) 535 self.model.setItem(WIDGETS.W_OSCILLATION, QtGui.QStandardItem( 536 "{:.3g}".format(pr.oscillations(out)))) 537 self.model.setItem(WIDGETS.W_POS_FRACTION, QtGui.QStandardItem( 538 "{:.3g}".format(pr.get_positive(out)))) 539 if cov is not None: 540 self.model.setItem(WIDGETS.W_SIGMA_POS_FRACTION, 541 QtGui.QStandardItem( 542 "{:.3g}".format( 543 pr.get_pos_err(out, cov)))) 544 if self.prPlot is not None: 545 title = self.prPlot.name 546 GuiUtils.updateModelItemWithPlot(self._data, self.prPlot, title) 547 self.communicate.plotRequestedSignal.emit([self.prPlot]) 548 if self.dataPlot is not None: 549 title = self.dataPlot.name 550 GuiUtils.updateModelItemWithPlot(self._data, self.dataPlot, title) 551 self.communicate.plotRequestedSignal.emit([self.dataPlot]) 552 self.enableButtons() 553 554 def removeData(self, data_list=None): 555 """Remove the existing data reference from the P(r) Persepective""" 556 self.dataDeleted = True 557 self.batchResults = {} 558 if not data_list: 559 data_list = [self._data] 560 self.closeDMax() 561 for data in data_list: 562 self._dataList.pop(data) 563 self._data = None 564 length = len(self.dataList) 565 for index in reversed(range(length)): 566 if self.dataList.itemData(index) in data_list: 567 self.dataList.removeItem(index) 568 # Last file removed 569 self.dataDeleted = False 570 if len(self._dataList) == 0: 571 self.prPlot = None 572 self.dataPlot = None 573 self.logic.data = None 574 self._calculator = Invertor() 575 self.closeBatchResults() 576 self.nTermsSuggested = NUMBER_OF_TERMS 577 self.noOfTermsSuggestionButton.setText("{:n}".format( 578 self.nTermsSuggested)) 579 self.regConstantSuggestionButton.setText("{:-3.2g}".format( 580 REGULARIZATION)) 581 self.updateGuiValues() 582 self.setupModel() 583 else: 584 self.dataList.setCurrentIndex(0) 585 self.updateGuiValues() 461 self._data_set = GuiUtils.dataFromItem(data_ref) 462 self._calculator = self._data_list[data_ref] 463 self.pr_plot = self.pr_plot_list[data_ref] 464 self.data_plot = self.data_plot_list[data_ref] 586 465 587 466 ###################################################################### 588 467 # Thread Creators 589 590 468 def startThreadAll(self): 591 self.isCalculating = True 592 self.isBatch = True 593 self.batchComplete = [] 594 self.calculateAllButton.setText("Calculating...") 595 self.enableButtons() 596 self.batchResultsWindow = BatchInversionOutputPanel( 597 parent=self, output_data=self.batchResults) 598 self.performEstimate() 599 600 def startNextBatchItem(self): 601 self.isBatch = False 602 for index in range(len(self._dataList)): 603 if index not in self.batchComplete: 604 self.dataList.setCurrentIndex(index) 605 self.isBatch = True 606 # Add the index before calculating in case calculation fails 607 self.batchComplete.append(index) 608 break 609 if self.isBatch: 610 self.performEstimate() 611 else: 612 # If no data sets left, end batch calculation 613 self.isCalculating = False 614 self.batchComplete = [] 615 self.calculateAllButton.setText("Calculate All") 616 self.showBatchOutput() 617 self.enableButtons() 469 for data_ref, pr in list(self._data_list.items()): 470 self._data_set = GuiUtils.dataFromItem(data_ref) 471 self._calculator = pr 472 self.startThread() 618 473 619 474 def startThread(self): … … 624 479 625 480 # Set data before running the calculations 626 self.isCalculating = True 627 self.enableButtons() 628 self.updateCalculator() 629 # Disable calculation buttons to prevent thread interference 630 631 # If the thread is already started, stop it 632 self.stopCalcThread() 633 481 self.update_calculator() 482 483 # If a thread is already started, stop it 484 if self.calc_thread is not None and self.calc_thread.isrunning(): 485 self.calc_thread.stop() 634 486 pr = self._calculator.clone() 635 487 nfunc = self.getNFunc() 636 self.calcThread = CalcPr(pr, nfunc, 637 error_func=self._threadError, 638 completefn=self._calculateCompleted, 639 updatefn=None) 640 self.calcThread.queue() 641 self.calcThread.ready(2.5) 642 643 def stopCalcThread(self): 644 """ Stops a thread if it exists and is running """ 645 if self.calcThread is not None and self.calcThread.isrunning(): 646 self.calcThread.stop() 488 self.calc_thread = CalcPr(pr, nfunc, 489 error_func=self._threadError, 490 completefn=self._calculateCompleted, 491 updatefn=None) 492 self.calc_thread.queue() 493 self.calc_thread.ready(2.5) 647 494 648 495 def performEstimateNT(self): … … 652 499 from .Thread import EstimateNT 653 500 654 self.updateCalculator()655 656 501 # If a thread is already started, stop it 657 self.stopEstimateNTThread() 658 502 if (self.estimation_thread is not None and 503 self.estimation_thread.isrunning()): 504 self.estimation_thread.stop() 659 505 pr = self._calculator.clone() 660 506 # Skip the slit settings for the estimation … … 664 510 nfunc = self.getNFunc() 665 511 666 self.estimationThreadNT = EstimateNT(pr, nfunc, 667 error_func=self._threadError, 668 completefn=self._estimateNTCompleted, 669 updatefn=None) 670 self.estimationThreadNT.queue() 671 self.estimationThreadNT.ready(2.5) 672 673 def stopEstimateNTThread(self): 674 if (self.estimationThreadNT is not None and 675 self.estimationThreadNT.isrunning()): 676 self.estimationThreadNT.stop() 512 self.estimation_thread = EstimateNT(pr, nfunc, 513 error_func=self._threadError, 514 completefn=self._estimateNTCompleted, 515 updatefn=None) 516 self.estimation_thread.queue() 517 self.estimation_thread.ready(2.5) 677 518 678 519 def performEstimate(self): … … 682 523 from .Thread import EstimatePr 683 524 525 self.startThread() 526 684 527 # If a thread is already started, stop it 685 self.stopEstimationThread() 686 687 self.estimationThread = EstimatePr(self._calculator.clone(), 688 self.getNFunc(), 689 error_func=self._threadError, 690 completefn=self._estimateCompleted, 691 updatefn=None) 692 self.estimationThread.queue() 693 self.estimationThread.ready(2.5) 694 695 def stopEstimationThread(self): 696 """ Stop the estimation thread if it exists and is running """ 697 if (self.estimationThread is not None and 698 self.estimationThread.isrunning()): 699 self.estimationThread.stop() 528 if (self.estimation_thread is not None and 529 self.estimation_thread.isrunning()): 530 self.estimation_thread.stop() 531 pr = self._calculator.clone() 532 nfunc = self.getNFunc() 533 self.estimation_thread = EstimatePr(pr, nfunc, 534 error_func=self._threadError, 535 completefn=self._estimateCompleted, 536 updatefn=None) 537 self.estimation_thread.queue() 538 self.estimation_thread.ready(2.5) 700 539 701 540 ###################################################################### … … 715 554 """ 716 555 alpha, message, elapsed = output_tuple 717 self._calculator.alpha = alpha 718 self._calculator.elapsed += self._calculator.elapsed 556 # Save useful info 557 self.model.setItem(WIDGETS.W_COMP_TIME, QtGui.QStandardItem("{:.4g}".format(elapsed))) 558 self.regConstantSuggestionButton.setText("{:-3.2g}".format(alpha)) 559 self.regConstantSuggestionButton.setEnabled(True) 719 560 if message: 720 logg er.info(message)561 logging.info(message) 721 562 self.performEstimateNT() 722 563 … … 735 576 """ 736 577 nterms, alpha, message, elapsed = output_tuple 737 self._calculator.elapsed += elapsed738 self._calculator.suggested_alpha = alpha739 self.nTermsSuggested = nterms740 578 # Save useful info 741 self.updateGuiValues() 579 self.noOfTermsSuggestionButton.setText("{:n}".format(nterms)) 580 self.noOfTermsSuggestionButton.setEnabled(True) 581 self.regConstantSuggestionButton.setText("{:.3g}".format(alpha)) 582 self.regConstantSuggestionButton.setEnabled(True) 583 self.model.setItem(WIDGETS.W_COMP_TIME, QtGui.QStandardItem("{:.2g}".format(elapsed))) 742 584 if message: 743 logger.info(message) 744 if self.isBatch: 745 self.acceptAlpha() 746 self.acceptNoTerms() 747 self.startThread() 585 logging.info(message) 748 586 749 587 def _calculateCompleted(self, out, cov, pr, elapsed): … … 767 605 pr.elapsed = elapsed 768 606 607 # Show result on control panel 608 self.model.setItem(WIDGETS.W_RG, QtGui.QStandardItem("{:.3g}".format(pr.rg(out)))) 609 self.model.setItem(WIDGETS.W_I_ZERO, QtGui.QStandardItem("{:.3g}".format(pr.iq0(out)))) 610 self.model.setItem(WIDGETS.W_BACKGROUND_INPUT, 611 QtGui.QStandardItem("{:.3f}".format(pr.est_bck))) 612 self.model.setItem(WIDGETS.W_BACKGROUND_OUTPUT, QtGui.QStandardItem("{:.3g}".format(pr.background))) 613 self.model.setItem(WIDGETS.W_CHI_SQUARED, QtGui.QStandardItem("{:.3g}".format(pr.chi2[0]))) 614 self.model.setItem(WIDGETS.W_COMP_TIME, QtGui.QStandardItem("{:.2g}".format(elapsed))) 615 self.model.setItem(WIDGETS.W_OSCILLATION, QtGui.QStandardItem("{:.3g}".format(pr.oscillations(out)))) 616 self.model.setItem(WIDGETS.W_POS_FRACTION, QtGui.QStandardItem("{:.3g}".format(pr.get_positive(out)))) 617 self.model.setItem(WIDGETS.W_SIGMA_POS_FRACTION, 618 QtGui.QStandardItem("{:.3g}".format(pr.get_pos_err(out, cov)))) 619 769 620 # Save Pr invertor 770 621 self._calculator = pr 771 772 # Update P(r) and fit plots 773 self.prPlot = self.logic.newPRPlot(out, self._calculator, cov) 774 self.prPlot.filename = self.logic.data.filename 775 self.dataPlot = self.logic.new1DPlot(out, self._calculator) 776 self.dataPlot.filename = self.logic.data.filename 777 778 # Udpate internals and GUI 779 self.updateDataList(self._data) 780 if self.isBatch: 781 self.batchComplete.append(self.dataList.currentIndex()) 782 self.startNextBatchItem() 622 # Append data to data list 623 self._data_list[self._data] = self._calculator.clone() 624 625 # Update model dict 626 self._models[self.model_item] = self.model 627 628 # Create new P(r) and fit plots 629 if self.pr_plot is None: 630 self.pr_plot = self.logic.newPRPlot(out, self._calculator, cov) 631 self.pr_plot_list[self._data] = self.pr_plot 783 632 else: 784 self.isCalculating = False 785 self.updateGuiValues() 633 # FIXME: this should update the existing plot, not create a new one 634 self.pr_plot = self.logic.newPRPlot(out, self._calculator, cov) 635 self.pr_plot_list[self._data] = self.pr_plot 636 if self.data_plot is None: 637 self.data_plot = self.logic.new1DPlot(out, self._calculator) 638 self.data_plot_list[self._data] = self.data_plot 639 else: 640 # FIXME: this should update the existing plot, not create a new one 641 self.data_plot = self.logic.new1DPlot(out, self._calculator) 642 self.data_plot_list[self._data] = self.data_plot 786 643 787 644 def _threadError(self, error): … … 789 646 Call-back method for calculation errors 790 647 """ 791 logger.error(error) 792 if self.isBatch: 793 self.startNextBatchItem() 794 else: 795 self.stopCalculation() 648 logging.warning(error) -
src/sas/qtgui/Perspectives/Inversion/InversionUtils.py
- Property mode changed from 100644 to 100755
-
src/sas/qtgui/Perspectives/Inversion/Thread.py
- Property mode changed from 100644 to 100755
-
src/sas/qtgui/Perspectives/Inversion/UI/TabbedInversionUI.ui
- Property mode changed from 100644 to 100755
r72ecbdf2 r8f83719f 317 317 <widget class="QLabel" name="label_22"> 318 318 <property name="text"> 319 <string />319 <string> </string> 320 320 </property> 321 321 </widget> … … 754 754 </item> 755 755 <item> 756 <widget class="QPushButton" name="stopButton">757 <property name="text">758 <string>Stop P(r)</string>759 </property>760 </widget>761 </item>762 <item>763 756 <spacer name="horizontalSpacer"> 764 757 <property name="orientation"> -
src/sas/qtgui/Perspectives/Inversion/UI/__init__.py
- Property mode changed from 100644 to 100755
-
src/sas/qtgui/Perspectives/Inversion/UnitTesting/InversionPerspectiveTest.py
r72ecbdf2 r50bfab0 1 import time1 import sys 2 2 import unittest 3 import logging 3 4 from unittest.mock import MagicMock 4 5 5 6 from PyQt5 import QtGui, QtWidgets 7 from PyQt5.QtTest import QTest 8 from PyQt5 import QtCore 6 9 7 from sas.qtgui.Utilities.GuiUtils import * 10 import sas.qtgui.path_prepare 8 11 from sas.qtgui.Perspectives.Inversion.InversionPerspective import InversionWindow 9 from sas. qtgui.Perspectives.Inversion.InversionUtils import WIDGETS12 from sas.sascalc.dataloader.loader import Loader 10 13 from sas.qtgui.Plotting.PlotterData import Data1D 11 14 15 from sas.qtgui.MainWindow.DataManager import DataManager 16 import sas.qtgui.Utilities.LocalConfig 12 17 import sas.qtgui.Utilities.GuiUtils as GuiUtils 13 18 … … 15 20 app = QtWidgets.QApplication(sys.argv) 16 21 17 18 class dummy_manager(object):19 HELP_DIRECTORY_LOCATION = "html"20 communicate = Communicate()21 22 def communicator(self):23 return self.communicate24 25 26 22 class InversionTest(unittest.TestCase): 27 """ Test the Inversion Perspective GUI """ 28 23 '''Test the Inversion Interface''' 29 24 def setUp(self): 30 """ Create the InversionWindow """ 31 self.widget = InversionWindow(dummy_manager()) 32 self.widget.show() 33 self.fakeData1 = GuiUtils.HashableStandardItem("A") 34 self.fakeData2 = GuiUtils.HashableStandardItem("B") 35 reference_data1 = Data1D(x=[0.1, 0.2], y=[0.0, 0.0], dy=[0.0, 0.0]) 36 reference_data1.filename = "Test A" 37 reference_data2 = Data1D(x=[0.1, 0.2], y=[0.0, 0.0], dy=[0.0, 0.0]) 38 reference_data2.filename = "Test B" 39 GuiUtils.updateModelItem(self.fakeData1, reference_data1) 40 GuiUtils.updateModelItem(self.fakeData2, reference_data2) 25 '''Create the InversionWindow''' 26 self.widget = InversionWindow(None) 41 27 42 28 def tearDown(self): 43 """ Destroy the InversionWindow """ 44 self.widget.setClosable(False) 29 '''Destroy the InversionWindow''' 45 30 self.widget.close() 46 31 self.widget = None 47 32 48 def removeAllData(self): 49 """ Cleanup method to restore widget to its base state """ 50 if len(self.widget.dataList) > 0: 51 remove_me = list(self.widget._dataList.keys()) 52 self.widget.removeData(remove_me) 53 54 def baseGUIState(self): 55 """ Testing base state of Inversion """ 56 # base class information 33 def testDefaults(self): 34 '''Test the GUI in its default state''' 57 35 self.assertIsInstance(self.widget, QtWidgets.QWidget) 58 36 self.assertEqual(self.widget.windowTitle(), "P(r) Inversion Perspective") 59 self.assertFalse(self.widget.isClosable()) 60 self.assertFalse(self.widget.isCalculating) 61 # mapper 37 self.assertEqual(self.widget.model.columnCount(), 1) 38 self.assertEqual(self.widget.model.rowCount(), 22) 39 self.assertFalse(self.widget.calculateAllButton.isEnabled()) 40 self.assertFalse(self.widget.calculateThisButton.isEnabled()) 62 41 self.assertIsInstance(self.widget.mapper, QtWidgets.QDataWidgetMapper) 42 # make sure the model is assigned and at least the data is mapped 43 self.assertEqual(self.widget.mapper.model(), self.widget.model) 63 44 self.assertNotEqual(self.widget.mapper.mappedSection(self.widget.dataList), -1) 45 64 46 # validators 65 47 self.assertIsInstance(self.widget.noOfTermsInput.validator(), QtGui.QIntValidator) … … 70 52 self.assertIsInstance(self.widget.slitHeightInput.validator(), QtGui.QDoubleValidator) 71 53 self.assertIsInstance(self.widget.slitWidthInput.validator(), QtGui.QDoubleValidator) 54 72 55 # model 73 56 self.assertEqual(self.widget.model.rowCount(), 22) 74 self.assertEqual(self.widget.model.columnCount(), 1)75 self.assertEqual(self.widget.mapper.model(), self.widget.model)76 # buttons77 self.assertFalse(self.widget.calculateThisButton.isEnabled())78 self.assertFalse(self.widget.removeButton.isEnabled())79 self.assertTrue(self.widget.stopButton.isEnabled())80 self.assertFalse(self.widget.stopButton.isVisible())81 self.assertFalse(self.widget.regConstantSuggestionButton.isEnabled())82 self.assertFalse(self.widget.noOfTermsSuggestionButton.isEnabled())83 self.assertFalse(self.widget.explorerButton.isEnabled())84 self.assertTrue(self.widget.helpButton.isEnabled())85 # extra windows and charts86 self.assertIsNone(self.widget.dmaxWindow)87 self.assertIsNone(self.widget.prPlot)88 self.assertIsNone(self.widget.dataPlot)89 # threads90 self.assertIsNone(self.widget.calcThread)91 self.assertIsNone(self.widget.estimationThread)92 self.assertIsNone(self.widget.estimationThreadNT)93 57 94 def baseBatchState(self):95 """ Testing the base batch fitting state """96 self.assertTrue(self.widget.allowBatch())97 self.assertFalse(self.widget.isBatch)98 self.assertIsNone(self.widget.batchResultsWindow)99 self.assertFalse(self.widget.calculateAllButton.isEnabled())100 self.assertEqual(len(self.widget.batchResults), 0)101 self.assertEqual(len(self.widget.batchComplete), 0)102 self.widget. closeBatchResults()103 self. assertIsNone(self.widget.batchResultsWindow)58 def testSetData(self): 59 ''' Check if sending data works as expected''' 60 # Create dummy data 61 item1 = GuiUtils.HashableStandardItem("A") 62 item2 = GuiUtils.HashableStandardItem("B") 63 reference_data = Data1D(x=[0.1, 0.2], y=[0.0, 0.0], dy=[0.0, 0.0]) 64 GuiUtils.updateModelItem(item1, [reference_data]) 65 GuiUtils.updateModelItem(item2, [reference_data]) 66 self.widget.performEstimate = MagicMock() 67 self.widget.setData([item1, item2]) 104 68 105 def zeroDataSetState(self): 106 """ Testing the base data state of the GUI """ 107 # data variables 108 self.assertIsNone(self.widget._data) 109 self.assertEqual(len(self.widget._dataList), 0) 110 self.assertEqual(self.widget.nTermsSuggested, 10) 111 # inputs 112 self.assertEqual(len(self.widget.dataList), 0) 113 self.assertEqual(self.widget.backgroundInput.text(), "0.0") 114 self.assertEqual(self.widget.minQInput.text(), "") 115 self.assertEqual(self.widget.maxQInput.text(), "") 116 self.assertEqual(self.widget.regularizationConstantInput.text(), "0.0001") 117 self.assertEqual(self.widget.noOfTermsInput.text(), "10") 118 self.assertEqual(self.widget.maxDistanceInput.text(), "140.0") 69 # Test the globals 70 self.assertEqual(len(self.widget._data_list), 2) 71 self.assertEqual(len(self.widget.data_plot_list), 2) 72 self.assertEqual(len(self.widget.pr_plot_list), 2) 73 self.assertEqual(self.widget.dataList.count(), 2) 119 74 120 def oneDataSetState(self): 121 """ Testing the base data state of the GUI """ 122 # Test the globals after first sent 123 self.assertEqual(len(self.widget._dataList), 1) 124 self.assertEqual(self.widget.dataList.count(), 1) 125 # See that the buttons are now enabled properly 126 self.widget.enableButtons() 127 self.assertFalse(self.widget.calculateAllButton.isEnabled()) 75 # See that the buttons are now enabled 76 self.assertTrue(self.widget.calculateAllButton.isEnabled()) 128 77 self.assertTrue(self.widget.calculateThisButton.isEnabled()) 129 78 self.assertTrue(self.widget.removeButton.isEnabled()) 130 79 self.assertTrue(self.widget.explorerButton.isEnabled()) 131 80 132 def twoDataSetState(self):133 """ Testing the base data state of the GUI """134 # Test the globals after first sent135 self.assertEqual(len(self.widget._dataList), 2)136 self.assertEqual(self.widget.dataList.count(), 2)137 # See that the buttons are now enabled properly138 self.widget.enableButtons()139 self.assertTrue(self.widget.calculateThisButton.isEnabled())140 self.assertTrue(self.widget.calculateAllButton.isEnabled())141 self. assertTrue(self.widget.removeButton.isEnabled())142 self. assertTrue(self.widget.explorerButton.isEnabled())81 def notestRemoveData(self): 82 ''' Test data removal from widget ''' 83 # Create dummy data 84 item1 = GuiUtils.HashableStandardItem("A") 85 item2 = GuiUtils.HashableStandardItem("B") 86 reference_data1 = Data1D(x=[0.1, 0.2], y=[0.0, 0.0], dy=[0.0, 0.0]) 87 reference_data2 = Data1D(x=[0.1, 0.2], y=[0.0, 0.0], dy=[0.0, 0.0]) 88 GuiUtils.updateModelItem(item1, [reference_data1]) 89 GuiUtils.updateModelItem(item2, [reference_data2]) 90 self.widget.performEstimate = MagicMock() 91 self.widget.setData([item1, item2]) 143 92 144 def testDefaults(self): 145 """ Test the GUI in its default state """ 146 self.baseGUIState() 147 self.zeroDataSetState() 148 self.baseBatchState() 149 self.removeAllData() 93 # Remove data 0 94 self.widget.removeData() 95 96 # Test the globals 97 self.assertEqual(len(self.widget._data_list), 1) 98 self.assertEqual(len(self.widget.data_plot_list), 1) 99 self.assertEqual(len(self.widget.pr_plot_list), 1) 100 self.assertEqual(self.widget.dataList.count(), 1) 101 150 102 151 103 def testAllowBatch(self): 152 """ Batch P(r) Tests """ 153 self.baseBatchState() 154 self.widget.setData([self.fakeData1]) 155 self.oneDataSetState() 156 self.widget.setData([self.fakeData2]) 157 self.twoDataSetState() 158 self.widget.calculateAllButton.click() 159 self.assertTrue(self.widget.isCalculating) 160 self.assertTrue(self.widget.isBatch) 161 self.assertTrue(self.widget.stopButton.isVisible()) 162 self.assertTrue(self.widget.stopButton.isEnabled()) 163 self.assertIsNotNone(self.widget.batchResultsWindow) 164 self.assertTrue(self.widget.batchResultsWindow.cmdHelp.isEnabled()) 165 self.assertEqual(self.widget.batchResultsWindow.tblParams.columnCount(), 9) 166 self.assertEqual(self.widget.batchResultsWindow.tblParams.rowCount(), 2) 167 # Test stop button 168 self.widget.stopButton.click() 169 self.assertTrue(self.widget.batchResultsWindow.isVisible()) 170 self.assertFalse(self.widget.stopButton.isVisible()) 171 self.assertTrue(self.widget.stopButton.isEnabled()) 172 self.assertFalse(self.widget.isBatch) 173 self.assertFalse(self.widget.isCalculating) 174 self.widget.batchResultsWindow.close() 175 self.assertIsNone(self.widget.batchResultsWindow) 176 # Last test 177 self.removeAllData() 178 self.baseBatchState() 104 ''' Batch is allowed for this perspective''' 105 self.assertTrue(self.widget.allowBatch()) 179 106 180 def testSetData(self): 181 """ Check if sending data works as expected """ 182 self.zeroDataSetState() 183 self.widget.setData([self.fakeData1]) 184 self.oneDataSetState() 185 self.widget.setData([self.fakeData1]) 186 self.oneDataSetState() 187 self.widget.setData([self.fakeData2]) 188 self.twoDataSetState() 189 self.removeAllData() 190 self.zeroDataSetState() 191 self.removeAllData() 107 def notestModelChanged(self): 108 ''' test the model update ''' 109 # unfinished functionality 110 pass 192 111 193 def testRemoveData(self): 194 """ Test data removal from widget """ 195 self.widget.setData([self.fakeData1, self.fakeData2]) 196 self.twoDataSetState() 197 # Remove data 0 198 self.widget.removeData() 199 self.oneDataSetState() 200 self.removeAllData() 112 def notestHelp(self): 113 ''' test help widget show ''' 114 # unfinished functionality 115 pass 201 116 202 def testClose(self): 203 """ Test methods related to closing the window """ 204 self.assertFalse(self.widget.isClosable()) 205 self.widget.close() 206 self.assertTrue(self.widget.isMinimized()) 207 self.assertIsNone(self.widget.dmaxWindow) 208 self.assertIsNone(self.widget.batchResultsWindow) 209 self.widget.setClosable(False) 210 self.assertFalse(self.widget.isClosable()) 211 self.widget.close() 212 self.assertTrue(self.widget.isMinimized()) 213 self.widget.setClosable(True) 214 self.assertTrue(self.widget.isClosable()) 215 self.widget.setClosable() 216 self.assertTrue(self.widget.isClosable()) 217 self.removeAllData() 117 def testOpenExplorerWindow(self): 118 ''' open Dx window ''' 119 self.widget.openExplorerWindow() 120 self.assertTrue(self.widget.dmaxWindow.isVisible()) 218 121 219 122 def testGetNFunc(self): 220 """ test nfunc getter """123 ''' test nfunc getter ''' 221 124 # Float 222 125 self.widget.noOfTermsInput.setText("10.0") … … 229 132 self.widget.noOfTermsInput.setText("") 230 133 n = self.widget.getNFunc() 231 self.assertEqual(cm.output, ['ERROR: sas.qtgui.Perspectives.Inversion.InversionPerspective:Incorrect number of terms specified: '])134 self.assertEqual(cm.output, ['ERROR:root:Incorrect number of terms specified: ']) 232 135 self.assertEqual(self.widget.getNFunc(), 10) 233 136 # string … … 235 138 self.widget.noOfTermsInput.setText("Nordvest Pizza") 236 139 n = self.widget.getNFunc() 237 self.assertEqual(cm.output, ['ERROR: sas.qtgui.Perspectives.Inversion.InversionPerspective:Incorrect number of terms specified: Nordvest Pizza'])140 self.assertEqual(cm.output, ['ERROR:root:Incorrect number of terms specified: Nordvest Pizza']) 238 141 self.assertEqual(self.widget.getNFunc(), 10) 239 self.removeAllData()240 142 241 143 def testSetCurrentData(self): 242 """ test current data setter """ 243 self.widget.setData([self.fakeData1, self.fakeData2]) 144 ''' test current data setter ''' 145 # Create dummy data 146 item1 = GuiUtils.HashableStandardItem("A") 147 item2 = GuiUtils.HashableStandardItem("B") 148 reference_data1 = Data1D(x=[0.1, 0.2], y=[0.0, 0.0], dy=[0.0, 0.0]) 149 reference_data2 = Data1D(x=[0.1, 0.2], y=[0.0, 0.0], dy=[0.0, 0.0]) 150 GuiUtils.updateModelItem(item1, [reference_data1]) 151 GuiUtils.updateModelItem(item2, [reference_data2]) 152 self.widget.performEstimate = MagicMock() 153 self.widget.setData([item1, item2]) 244 154 245 155 # Check that the current data is reference2 246 self.assertEqual(self.widget._data, self.fakeData2) 156 self.assertEqual(self.widget._data, item2) 157 247 158 # Set the ref to none 248 159 self.widget.setCurrentData(None) 249 self.assertEqual(self.widget._data, self.fakeData2) 160 self.assertEqual(self.widget._data, item2) 161 250 162 # Set the ref to wrong type 251 163 with self.assertRaises(AttributeError): 252 164 self.widget.setCurrentData("Afandi Kebab") 253 # Set the reference to ref1254 self.widget.setCurrentData(self.fakeData1)255 self.assertEqual(self.widget._data, self.fakeData1)256 self.removeAllData()257 165 258 def testModelChanged(self): 259 """ Test setting the input and the model and vice-versa """ 260 # Initial values 261 self.assertEqual(self.widget._calculator.get_dmax(), 140.0) 262 self.assertEqual(self.widget._calculator.get_qmax(), -1.0) 263 self.assertEqual(self.widget._calculator.get_qmin(), -1.0) 264 self.assertEqual(self.widget._calculator.slit_height, 0.0) 265 self.assertEqual(self.widget._calculator.slit_width, 0.0) 266 self.assertEqual(self.widget._calculator.alpha, 0.0001) 267 # Set new values 268 self.widget.maxDistanceInput.setText("1.0") 269 self.widget.maxQInput.setText("3.0") 270 self.widget.minQInput.setText("5.0") 271 self.widget.slitHeightInput.setText("7.0") 272 self.widget.slitWidthInput.setText("9.0") 273 self.widget.regularizationConstantInput.setText("11.0") 274 # Check new values 275 self.assertEqual(self.widget._calculator.get_dmax(), 1.0) 276 self.assertEqual(self.widget._calculator.get_qmax(), 3.0) 277 self.assertEqual(self.widget._calculator.get_qmin(), 5.0) 278 self.assertEqual(self.widget._calculator.slit_height, 7.0) 279 self.assertEqual(self.widget._calculator.slit_width, 9.0) 280 self.assertEqual(self.widget._calculator.alpha, 11.0) 281 # Change model directly 282 self.widget.model.setItem(WIDGETS.W_MAX_DIST, QtGui.QStandardItem("2.0")) 283 self.widget.model.setItem(WIDGETS.W_QMIN, QtGui.QStandardItem("4.0")) 284 self.widget.model.setItem(WIDGETS.W_QMAX, QtGui.QStandardItem("6.0")) 285 self.widget.model.setItem(WIDGETS.W_SLIT_HEIGHT, QtGui.QStandardItem("8.0")) 286 self.widget.model.setItem(WIDGETS.W_SLIT_WIDTH, QtGui.QStandardItem("10.0")) 287 self.widget.model.setItem(WIDGETS.W_REGULARIZATION, QtGui.QStandardItem("12.0")) 288 # Check values 289 self.assertEqual(self.widget._calculator.get_dmax(), 2.0) 290 self.assertEqual(self.widget._calculator.get_qmin(), 4.0) 291 self.assertEqual(self.widget._calculator.get_qmax(), 6.0) 292 self.assertEqual(self.widget._calculator.slit_height, 8.0) 293 self.assertEqual(self.widget._calculator.slit_width, 10.0) 294 self.assertEqual(self.widget._calculator.alpha, 12.0) 295 self.removeAllData() 296 297 def testOpenExplorerWindow(self): 298 """ open Dx window """ 299 self.assertIsNone(self.widget.dmaxWindow) 300 self.assertFalse(self.widget.explorerButton.isEnabled()) 301 self.widget.openExplorerWindow() 302 self.assertIsNotNone(self.widget.dmaxWindow) 303 self.assertTrue(self.widget.dmaxWindow.isVisible()) 304 self.assertTrue(self.widget.dmaxWindow.windowTitle() == "Dââ Explorer") 305 166 # Set the reference to ref2 167 self.widget.setCurrentData(item1) 168 self.assertEqual(self.widget._data, item1) 306 169 307 170 if __name__ == "__main__": -
src/sas/qtgui/Perspectives/Inversion/__init__.py
- Property mode changed from 100644 to 100755
-
src/sas/qtgui/Perspectives/Inversion/media/pr_help.rst
r417c03f r417c03f 68 68 .. ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ 69 69 70 .. _Batch_Pr_Mode:71 72 Batch P(r) inversion73 --------------------74 75 The p(r) calculator accepts any number of data sets, and supports batch fitting76 for all data sets in the p(r) data set combo box. Switching between data sets in77 the combo box allows for individual fits and review of fit parameters. The78 displayed plots will also update to include the latest fit results for the79 selected data set.80 81 The 'Calculate All' button will run the p(r) calculation for each file,82 sequentially. The calculator will estimate the number of terms, background, and83 regularization constant for each file prior to running the p(r) calculation.84 The final results for all data sets are then presented in a savable data table.85 86 .. ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ87 88 70 Reference 89 71 --------- … … 94 76 .. ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ 95 77 96 .. note:: This help document was last modified by Jeff Krzywon, 18 April 201878 .. note:: This help document was last modified by Paul Butler, 05 September, 2016 -
src/sas/qtgui/Perspectives/__init__.py
- Property mode changed from 100644 to 100755
-
src/sas/qtgui/Plotting/ConvertUnits.py
- Property mode changed from 100755 to 100644
-
src/sas/qtgui/Plotting/DataTransform.py
- Property mode changed from 100755 to 100644
-
src/sas/qtgui/Plotting/Slicers/Arc.py
- Property mode changed from 100755 to 100644
-
src/sas/qtgui/Utilities/GridPanel.py
raed0532 raed0532 410 410 index += 1 411 411 412 413 class BatchInversionOutputPanel(BatchOutputPanel):414 """415 Class for stateless grid-like printout of P(r) parameters for any number416 of data sets417 """418 def __init__(self, parent = None, output_data=None):419 420 super(BatchInversionOutputPanel, self).__init__(parent, output_data)421 _translate = QtCore.QCoreApplication.translate422 self.setWindowTitle(_translate("GridPanelUI", "Batch P(r) Results"))423 424 def setupTable(self, data):425 """426 Create tablewidget items and show them, based on params427 """428 # headers429 param_list = ['Filename', 'Rg [Ã430 ]', 'Chi^2/dof', 'I(Q=0)', 'Oscillations',431 'Background [Ã432 ^-1]', 'P+ Fraction', 'P+1-theta Fraction',433 'Calc. Time [sec]']434 435 keys = data.keys()436 rows = len(keys)437 columns = len(param_list)438 self.tblParams.setColumnCount(columns)439 self.tblParams.setRowCount(rows)440 441 for i, param in enumerate(param_list):442 self.tblParams.setHorizontalHeaderItem(i, QtWidgets.QTableWidgetItem(param))443 444 # first - Chi2 and data filename445 for i_row, (filename, pr) in enumerate(data.items()):446 out = pr.out447 cov = pr.cov448 if out is None:449 logging.warning("P(r) for {} did not converge.".format(filename))450 continue451 self.tblParams.setItem(i_row, 0, QtWidgets.QTableWidgetItem(452 "{}".format(filename)))453 self.tblParams.setItem(i_row, 1, QtWidgets.QTableWidgetItem(454 "{:.3g}".format(pr.rg(out))))455 self.tblParams.setItem(i_row, 2, QtWidgets.QTableWidgetItem(456 "{:.3g}".format(pr.chi2[0])))457 self.tblParams.setItem(i_row, 3, QtWidgets.QTableWidgetItem(458 "{:.3g}".format(pr.iq0(out))))459 self.tblParams.setItem(i_row, 4, QtWidgets.QTableWidgetItem(460 "{:.3g}".format(pr.oscillations(out))))461 self.tblParams.setItem(i_row, 5, QtWidgets.QTableWidgetItem(462 "{:.3g}".format(pr.background)))463 self.tblParams.setItem(i_row, 6, QtWidgets.QTableWidgetItem(464 "{:.3g}".format(pr.get_positive(out))))465 self.tblParams.setItem(i_row, 7, QtWidgets.QTableWidgetItem(466 "{:.3g}".format(pr.get_pos_err(out, cov))))467 self.tblParams.setItem(i_row, 8, QtWidgets.QTableWidgetItem(468 "{:.2g}".format(pr.elapsed)))469 470 self.tblParams.resizeColumnsToContents()471 472 @classmethod473 def onHelp(cls):474 """475 Open a local url in the default browser476 """477 location = GuiUtils.HELP_DIRECTORY_LOCATION478 url = "/user/sasgui/perspectives/pr/pr_help.html#batch-pr-mode"479 try:480 webbrowser.open('file://' + os.path.realpath(location + url))481 except webbrowser.Error as ex:482 logging.warning("Cannot display help. %s" % ex)483 484 def closeEvent(self, event):485 """Tell the parent window the window closed"""486 self.parent.batchResultsWindow = None487 event.accept() -
src/sas/sascalc/pr/invertor.py
r6da860a r8f83719f 481 481 float(chi2) 482 482 except: 483 chi2 = -1.0483 chi2 = [-1.0] 484 484 self.chi2 = chi2 485 485
Note: See TracChangeset
for help on using the changeset viewer.