Changes in / [80468f6:3c6ecd9] in sasview
- Files:
-
- 3 added
- 18 edited
Legend:
- Unmodified
- Added
- Removed
-
.gitignore
r948484f r846a063 29 29 default_categories.json 30 30 /setup.cfg 31 **/UI/*.py 32 !**/UI/__init__.py 31 33 32 34 # doc build -
run.py
ra3221b6 ra3221b6 141 141 sys.path.append(build_path) 142 142 143 # Run the UI conversion tool 144 import sas.qtgui.convertUI 145 146 143 147 if __name__ == "__main__": 144 148 # Need to add absolute path before actual prepare call, -
src/sas/qtgui/Perspectives/Inversion/DMaxExplorerWidget.py
rf4480f0 rb0ba43e 8 8 9 9 # global 10 import sys11 import os12 10 import logging 13 11 import numpy as np … … 42 40 super(DmaxWindow, self).__init__() 43 41 self.setupUi(self) 42 self.parent = parent 44 43 45 44 self.setWindowTitle("Dââ Explorer") … … 50 49 51 50 self.plot = PlotterWidget(self, self) 52 self.hasPlot = None51 self.hasPlot = False 53 52 self.verticalLayout.insertWidget(0, self.plot) 54 53 55 54 # Let's choose the Standard Item Model. 56 55 self.model = QtGui.QStandardItemModel(self) 57 self.mapper = None56 self.mapper = QtWidgets.QDataWidgetMapper(self) 58 57 59 58 # Add validators on line edits 60 59 self.setupValidators() 61 60 62 # #Connect buttons to slots.63 # #Needs to be done early so default values propagate properly.61 # Connect buttons to slots. 62 # Needs to be done early so default values propagate properly. 64 63 self.setupSlots() 65 64 … … 82 81 83 82 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) 85 87 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) 86 90 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) 87 93 self.model.setItem(W.VARIABLE, QtGui.QStandardItem( "ϲ/dof")) 94 self.model.blockSignals(False) 88 95 89 96 def setupMapper(self): 90 self.mapper = QtWidgets.QDataWidgetMapper(self)91 97 self.mapper.setOrientation(QtCore.Qt.Vertical) 92 98 self.mapper.setModel(self.model) … … 111 117 chi2 = [] 112 118 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())) 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) 116 128 117 129 original = self.pr_state.d_max … … 132 144 msg = "ExploreDialog: inversion failed " 133 145 msg += "for D_max=%s\n%s" % (str(x), ex) 134 print(msg)135 146 logger.error(msg) 136 147 … … 142 153 msg = "ExploreDialog: inversion failed " 143 154 msg += "for D_max=%s\n%s" % (str(x), ex) 144 print(msg)145 155 logger.error(msg) 146 156 … … 151 161 if plotter == "ϲ/dof": 152 162 ys = chi2 153 y_label = "\ chi^2/dof"163 y_label = "\\chi^2/dof" 154 164 y_unit = "a.u." 155 165 elif plotter == "I(Q=0)": 156 166 ys = iq0 157 167 y_label = "I(q=0)" 158 y_unit = "\ AA^{-1}"168 y_unit = "\\AA^{-1}" 159 169 elif plotter == "Rg": 160 170 ys = rg 161 171 y_label = "R_g" 162 y_unit = "\ AA"172 y_unit = "\\AA" 163 173 elif plotter == "Oscillation parameter": 164 174 ys = osc … … 168 178 ys = bck 169 179 y_label = "Bckg" 170 y_unit = "\ AA^{-1}"180 y_unit = "\\AA^{-1}" 171 181 elif plotter == "Positive Fraction": 172 182 ys = pos … … 175 185 else: 176 186 ys = pos_err 177 y_label = "P^{+}_{1\ sigma}"187 y_label = "P^{+}_{1\\sigma}" 178 188 y_unit = "a.u." 179 189 … … 188 198 data._yunit = y_unit 189 199 self.plot.plot(data=data, marker="-") 200 201 def closeEvent(self, event): 202 """Override close event""" 203 self.parent.dmaxWindow = None 204 event.accept() -
src/sas/qtgui/Perspectives/Inversion/InversionLogic.py
- Property mode changed from 100755 to 100644
rfa81e94 r6da860a 1 1 import math 2 import pylab2 import logging 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 = 51 15 16 logger = logging.getLogger(__name__) 14 17 15 18 … … 19 22 No QStandardModelIndex here. 20 23 """ 21 22 # TODO: Add way to change this value23 _pr_n_pts = 5124 24 25 25 def __init__(self, data=None): … … 37 37 """ data setter """ 38 38 self._data = value 39 self.data_is_loaded = True39 self.data_is_loaded = (self._data is not None) 40 40 41 41 def isLoadedData(self): … … 63 63 maxq = pr.q_max 64 64 65 x = pylab.arange(minq, maxq, maxq / 301.0)65 x = np.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 print(("Error getting error", value, x[i]))75 logger.log(("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 = pylab.arange(minq, maxq, maxq / 301.0)91 x = np.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 print(("Error getting error", value, x[i]))101 logger.log(("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 results118 """119 120 qtemp = pr.x121 if q is not None:122 qtemp = q123 124 # Make a plot125 maxq = max(qtemp)126 127 minq = min(qtemp)128 129 # Check for user min/max130 if pr.q_min is not None and maxq >= pr.q_min >= minq:131 minq = pr.q_min132 if pr.q_max is not None and maxq >= pr.q_max >= minq:133 maxq = pr.q_max134 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] = value141 try:142 err[i] = math.sqrt(math.fabs(value))143 except:144 err[i] = 1.0145 print(("Error getting error", value, x[i]))146 147 plot.x = x148 plot.y = y149 150 # If we have used slit smearing, plot the smeared I(q) too151 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] = value158 try:159 err[i] = math.sqrt(math.fabs(value))160 except:161 err[i] = 1.0162 print(("Error getting error", value, x[i]))163 164 plot.x = x165 plot.y = y166 167 return plot168 169 115 def newPRPlot(self, out, pr, cov=None): 170 116 """ 171 117 """ 172 118 # Show P(r) 173 x = pylab.arange(0.0, pr.d_max, pr.d_max / self._pr_n_pts)119 x = np.arange(0.0, pr.d_max, pr.d_max / PR_PLOT_PTS) 174 120 175 121 y = np.zeros(len(x)) … … 193 139 y[i] = value 194 140 195 # if self._normalize_output == True:196 # y = y / total197 # dy = dy / total198 # elif self._scale_output_unity == True:199 # y = y / pmax200 # dy = dy / pmax201 202 141 if cov2 is None: 203 142 new_plot = Data1D(x, y) … … 209 148 new_plot.title = "P(r) fit" 210 149 new_plot.id = PR_FIT_LABEL 211 # Make sure that the plot is linear 212 new_plot.xtransform = "x" 213 new_plot.ytransform = "y" 150 new_plot.scale = "linear" 214 151 new_plot.group_id = GROUP_ID_PR_FIT 215 152 216 153 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.0225 pmax = 0.0226 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) value236 if value > pmax:237 pmax = value238 239 y[i] = value240 241 # if self._normalize_output == True:242 # y = y / total243 # dy = dy / total244 # elif self._scale_output_unity == True:245 # y = y / pmax246 # dy = dy / pmax247 plot.x = x248 plot.y = y249 250 if cov2 is not None:251 plot.dy = dy252 253 return plot254 154 255 155 def computeDataRange(self): -
src/sas/qtgui/Perspectives/Inversion/InversionPerspective.py
raed0532 rb0ba43e 1 import sys2 1 import logging 3 import pylab4 2 import numpy as np 5 3 … … 15 13 16 14 # pr inversion calculation elements 17 from sas.sascalc.dataloader.data_info import Data1D18 15 from sas.sascalc.pr.invertor import Invertor 16 # Batch calculation display 17 from sas.qtgui.Utilities.GridPanel import BatchInversionOutputPanel 18 19 19 20 20 def is_float(value): … … 25 25 return 0.0 26 26 27 27 28 NUMBER_OF_TERMS = 10 28 29 REGULARIZATION = 0.0001 29 30 BACKGROUND_INPUT = 0.0 30 31 MAX_DIST = 140.0 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 32 DICT_KEYS = ["Calculator", "PrPlot", "DataPlot"] 33 34 logger = logging.getLogger(__name__) 35 36 35 37 class InversionWindow(QtWidgets.QDialog, Ui_PrInversion): 36 38 """ … … 50 52 51 53 self._manager = parent 52 self. _model_item = QtGui.QStandardItem()53 self.communicate = GuiUtils.Communicate()54 self.communicate = parent.communicator() 55 self.communicate.dataDeletedSignal.connect(self.removeData) 54 56 55 57 self.logic = InversionLogic() 56 58 57 # Reference to Dmax window58 self.dmaxWindow = None59 60 59 # The window should not close 61 self._allow_close = False 62 60 self._allowClose = False 61 62 # Visible data items 63 63 # current QStandardItem showing on the panel 64 64 self._data = None 65 # current Data1D as referenced by self._data 66 self._data_set = None 67 68 # p(r) calculator 65 # Reference to Dmax window for self._data 66 self.dmaxWindow = None 67 # p(r) calculator for self._data 69 68 self._calculator = Invertor() 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 = {} 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 = {} 78 86 if not isinstance(data, list): 79 87 data_list = [data] 80 88 if data is not None: 81 89 for datum in data_list: 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 = {} 90 self.updateDataList(datum) 91 92 self.dataDeleted = False 97 93 98 94 self.model = QtGui.QStandardItemModel(self) 99 95 self.mapper = QtWidgets.QDataWidgetMapper(self) 96 97 # Batch fitting parameters 98 self.isBatch = False 99 self.batchResultsWindow = None 100 self.batchResults = {} 101 self.batchComplete = [] 100 102 101 103 # Add validators … … 124 126 """ 125 127 assert isinstance(value, bool) 126 self._allow_close = value 128 self._allowClose = value 129 130 def isClosable(self): 131 """ 132 Allow outsiders close this widget 133 """ 134 return self._allowClose 127 135 128 136 def closeEvent(self, event): … … 130 138 Overwrite QDialog close method to allow for custom widget close 131 139 """ 132 if self._allow_close: 140 # Close report widgets before closing/minimizing main widget 141 self.closeDMax() 142 self.closeBatchResults() 143 if self._allowClose: 133 144 # reset the closability flag 134 145 self.setClosable(value=False) … … 141 152 self.setWindowState(QtCore.Qt.WindowMinimized) 142 153 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 143 162 ###################################################################### 144 163 # Initialization routines … … 149 168 self.calculateAllButton.clicked.connect(self.startThreadAll) 150 169 self.calculateThisButton.clicked.connect(self.startThread) 170 self.stopButton.clicked.connect(self.stopCalculation) 151 171 self.removeButton.clicked.connect(self.removeData) 152 172 self.helpButton.clicked.connect(self.help) … … 157 177 self.explorerButton.clicked.connect(self.openExplorerWindow) 158 178 159 self.backgroundInput. editingFinished.connect(160 lambda: self. _calculator.set_est_bck(int(is_float(self.backgroundInput.text()))))161 self.minQInput. editingFinished.connect(179 self.backgroundInput.textChanged.connect( 180 lambda: self.set_background(self.backgroundInput.text())) 181 self.minQInput.textChanged.connect( 162 182 lambda: self._calculator.set_qmin(is_float(self.minQInput.text()))) 163 self.regularizationConstantInput. editingFinished.connect(183 self.regularizationConstantInput.textChanged.connect( 164 184 lambda: self._calculator.set_alpha(is_float(self.regularizationConstantInput.text()))) 165 self.maxDistanceInput. editingFinished.connect(185 self.maxDistanceInput.textChanged.connect( 166 186 lambda: self._calculator.set_dmax(is_float(self.maxDistanceInput.text()))) 167 self.maxQInput. editingFinished.connect(187 self.maxQInput.textChanged.connect( 168 188 lambda: self._calculator.set_qmax(is_float(self.maxQInput.text()))) 169 self.slitHeightInput. editingFinished.connect(189 self.slitHeightInput.textChanged.connect( 170 190 lambda: self._calculator.set_slit_height(is_float(self.slitHeightInput.text()))) 171 self.slitWidthInput. editingFinished.connect(172 lambda: self._calculator.set_slit_width(is_float(self.slit HeightInput.text())))191 self.slitWidthInput.textChanged.connect( 192 lambda: self._calculator.set_slit_width(is_float(self.slitWidthInput.text()))) 173 193 174 194 self.model.itemChanged.connect(self.model_changed) … … 227 247 Update boxes with initial values 228 248 """ 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) 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) 263 281 264 282 def setupWindow(self): … … 284 302 Enable buttons when data is present, else disable them 285 303 """ 286 self.calculateAllButton.setEnabled(self.logic.data_is_loaded) 287 self.calculateThisButton.setEnabled(self.logic.data_is_loaded) 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) 288 310 self.removeButton.setEnabled(self.logic.data_is_loaded) 289 self.explorerButton.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) 290 320 291 321 def populateDataComboBox(self, filename, data_ref): … … 307 337 self.regConstantSuggestionButton.text())) 308 338 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) 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)) 332 345 333 346 ###################################################################### 334 347 # GUI Interaction Events 335 348 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): 349 def updateCalculator(self): 342 350 """Update all p(r) params""" 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) 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) 346 358 347 359 def model_changed(self): … … 350 362 msg = "Unable to update P{r}. The connection between the main GUI " 351 363 msg += "and P(r) was severed. Attempting to restart P(r)." 352 logg ing.warning(msg)364 logger.warning(msg) 353 365 self.setClosable(True) 354 366 self.close() 355 InversionWindow.__init__(self.parent(), list(self._data _list.keys()))367 InversionWindow.__init__(self.parent(), list(self._dataList.keys())) 356 368 exit(0) 357 # TODO: Only send plot first time - otherwise, update in complete358 if self.pr_plot is not None:359 title = self.pr_plot.name360 GuiUtils.updateModelItemWithPlot(self._data, self.pr_plot, title)361 if self.data_plot is not None:362 title = self.data_plot.name363 GuiUtils.updateModelItemWithPlot(self._data, self.data_plot, title)364 369 if self.dmaxWindow is not None: 365 self.dmaxWindow.pr_state = self._calculator 366 self.dmaxWindow.nfunc = self.getNFunc() 367 368 self.mapper.toFirst() 370 self.dmaxWindow.nfunc = self.getNFunc() 371 self.dmaxWindow.pr_state = self._calculator 372 self.mapper.toLast() 369 373 370 374 def help(self): … … 383 387 Toggle the background between manual and estimated 384 388 """ 385 sender = self.sender()386 if sender is self.estimateBgd:389 if self.estimateBgd.isChecked(): 390 self.manualBgd.setChecked(False) 387 391 self.backgroundInput.setEnabled(False) 392 self._calculator.set_est_bck = True 393 elif self.manualBgd.isChecked(): 394 self.estimateBgd.setChecked(False) 395 self.backgroundInput.setEnabled(True) 396 self._calculator.set_est_bck = False 388 397 else: 389 self.backgroundInput.setEnabled(True)398 pass 390 399 391 400 def openExplorerWindow(self): … … 394 403 """ 395 404 from .DMaxExplorerWidget import DmaxWindow 396 self.dmaxWindow = DmaxWindow(self._calculator, self.getNFunc(), self) 405 self.dmaxWindow = DmaxWindow(pr_state=self._calculator, 406 nfunc=self.getNFunc(), 407 parent=self) 397 408 self.dmaxWindow.show() 409 410 def showBatchOutput(self): 411 """ 412 Display the batch output in tabular form 413 :param output_data: Dictionary mapping filename -> P(r) instance 414 """ 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 completed 428 if self.isBatch and self.batchResultsWindow is not None: 429 self.showBatchOutput() 430 self.isBatch = False 431 self.isCalculating = False 432 self.updateGuiValues() 398 433 399 434 ###################################################################### … … 410 445 if not isinstance(data_item, list): 411 446 msg = "Incorrect type passed to the P(r) Perspective" 412 raise AttributeError 447 raise AttributeError(msg) 413 448 414 449 for data in data_item: 415 if data in self._data _list.keys():450 if data in self._dataList.keys(): 416 451 # Don't add data if it's already in 417 return452 continue 418 453 # Create initial internal mappings 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 454 self.logic.data = GuiUtils.dataFromItem(data) 430 455 # Estimate q range 431 456 qmin, qmax = self.logic.computeDataRange() 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() 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() 438 477 439 478 def getNFunc(self): … … 442 481 nfunc = int(self.noOfTermsInput.text()) 443 482 except ValueError: 444 logging.error("Incorrect number of terms specified: %s" %self.noOfTermsInput.text()) 483 logger.error("Incorrect number of terms specified: %s" 484 %self.noOfTermsInput.text()) 445 485 self.noOfTermsInput.setText(str(NUMBER_OF_TERMS)) 446 486 nfunc = NUMBER_OF_TERMS … … 448 488 449 489 def setCurrentData(self, data_ref): 450 """Get the current data and display as necessary""" 451 490 """Get the data by reference and display as necessary""" 452 491 if data_ref is None: 453 492 return 454 455 493 if not isinstance(data_ref, QtGui.QStandardItem): 456 494 msg = "Incorrect type passed to the P(r) Perspective" 457 raise AttributeError 458 495 raise AttributeError(msg) 459 496 # Data references 460 497 self._data = data_ref 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] 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() 465 586 466 587 ###################################################################### 467 588 # Thread Creators 589 468 590 def startThreadAll(self): 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() 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() 473 618 474 619 def startThread(self): … … 479 624 480 625 # Set data before running the calculations 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() 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 486 634 pr = self._calculator.clone() 487 635 nfunc = self.getNFunc() 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) 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() 494 647 495 648 def performEstimateNT(self): … … 499 652 from .Thread import EstimateNT 500 653 654 self.updateCalculator() 655 501 656 # If a thread is already started, stop it 502 if (self.estimation_thread is not None and 503 self.estimation_thread.isrunning()): 504 self.estimation_thread.stop() 657 self.stopEstimateNTThread() 658 505 659 pr = self._calculator.clone() 506 660 # Skip the slit settings for the estimation … … 510 664 nfunc = self.getNFunc() 511 665 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) 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() 518 677 519 678 def performEstimate(self): … … 523 682 from .Thread import EstimatePr 524 683 525 self.startThread()526 527 684 # If a thread is already started, stop it 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) 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() 539 700 540 701 ###################################################################### … … 554 715 """ 555 716 alpha, message, elapsed = output_tuple 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) 717 self._calculator.alpha = alpha 718 self._calculator.elapsed += self._calculator.elapsed 560 719 if message: 561 logg ing.info(message)720 logger.info(message) 562 721 self.performEstimateNT() 563 722 … … 576 735 """ 577 736 nterms, alpha, message, elapsed = output_tuple 737 self._calculator.elapsed += elapsed 738 self._calculator.suggested_alpha = alpha 739 self.nTermsSuggested = nterms 578 740 # Save useful info 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))) 741 self.updateGuiValues() 584 742 if message: 585 logging.info(message) 743 logger.info(message) 744 if self.isBatch: 745 self.acceptAlpha() 746 self.acceptNoTerms() 747 self.startThread() 586 748 587 749 def _calculateCompleted(self, out, cov, pr, elapsed): … … 605 767 pr.elapsed = elapsed 606 768 607 # Show result on control panel608 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 620 769 # Save Pr invertor 621 770 self._calculator = pr 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 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() 632 783 else: 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 784 self.isCalculating = False 785 self.updateGuiValues() 786 787 def _threadError(self, error): 788 """ 789 Call-back method for calculation errors 790 """ 791 logger.error(error) 792 if self.isBatch: 793 self.startNextBatchItem() 639 794 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 643 644 def _threadError(self, error): 645 """ 646 Call-back method for calculation errors 647 """ 648 logging.warning(error) 795 self.stopCalculation() -
src/sas/qtgui/Perspectives/Inversion/InversionUtils.py
- Property mode changed from 100755 to 100644
-
src/sas/qtgui/Perspectives/Inversion/Thread.py
- Property mode changed from 100755 to 100644
-
src/sas/qtgui/Perspectives/Inversion/UI/TabbedInversionUI.ui
- Property mode changed from 100755 to 100644
r8f83719f r72ecbdf2 317 317 <widget class="QLabel" name="label_22"> 318 318 <property name="text"> 319 <string > </string>319 <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> 756 763 <spacer name="horizontalSpacer"> 757 764 <property name="orientation"> -
src/sas/qtgui/Perspectives/Inversion/UI/__init__.py
- Property mode changed from 100755 to 100644
-
src/sas/qtgui/Perspectives/Inversion/UnitTesting/InversionPerspectiveTest.py
r50bfab0 r72ecbdf2 1 import sys1 import time 2 2 import unittest 3 import logging4 3 from unittest.mock import MagicMock 5 4 6 5 from PyQt5 import QtGui, QtWidgets 7 from PyQt5.QtTest import QTest 8 from PyQt5 import QtCore 9 10 import sas.qtgui.path_prepare 6 7 from sas.qtgui.Utilities.GuiUtils import * 11 8 from sas.qtgui.Perspectives.Inversion.InversionPerspective import InversionWindow 12 from sas. sascalc.dataloader.loader import Loader9 from sas.qtgui.Perspectives.Inversion.InversionUtils import WIDGETS 13 10 from sas.qtgui.Plotting.PlotterData import Data1D 14 11 15 from sas.qtgui.MainWindow.DataManager import DataManager16 import sas.qtgui.Utilities.LocalConfig17 12 import sas.qtgui.Utilities.GuiUtils as GuiUtils 18 13 … … 20 15 app = QtWidgets.QApplication(sys.argv) 21 16 17 18 class dummy_manager(object): 19 HELP_DIRECTORY_LOCATION = "html" 20 communicate = Communicate() 21 22 def communicator(self): 23 return self.communicate 24 25 22 26 class InversionTest(unittest.TestCase): 23 '''Test the Inversion Interface''' 27 """ Test the Inversion Perspective GUI """ 28 24 29 def setUp(self): 25 '''Create the InversionWindow''' 26 self.widget = InversionWindow(None) 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) 27 41 28 42 def tearDown(self): 29 '''Destroy the InversionWindow''' 43 """ Destroy the InversionWindow """ 44 self.widget.setClosable(False) 30 45 self.widget.close() 31 46 self.widget = None 32 47 33 def testDefaults(self): 34 '''Test the GUI in its default state''' 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 35 57 self.assertIsInstance(self.widget, QtWidgets.QWidget) 36 58 self.assertEqual(self.widget.windowTitle(), "P(r) Inversion Perspective") 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()) 59 self.assertFalse(self.widget.isClosable()) 60 self.assertFalse(self.widget.isCalculating) 61 # mapper 41 62 self.assertIsInstance(self.widget.mapper, QtWidgets.QDataWidgetMapper) 42 # make sure the model is assigned and at least the data is mapped43 self.assertEqual(self.widget.mapper.model(), self.widget.model)44 63 self.assertNotEqual(self.widget.mapper.mappedSection(self.widget.dataList), -1) 45 46 64 # validators 47 65 self.assertIsInstance(self.widget.noOfTermsInput.validator(), QtGui.QIntValidator) … … 52 70 self.assertIsInstance(self.widget.slitHeightInput.validator(), QtGui.QDoubleValidator) 53 71 self.assertIsInstance(self.widget.slitWidthInput.validator(), QtGui.QDoubleValidator) 54 55 72 # model 56 73 self.assertEqual(self.widget.model.rowCount(), 22) 57 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]) 68 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) 74 75 # See that the buttons are now enabled 76 self.assertTrue(self.widget.calculateAllButton.isEnabled()) 74 self.assertEqual(self.widget.model.columnCount(), 1) 75 self.assertEqual(self.widget.mapper.model(), self.widget.model) 76 # buttons 77 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 charts 86 self.assertIsNone(self.widget.dmaxWindow) 87 self.assertIsNone(self.widget.prPlot) 88 self.assertIsNone(self.widget.dataPlot) 89 # threads 90 self.assertIsNone(self.widget.calcThread) 91 self.assertIsNone(self.widget.estimationThread) 92 self.assertIsNone(self.widget.estimationThreadNT) 93 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) 104 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") 119 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()) 77 128 self.assertTrue(self.widget.calculateThisButton.isEnabled()) 78 129 self.assertTrue(self.widget.removeButton.isEnabled()) 79 130 self.assertTrue(self.widget.explorerButton.isEnabled()) 80 131 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]) 92 132 def twoDataSetState(self): 133 """ Testing the base data state of the GUI """ 134 # Test the globals after first sent 135 self.assertEqual(len(self.widget._dataList), 2) 136 self.assertEqual(self.widget.dataList.count(), 2) 137 # See that the buttons are now enabled properly 138 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()) 143 144 def testDefaults(self): 145 """ Test the GUI in its default state """ 146 self.baseGUIState() 147 self.zeroDataSetState() 148 self.baseBatchState() 149 self.removeAllData() 150 151 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() 179 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() 192 193 def testRemoveData(self): 194 """ Test data removal from widget """ 195 self.widget.setData([self.fakeData1, self.fakeData2]) 196 self.twoDataSetState() 93 197 # Remove data 0 94 198 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 102 103 def testAllowBatch(self): 104 ''' Batch is allowed for this perspective''' 105 self.assertTrue(self.widget.allowBatch()) 106 107 def notestModelChanged(self): 108 ''' test the model update ''' 109 # unfinished functionality 110 pass 111 112 def notestHelp(self): 113 ''' test help widget show ''' 114 # unfinished functionality 115 pass 116 117 def testOpenExplorerWindow(self): 118 ''' open Dx window ''' 119 self.widget.openExplorerWindow() 120 self.assertTrue(self.widget.dmaxWindow.isVisible()) 199 self.oneDataSetState() 200 self.removeAllData() 201 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() 121 218 122 219 def testGetNFunc(self): 123 ''' test nfunc getter '''220 """ test nfunc getter """ 124 221 # Float 125 222 self.widget.noOfTermsInput.setText("10.0") … … 132 229 self.widget.noOfTermsInput.setText("") 133 230 n = self.widget.getNFunc() 134 self.assertEqual(cm.output, ['ERROR: root:Incorrect number of terms specified: '])231 self.assertEqual(cm.output, ['ERROR:sas.qtgui.Perspectives.Inversion.InversionPerspective:Incorrect number of terms specified: ']) 135 232 self.assertEqual(self.widget.getNFunc(), 10) 136 233 # string … … 138 235 self.widget.noOfTermsInput.setText("Nordvest Pizza") 139 236 n = self.widget.getNFunc() 140 self.assertEqual(cm.output, ['ERROR: root:Incorrect number of terms specified: Nordvest Pizza'])237 self.assertEqual(cm.output, ['ERROR:sas.qtgui.Perspectives.Inversion.InversionPerspective:Incorrect number of terms specified: Nordvest Pizza']) 141 238 self.assertEqual(self.widget.getNFunc(), 10) 239 self.removeAllData() 142 240 143 241 def testSetCurrentData(self): 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]) 242 """ test current data setter """ 243 self.widget.setData([self.fakeData1, self.fakeData2]) 154 244 155 245 # Check that the current data is reference2 156 self.assertEqual(self.widget._data, item2) 157 246 self.assertEqual(self.widget._data, self.fakeData2) 158 247 # Set the ref to none 159 248 self.widget.setCurrentData(None) 160 self.assertEqual(self.widget._data, item2) 161 249 self.assertEqual(self.widget._data, self.fakeData2) 162 250 # Set the ref to wrong type 163 251 with self.assertRaises(AttributeError): 164 252 self.widget.setCurrentData("Afandi Kebab") 165 166 # Set the reference to ref2 167 self.widget.setCurrentData(item1) 168 self.assertEqual(self.widget._data, item1) 253 # Set the reference to ref1 254 self.widget.setCurrentData(self.fakeData1) 255 self.assertEqual(self.widget._data, self.fakeData1) 256 self.removeAllData() 257 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 169 306 170 307 if __name__ == "__main__": -
src/sas/qtgui/Perspectives/Inversion/__init__.py
- Property mode changed from 100755 to 100644
-
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) inversion 73 -------------------- 74 75 The p(r) calculator accepts any number of data sets, and supports batch fitting 76 for all data sets in the p(r) data set combo box. Switching between data sets in 77 the combo box allows for individual fits and review of fit parameters. The 78 displayed plots will also update to include the latest fit results for the 79 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, and 83 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 .. ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ 87 70 88 Reference 71 89 --------- … … 76 94 .. ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ 77 95 78 .. note:: This help document was last modified by Paul Butler, 05 September, 201696 .. note:: This help document was last modified by Jeff Krzywon, 18 April 2018 -
src/sas/qtgui/Perspectives/__init__.py
- Property mode changed from 100755 to 100644
-
src/sas/qtgui/Plotting/ConvertUnits.py
- Property mode changed from 100644 to 100755
-
src/sas/qtgui/Plotting/DataTransform.py
- Property mode changed from 100644 to 100755
-
src/sas/qtgui/Plotting/Slicers/Arc.py
- Property mode changed from 100644 to 100755
-
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 number 416 of data sets 417 """ 418 def __init__(self, parent = None, output_data=None): 419 420 super(BatchInversionOutputPanel, self).__init__(parent, output_data) 421 _translate = QtCore.QCoreApplication.translate 422 self.setWindowTitle(_translate("GridPanelUI", "Batch P(r) Results")) 423 424 def setupTable(self, data): 425 """ 426 Create tablewidget items and show them, based on params 427 """ 428 # headers 429 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 filename 445 for i_row, (filename, pr) in enumerate(data.items()): 446 out = pr.out 447 cov = pr.cov 448 if out is None: 449 logging.warning("P(r) for {} did not converge.".format(filename)) 450 continue 451 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 @classmethod 473 def onHelp(cls): 474 """ 475 Open a local url in the default browser 476 """ 477 location = GuiUtils.HELP_DIRECTORY_LOCATION 478 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 = None 487 event.accept() -
src/sas/sascalc/pr/invertor.py
r8f83719f r6da860a 481 481 float(chi2) 482 482 except: 483 chi2 = [-1.0]483 chi2 = -1.0 484 484 self.chi2 = chi2 485 485
Note: See TracChangeset
for help on using the changeset viewer.