- Timestamp:
- Mar 8, 2017 9:35:49 AM (8 years ago)
- Branches:
- ESS_GUI, ESS_GUI_Docs, ESS_GUI_batch_fitting, ESS_GUI_bumps_abstraction, ESS_GUI_iss1116, ESS_GUI_iss879, ESS_GUI_iss959, ESS_GUI_opencl, ESS_GUI_ordering, ESS_GUI_sync_sascalc
- Children:
- cbcdd2c
- Parents:
- 86f88d1
- Location:
- src/sas/qtgui
- Files:
-
- 7 edited
Legend:
- Unmodified
- Added
- Removed
-
src/sas/qtgui/DataExplorer.py
r83d6249 r5236449 82 82 self.communicator.activeGraphsSignal.connect(self.updateGraphCombo) 83 83 self.communicator.activeGraphName.connect(self.updatePlotName) 84 #self.communicator.updateTheoryFromPerspectiveSignal.connect(self.updateTheoryFromPerspective) 84 85 self.cbgraph.editTextChanged.connect(self.enableGraphCombo) 85 86 self.cbgraph.currentIndexChanged.connect(self.enableGraphCombo) 87 88 self._perspective = self.parent.perspective() 89 self._perspective.updateTheoryFromPerspectiveSignal.connect(self.updateTheoryFromPerspective) 86 90 87 91 # Proxy model for showing a subset of Data1D/Data2D content … … 787 791 new_plot = globals()[method_name](self, quickplot=True) 788 792 new_plot.data = data 789 new_plot.plot(marker='o') 793 #new_plot.plot(marker='o') 794 new_plot.plot() 790 795 791 796 # Update the global plot counter … … 903 908 pass 904 909 910 def updateTheoryFromPerspective(self, model_item): 911 """ 912 Receive an update theory item from a perspective 913 Make sure it is valid and if so, replace/add in the model 914 """ 915 # Assert the correct type 916 if not isinstance(model_item, QtGui.QStandardItem): 917 msg = "Wrong data type returned from calculations." 918 raise AttributeError, msg 919 920 # Check if there are any other items for this tab 921 # If so, delete them 922 current_tab_name = model_item.text()[:2] 923 for current_index in xrange(self.theory_model.rowCount()): 924 if current_tab_name in self.theory_model.item(current_index).text(): 925 self.theory_model.removeRow(current_index) 926 break 927 928 # Reset the view 929 self.model.reset() 930 931 # Reset the view 932 self.theory_model.appendRow(model_item) 933 934 # Pass acting as a debugger anchor 935 pass 936 905 937 906 938 if __name__ == "__main__": -
src/sas/qtgui/GuiManager.py
r60af928 r5236449 73 73 # Widgets 74 74 # 75 # Current displayed perspective 76 self._current_perspective = None 77 78 # Invoke the initial perspective 79 self.perspectiveChanged("Fitting") 80 75 81 self.addWidgets() 76 82 … … 100 106 "_downloads", 101 107 "Tutorial.pdf")) 102 # Current displayed perspective103 self._current_perspective = None104 105 # Invoke the initial perspective106 self.perspectiveChanged("Fitting")107 108 108 def addWidgets(self): 109 109 """ -
src/sas/qtgui/GuiUtils.py
r83d6249 r5236449 221 221 updateModelFromPerspectiveSignal = QtCore.pyqtSignal(QtGui.QStandardItem) 222 222 223 # New theory data in current perspective 224 updateTheoryFromPerspectiveSignal = QtCore.pyqtSignal(QtGui.QStandardItem) 225 223 226 # New plot requested from the GUI manager 224 227 # Old "NewPlotEvent" … … 256 259 break 257 260 258 checkbox_item = QtGui.QStandardItem(True) 261 # Create the new item 262 checkbox_item = createModelItemWithPlot(update_data, name) 263 264 # Append the new row to the main item 265 item.appendRow(checkbox_item) 266 267 def createModelItemWithPlot(update_data, name=""): 268 """ 269 Creates a checkboxed QStandardItem named "name" 270 Adds QVariant 'update_data' to that row. 271 """ 272 assert isinstance(update_data, QtCore.QVariant) 273 py_update_data = update_data.toPyObject() 274 275 checkbox_item = QtGui.QStandardItem() 259 276 checkbox_item.setCheckable(True) 260 277 checkbox_item.setCheckState(QtCore.Qt.Checked) … … 262 279 263 280 # Add "Info" item 264 if isinstance(py_update_data, (Data1D orData2D)):281 if isinstance(py_update_data, (Data1D, Data2D)): 265 282 # If Data1/2D added - extract Info from it 266 283 info_item = infoFromData(py_update_data) … … 279 296 checkbox_item.setChild(1, info_item) 280 297 281 # A ppend the new row to the mainitem282 item.appendRow(checkbox_item)298 # And return the newly created item 299 return checkbox_item 283 300 284 301 def updateModelItem(item, update_data, name=""): -
src/sas/qtgui/Perspectives/Fitting/FittingPerspective.py
r6f7f652 r5236449 5 5 from PyQt4 import QtGui 6 6 7 import sas.qtgui.GuiUtils as GuiUtils 8 7 9 from FittingWidget import FittingWidget 8 10 … … 10 12 """ 11 13 """ 14 updateTheoryFromPerspectiveSignal = QtCore.pyqtSignal(QtGui.QStandardItem) 12 15 name = "Fitting" # For displaying in the combo box in DataExplorer 13 16 def __init__(self, manager=None, parent=None, data=None): … … 41 44 self.setWindowTitle('Fit panel - Active Fitting Optimizer: %s' % self.optimizer) 42 45 46 self.communicate = GuiUtils.Communicate() 47 43 48 def addFit(self, data): 44 49 """ 45 50 Add a new tab for passed data 46 51 """ 47 tab = FittingWidget(manager=self.manager, parent=self.parent, data=data )52 tab = FittingWidget(manager=self.manager, parent=self.parent, data=data, id=self.maxIndex+1) 48 53 self.tabs.append(tab) 49 54 self.maxIndex += 1 50 55 self.addTab(tab, self.tabName()) 56 tab.signalTheory.connect(self.passSignal) 51 57 52 58 def tabName(self): … … 77 83 """ 78 84 Assign new dataset to the fitting instance 85 Obtain a QStandardItem object and dissect it to get Data1D/2D 86 Pass it over to the calculator 79 87 """ 80 88 assert(data_item is not None) 81 89 82 # Find an unassigned tab. 90 if not isinstance(data_item, list): 91 msg = "Incorrect type passed to the Fitting Perspective" 92 raise AttributeError, msg 93 94 if not isinstance(data_item[0], QtGui.QStandardItem): 95 msg = "Incorrect type passed to the Fitting Perspective" 96 raise AttributeError, msg 97 98 self._model_item = data_item[0] 99 100 # Extract data on 1st child - this is the Data1D/2D component 101 data = GuiUtils.dataFromItem(self._model_item) 102 103 # self.model.item(WIDGETS.W_FILENAME).setData(QtCore.QVariant(self._model_item.text())) 104 105 # Find the first unassigned tab. 83 106 # If none, open a new tab. 84 107 available_tabs = list(map(lambda tab:tab.acceptsData(), self.tabs)) … … 90 113 91 114 115 def passSignal(self, theory_item): 116 """ 117 """ 118 self.updateTheoryFromPerspectiveSignal.emit(theory_item) 119 92 120 if __name__ == "__main__": 93 121 app = QtGui.QApplication([]) -
src/sas/qtgui/Perspectives/Fitting/FittingWidget.py
r86f88d1 r5236449 2 2 import json 3 3 import os 4 import numpy 4 5 from collections import defaultdict 6 7 import logging 8 import traceback 9 5 10 6 11 from PyQt4 import QtGui 7 12 from PyQt4 import QtCore 8 13 9 from UI.FittingWidgetUI import Ui_FittingWidgetUI10 11 14 from sasmodels import generate 12 15 from sasmodels import modelinfo 16 from sasmodels.sasview_model import load_standard_models 17 13 18 from sas.sasgui.guiframe.CategoryInstaller import CategoryInstaller 19 from sas.sasgui.guiframe.dataFitting import Data1D 20 from sas.sasgui.guiframe.dataFitting import Data2D 21 import sas.qtgui.GuiUtils as GuiUtils 22 from sas.sascalc.dataloader.data_info import Detector 23 from sas.sascalc.dataloader.data_info import Source 24 from sas.sasgui.perspectives.fitting.model_thread import Calc1D 25 26 from UI.FittingWidgetUI import Ui_FittingWidgetUI 14 27 15 28 TAB_MAGNETISM = 4 … … 21 34 Main widget for selecting form and structure factor models 22 35 """ 23 def __init__(self, manager=None, parent=None, data=None): 36 signalTheory = QtCore.pyqtSignal(QtGui.QStandardItem) 37 38 def __init__(self, manager=None, parent=None, data=None, id=1): 24 39 """ 25 40 … … 32 47 # Necessary globals 33 48 self.model_is_loaded = False 34 self._data = data 49 self.data_is_loaded = False 50 self.kernel_module = None 35 51 self.is2D = False 36 52 self.model_has_shells = False 37 self.data_assigned = False38 53 self._previous_category_index = 0 39 54 self._last_model_row = 0 55 self._current_parameter_name = None 56 self.models = {} 57 58 # Which tab is this widget displayed in? 59 self.tab_id = id 60 61 # Parameters 62 self.q_range_min = 0.0005 63 self.q_range_max = 0.5 64 self.npts = 20 65 self._data = None 40 66 41 67 # Main GUI setup up 42 68 self.setupUi(self) 43 69 self.setWindowTitle("Fitting") 70 self.communicate = GuiUtils.Communicate() 44 71 45 72 # Set the main models … … 52 79 # Param model displayed in param list 53 80 self.lstParams.setModel(self._model_model) 54 self. _readCategoryInfo()81 self.readCategoryInfo() 55 82 self.model_parameters = None 56 83 self.lstParams.setAlternatingRowColors(True) … … 66 93 self.setTableProperties(self.lstMagnetic) 67 94 68 # Defaults for the str cutre factors95 # Defaults for the structure factors 69 96 self.setDefaultStructureCombo() 70 97 71 # make structure factor and model CBs disabled98 # Make structure factor and model CBs disabled 72 99 self.disableModelCombo() 73 100 self.disableStructureCombo() … … 80 107 self.cbCategory.setCurrentIndex(0) 81 108 109 self._index = data 110 if data is not None: 111 self.data = data 112 82 113 # Connect signals to controls 83 114 self.initializeSignals() … … 93 124 def data(self, value): 94 125 """ data setter """ 95 self._data = value 96 self.data_assigned = True 97 # TODO: update ranges, chi2 etc 126 self._index = value 127 self._data = GuiUtils.dataFromItem(value[0]) 128 self.data_is_loaded = True 129 self.updateQRange() 130 self.cmdFit.setEnabled(True) 98 131 99 132 def acceptsData(self): 100 133 """ Tells the caller this widget can accept new dataset """ 101 return not self.data_ assigned134 return not self.data_is_loaded 102 135 103 136 def disableModelCombo(self): … … 116 149 self.cbStructureFactor.setEnabled(True) 117 150 self.label_4.setEnabled(True) 151 152 def updateQRange(self): 153 """ 154 Updates Q Range display 155 """ 156 if self.data_is_loaded: 157 self.q_range_min, self.q_range_max, self.npts = self.computeDataRange(self.data) 158 # set Q range labels 159 self.lblMinRangeDef.setText(str(self.q_range_min)) 160 self.lblMaxRangeDef.setText(str(self.q_range_max)) 161 162 self.txtMaxRange.setText(str(self.q_range_max)) 163 self.txtMinRange.setText(str(self.q_range_min)) 164 self.txtNpts.setText(str(self.npts)) 118 165 119 166 def initializeControls(self): … … 132 179 self.tabFitting.setTabEnabled(TAB_POLY, False) 133 180 self.tabFitting.setTabEnabled(TAB_MAGNETISM, False) 134 # set initial labels135 self.lblMinRangeDef.setText("---")136 self.lblMaxRangeDef.setText("---")137 181 self.lblChi2Value.setText("---") 182 183 # Update Q Ranges 184 self.updateQRange() 138 185 139 186 def initializeSignals(self): … … 147 194 self.chkPolydispersity.toggled.connect(self.togglePoly) 148 195 self.chkMagnetism.toggled.connect(self.toggleMagnetism) 196 self.cmdFit.clicked.connect(self.onFit) 149 197 150 198 def setDefaultStructureCombo(self): … … 191 239 def selectModel(self): 192 240 """ 193 Select Model from list 194 :return: 241 Respond to select Model from list event 195 242 """ 196 243 model = self.cbModel.currentText() 244 self._current_parameter_name = model 245 246 # SasModel -> QModel 197 247 self.setModelModel(model) 248 249 if self._index is None: 250 if self.is2D: 251 self.createDefault2dData() 252 else: 253 self.createDefault1dData() 254 self.createTheoryIndex() 255 else: 256 # TODO: 2D case 257 # TODO: attach the chart to index 258 self.calculate1DForModel() 198 259 199 260 def selectStructureFactor(self): … … 206 267 self.setModelModel(model) 207 268 208 def _readCategoryInfo(self):269 def readCategoryInfo(self): 209 270 """ 210 271 Reads the categories in from file … … 220 281 cat_file = open(categorization_file, 'rb') 221 282 self.master_category_dict = json.load(cat_file) 222 self. _regenerate_model_dict()283 self.regenerateModelDict() 223 284 cat_file.close() 224 285 except IOError: … … 228 289 print 'An existential crisis if there ever was one.' 229 290 230 def _regenerate_model_dict(self): 291 # Load the model dict 292 models = load_standard_models() 293 for model in models: 294 self.models[model.name] = model 295 296 def regenerateModelDict(self): 231 297 """ 232 298 regenerates self.by_model_dict which has each model name as the … … 313 379 Setting model parameters into table based on selected 314 380 :param model_name: 315 :return:316 381 """ 317 382 # Crete/overwrite model items 318 383 self._model_model.clear() 319 384 model_name = str(model_name) 385 320 386 kernel_module = generate.load_kernel_module(model_name) 387 #model_info = modelinfo.make_model_info(kernel_module) 388 #self.kernel_module = _make_model_from_info(model_info) 321 389 self.model_parameters = modelinfo.make_parameter_table(getattr(kernel_module, 'parameters', [])) 322 390 323 #TODO: scale and background are implicit in sasmodels and needs to be added 391 # Instantiate the current model 392 self.kernel_module = self.models[model_name]() 393 394 # Explicitly add scale and background with default values 324 395 self.addScaleToModel(self._model_model) 325 396 self.addBackgroundToModel(self._model_model) 326 397 398 # Update the QModel 327 399 self.addParametersToModel(self.model_parameters, self._model_model) 328 329 400 self.addHeadersToModel(self._model_model) 330 401 # Multishell models need additional treatment 331 402 self.addExtraShells() 332 403 404 # Add polydispersity to the model 333 405 self.setPolyModel() 406 # Add magnetic parameters to the model 334 407 self.setMagneticModel() 408 409 # Now we claim the model has been loaded 335 410 self.model_is_loaded = True 411 412 # Update Q Ranges 413 self.updateQRange() 414 415 def computeDataRange(self, data): 416 """ 417 compute the minimum and the maximum range of the data 418 return the npts contains in data 419 """ 420 assert(data is not None) 421 assert((isinstance(data, Data1D) or isinstance(data, Data2D))) 422 qmin, qmax, npts = None, None, None 423 if isinstance(data, Data1D): 424 try: 425 qmin = min(data.x) 426 qmax = max(data.x) 427 npts = len(data.x) 428 except: 429 msg = "Unable to find min/max/length of \n data named %s" % \ 430 data.filename 431 raise ValueError, msg 432 433 else: 434 qmin = 0 435 try: 436 x = max(numpy.fabs(data.xmin), numpy.fabs(data.xmax)) 437 y = max(numpy.fabs(data.ymin), numpy.fabs(data.ymax)) 438 except: 439 msg = "Unable to find min/max of \n data named %s" % \ 440 data.filename 441 raise ValueError, msg 442 qmax = math.sqrt(x * x + y * y) 443 npts = len(data.data) 444 return qmin, qmax, npts 336 445 337 446 def addParametersToModel(self, parameters, model): … … 379 488 self._last_model_row = self._model_model.rowCount() 380 489 381 def modelToFittingParameters(self): 490 def createDefault1dData(self): 491 """ 492 Create default data for fitting perspective 493 Only when the page is on theory mode. 494 """ 495 x = numpy.linspace(start=self.q_range_min, stop=self.q_range_max, 496 num=self.npts, endpoint=True) 497 self._data = Data1D(x=x) 498 self._data.xaxis('\\rm{Q}', "A^{-1}") 499 self._data.yaxis('\\rm{Intensity}', "cm^{-1}") 500 self._data.is_data = False 501 self._data.id = str(self.tab_id) + " data" 502 self._data.group_id = str(self.tab_id) + " Model1D" 503 504 def createDefault2dData(self): 505 """ 506 Create 2D data by default 507 Only when the page is on theory mode. 508 """ 509 self._data = Data2D() 510 qmax = self.q_range_max / numpy.sqrt(2) 511 self._data.xaxis('\\rm{Q_{x}}', 'A^{-1}') 512 self._data.yaxis('\\rm{Q_{y}}', 'A^{-1}') 513 self._data.is_data = False 514 self._data.id = str(self.tab_id) + " data" 515 self._data.group_id = str(self.tab_id) + " Model2D" 516 517 # Default detector 518 self._data.detector.append(Detector()) 519 index = len(self._data.detector) - 1 520 self._data.detector[index].distance = 8000 # mm 521 self._data.source.wavelength = 6 # A 522 self._data.detector[index].pixel_size.x = 5 # mm 523 self._data.detector[index].pixel_size.y = 5 # mm 524 self._data.detector[index].beam_center.x = qmax 525 self._data.detector[index].beam_center.y = qmax 526 # theory default: assume the beam 527 #center is located at the center of sqr detector 528 xmax = qmax 529 xmin = -qmax 530 ymax = qmax 531 ymin = -qmax 532 qstep = self.npts 533 534 x = numpy.linspace(start=xmin, stop=xmax, num=qstep, endpoint=True) 535 y = numpy.linspace(start=ymin, stop=ymax, num=qstep, endpoint=True) 536 # Use data info instead 537 new_x = numpy.tile(x, (len(y), 1)) 538 new_y = numpy.tile(y, (len(x), 1)) 539 new_y = new_y.swapaxes(0, 1) 540 541 # all data required in 1d array 542 qx_data = new_x.flatten() 543 qy_data = new_y.flatten() 544 q_data = numpy.sqrt(qx_data * qx_data + qy_data * qy_data) 545 546 # set all True (standing for unmasked) as default 547 mask = numpy.ones(len(qx_data), dtype=bool) 548 # calculate the range of qx and qy: this way, 549 # it is a little more independent 550 # store x and y bin centers in q space 551 x_bins = x 552 y_bins = y 553 554 self._data.source = Source() 555 self._data.data = numpy.ones(len(mask)) 556 self._data.err_data = numpy.ones(len(mask)) 557 self._data.qx_data = qx_data 558 self._data.qy_data = qy_data 559 self._data.q_data = q_data 560 self._data.mask = mask 561 self._data.x_bins = x_bins 562 self._data.y_bins = y_bins 563 # max and min taking account of the bin sizes 564 self._data.xmin = xmin 565 self._data.xmax = xmax 566 self._data.ymin = ymin 567 self._data.ymax = ymax 568 569 def createTheoryIndex(self): 570 """ 571 Create a QStandardModelIndex containing default model data 572 """ 573 name = self._current_parameter_name 574 if self.is2D: 575 name += "2d" 576 name = "M%i [%s]" % (self.tab_id, name) 577 new_item = GuiUtils.createModelItemWithPlot(QtCore.QVariant(self.data), name=name) 578 self.signalTheory.emit(new_item) 579 580 def onFit(self): 581 """ 582 Perform fitting on the current data 583 """ 584 # TEST FOR DISPLAY. 585 self.calculate1DForModel() 586 587 def calculate1DForModel(self): 382 588 """ 383 589 Prepare the fitting data object, based on current ModelModel 384 590 """ 385 pass 591 data = self.data 592 model = self.kernel_module 593 page_id = 0 594 qmin = self.q_range_min 595 qmax = self.q_range_max 596 smearer = None 597 state = None 598 weight = None 599 fid = None 600 toggle_mode_on = False 601 update_chisqr = False 602 source = None 603 604 self.calc_1D = Calc1D(data=data, 605 model=model, 606 page_id=page_id, 607 qmin=qmin, 608 qmax=qmax, 609 smearer=smearer, 610 state=state, 611 weight=weight, 612 fid=fid, 613 toggle_mode_on=toggle_mode_on, 614 completefn=self.complete1D, 615 update_chisqr=update_chisqr, 616 exception_handler=self.calcException, 617 source=source) 618 self.calc_1D.queue() 619 620 def complete1D(self, x, y, page_id, elapsed, index, model, 621 weight=None, fid=None, 622 toggle_mode_on=False, state=None, 623 data=None, update_chisqr=True, 624 source='model', plot_result=True): 625 """ 626 Plot the current data 627 Should be a rewrite of fitting.py/_complete1D 628 """ 629 print "THREAD FINISHED" 630 631 def calcException(self, etype, value, tb): 632 """ 633 """ 634 print "THREAD EXCEPTION" 635 logging.error("".join(traceback.format_exception(etype, value, tb))) 636 msg = traceback.format_exception(etype, value, tb, limit=1) 386 637 387 638 def replaceShellName(self, param_name, value): -
src/sas/qtgui/Perspectives/Fitting/UI/FittingWidgetUI.ui
r86f88d1 r5236449 302 302 </item> 303 303 <item row="0" column="1"> 304 <widget class="QLineEdit" name=" lineEdit"/>304 <widget class="QLineEdit" name="txtMinRange"/> 305 305 </item> 306 306 <item row="0" column="2"> … … 320 320 </item> 321 321 <item row="1" column="1"> 322 <widget class="QLineEdit" name=" lineEdit_2"/>322 <widget class="QLineEdit" name="txtMaxRange"/> 323 323 </item> 324 324 <item row="1" column="2"> … … 378 378 </item> 379 379 <item row="0" column="1"> 380 <widget class="QLineEdit" name=" lineEdit_3"/>380 <widget class="QLineEdit" name="txtNpts"/> 381 381 </item> 382 382 <item row="0" column="2"> … … 395 395 </item> 396 396 <item row="1" column="1"> 397 <widget class="QLineEdit" name=" lineEdit_4"/>397 <widget class="QLineEdit" name="txtNptsFit"/> 398 398 </item> 399 399 <item row="2" column="0"> … … 405 405 </item> 406 406 <item row="2" column="1"> 407 <widget class="QLineEdit" name=" lineEdit_5"/>407 <widget class="QLineEdit" name="txtChi2"/> 408 408 </item> 409 409 <item row="2" column="3"> -
src/sas/qtgui/Plotter2D.py
r161713c r5236449 81 81 self._item = item 82 82 83 def plot(self, data=None):83 def plot(self, marker=None, data=None): 84 84 """ 85 85 Plot 2D self._data 86 marker - unused 86 87 """ 87 88 # Assing data
Note: See TracChangeset
for help on using the changeset viewer.