Changeset 8289ae3 in sasview
- Timestamp:
- Mar 23, 2018 8:24:10 AM (7 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:
- ba4e3ba
- Parents:
- 304e42f (diff), 8ac3551 (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the (diff) links above to see all the changes relative to each parent. - Files:
-
- 22 added
- 28 edited
Legend:
- Unmodified
- Added
- Removed
-
TabularUnified setup.py ¶
- Property mode changed from 100644 to 100755
r14ec91c5 r3b3b40b 351 351 "src", "sas", "qtgui", "Utilities") 352 352 packages.append("sas.qtgui.Utilities") 353 package_dir["sas.qtgui.UtilitiesUI"] = os.path.join( 354 "src", "sas", "qtgui", "Utilities","UI") 355 packages.append("sas.qtgui.Utilities.UI") 353 356 354 357 package_dir["sas.qtgui.Calculators"] = os.path.join( -
TabularUnified src/sas/qtgui/GUITests.py ¶
rda9a0722 r3b3b40b 44 44 from Utilities.UnitTesting import GuiUtilsTest 45 45 from Utilities.UnitTesting import SasviewLoggerTest 46 from Utilities.UnitTesting import GridPanelTest 47 from Utilities.UnitTesting import ModelEditorTest 48 from Utilities.UnitTesting import PluginDefinitionTest 49 from Utilities.UnitTesting import TabbedModelEditorTest 46 50 47 51 # Unit Testing … … 99 103 unittest.makeSuite(GuiUtilsTest.DoubleValidatorTest, 'test'), 100 104 unittest.makeSuite(GuiUtilsTest.HashableStandardItemTest, 'test'), 105 unittest.makeSuite(GridPanelTest.BatchOutputPanelTest, 'test'), 106 unittest.makeSuite(ModelEditorTest.ModelEditorTest, 'test'), 107 unittest.makeSuite(PluginDefinitionTest.PluginDefinitionTest, 'test'), 108 unittest.makeSuite(TabbedModelEditorTest.TabbedModelEditorTest,'test'), 101 109 102 110 # Calculators -
TabularUnified src/sas/qtgui/MainWindow/DataExplorer.py ¶
re90988c r8ac3551 473 473 Notify the gui manager about the new perspective chosen. 474 474 """ 475 self.communicator.perspectiveChangedSignal.emit(self.cbFitting. currentText())475 self.communicator.perspectiveChangedSignal.emit(self.cbFitting.itemText(index)) 476 476 self.chkBatch.setEnabled(self.parent.perspective().allowBatch()) 477 477 478 def display Data(self, data_list):478 def displayFile(self, filename=None, is_data=True): 479 479 """ 480 480 Forces display of charts for the given filename 481 481 """ 482 plot_to_show = data_list[0] 483 484 # passed plot is used ONLY to figure out its title, 485 # so all the charts related by it can be pulled from 486 # the data explorer indices. 487 filename = plot_to_show.filename 488 model = self.model if plot_to_show.is_data else self.theory_model 489 482 model = self.model if is_data else self.theory_model 490 483 # Now query the model item for available plots 491 484 plots = GuiUtils.plotsFromFilename(filename, model) … … 506 499 if new_plots: 507 500 self.plotData(new_plots) 501 502 def displayData(self, data_list): 503 """ 504 Forces display of charts for the given data set 505 """ 506 plot_to_show = data_list[0] 507 # passed plot is used ONLY to figure out its title, 508 # so all the charts related by it can be pulled from 509 # the data explorer indices. 510 filename = plot_to_show.filename 511 self.displayFile(filename=filename, is_data=plot_to_show.is_data) 508 512 509 513 def addDataPlot2D(self, plot_set, item): … … 1027 1031 pass 1028 1032 1033 def onAnalysisUpdate(self, new_perspective=""): 1034 """ 1035 Update the perspective combo index based on passed string 1036 """ 1037 assert new_perspective in Perspectives.PERSPECTIVES.keys() 1038 self.cbFitting.blockSignals(True) 1039 self.cbFitting.setCurrentIndex(self.cbFitting.findText(new_perspective)) 1040 self.cbFitting.blockSignals(False) 1041 pass 1042 1029 1043 def loadComplete(self, output): 1030 1044 """ -
TabularUnified src/sas/qtgui/MainWindow/GuiManager.py ¶
r14ec91c5 r8ac3551 19 19 20 20 import sas.qtgui.Utilities.ObjectLibrary as ObjectLibrary 21 from sas.qtgui.Utilities.TabbedModelEditor import TabbedModelEditor 22 from sas.qtgui.Utilities.PluginManager import PluginManager 21 23 from sas.qtgui.MainWindow.UI.AcknowledgementsUI import Ui_Acknowledgements 22 24 from sas.qtgui.MainWindow.AboutBox import AboutBox … … 47 49 Main SasView window functionality 48 50 """ 49 50 51 def __init__(self, parent=None): 51 52 """ … … 182 183 Respond to change of the perspective signal 183 184 """ 184 185 # Save users from themselves...186 #if isinstance(self._current_perspective, Perspectives.PERSPECTIVES[str(perspective_name)]):187 self.setupPerspectiveMenubarOptions(self._current_perspective)188 # return189 190 185 # Close the previous perspective 191 186 self.clearPerspectiveMenubarOptions(self._current_perspective) … … 197 192 # Default perspective 198 193 self._current_perspective = Perspectives.PERSPECTIVES[str(perspective_name)](parent=self) 194 195 self.setupPerspectiveMenubarOptions(self._current_perspective) 199 196 200 197 subwindow = self._workspace.workspace.addSubWindow(self._current_perspective) … … 358 355 self.communicate.updateTheoryFromPerspectiveSignal.connect(self.updateTheoryFromPerspective) 359 356 self.communicate.plotRequestedSignal.connect(self.showPlot) 357 self.communicate.plotFromFilenameSignal.connect(self.showPlotFromFilename) 360 358 self.communicate.updateModelFromDataOperationPanelSignal.connect(self.updateModelFromDataOperationPanel) 361 359 … … 406 404 self._workspace.actionFit_Results.triggered.connect(self.actionFit_Results) 407 405 self._workspace.actionChain_Fitting.triggered.connect(self.actionChain_Fitting) 406 self._workspace.actionAdd_Custom_Model.triggered.connect(self.actionAdd_Custom_Model) 408 407 self._workspace.actionEdit_Custom_Model.triggered.connect(self.actionEdit_Custom_Model) 408 self._workspace.actionManage_Custom_Models.triggered.connect(self.actionManage_Custom_Models) 409 409 # Window 410 410 self._workspace.actionCascade.triggered.connect(self.actionCascade) … … 417 417 self._workspace.actionInversion.triggered.connect(self.actionInversion) 418 418 self._workspace.actionInvariant.triggered.connect(self.actionInvariant) 419 self._workspace.actionCorfunc.triggered.connect(self.actionCorfunc) 419 420 # Help 420 421 self._workspace.actionDocumentation.triggered.connect(self.actionDocumentation) … … 662 663 pass 663 664 665 def actionAdd_Custom_Model(self): 666 """ 667 """ 668 self.model_editor = TabbedModelEditor(self) 669 self.model_editor.show() 670 664 671 def actionEdit_Custom_Model(self): 665 672 """ 666 673 """ 667 print("actionEdit_Custom_Model TRIGGERED") 668 pass 674 self.model_editor = TabbedModelEditor(self, edit_only=True) 675 self.model_editor.show() 676 677 def actionManage_Custom_Models(self): 678 """ 679 """ 680 self.model_manager = PluginManager(self) 681 self.model_manager.show() 669 682 670 683 #============ ANALYSIS ================= … … 674 687 """ 675 688 self.perspectiveChanged("Fitting") 689 # Notify other widgets 690 self.filesWidget.onAnalysisUpdate("Fitting") 676 691 677 692 def actionInversion(self): … … 679 694 Change to the Inversion perspective 680 695 """ 681 # For now we'll just update the analysis menu status but when the inversion is implemented delete from here682 self.checkAnalysisOption(self._workspace.actionInversion)683 # to here and uncomment the following line684 696 self.perspectiveChanged("Inversion") 697 self.filesWidget.onAnalysisUpdate("Inversion") 685 698 686 699 def actionInvariant(self): … … 689 702 """ 690 703 self.perspectiveChanged("Invariant") 704 self.filesWidget.onAnalysisUpdate("Invariant") 705 706 def actionCorfunc(self): 707 """ 708 Change to the Corfunc perspective 709 """ 710 self.perspectiveChanged("Corfunc") 711 self.filesWidget.onAnalysisUpdate("Corfunc") 691 712 692 713 #============ WINDOW ================= … … 779 800 self.filesWidget.model.appendRow(new_item) 780 801 self._data_manager.add_data(new_datalist_item) 802 803 def showPlotFromFilename(self, filename): 804 """ 805 Pass the show plot request to the data explorer 806 """ 807 if hasattr(self, "filesWidget"): 808 self.filesWidget.displayFile(filename=filename, is_data=True) 781 809 782 810 def showPlot(self, plot): … … 828 856 elif isinstance(perspective, Perspectives.PERSPECTIVES["Invariant"]): 829 857 self.checkAnalysisOption(self._workspace.actionInvariant) 830 # elif isinstance(perspective, Perspectives.PERSPECTIVES["Inversion"]): 831 # self.checkAnalysisOption(self._workspace.actionInversion) 858 elif isinstance(perspective, Perspectives.PERSPECTIVES["Inversion"]): 859 self.checkAnalysisOption(self._workspace.actionInversion) 860 elif isinstance(perspective, Perspectives.PERSPECTIVES["Corfunc"]): 861 self.checkAnalysisOption(self._workspace.actionCorfunc) -
TabularUnified src/sas/qtgui/MainWindow/MainWindow.py ¶
r8353d90 r8ac3551 30 30 except Exception as ex: 31 31 import logging 32 logging.error("Application failed with: " , ex)32 logging.error("Application failed with: " + str(ex)) 33 33 print("Application failed with: ", ex) 34 34 -
TabularUnified src/sas/qtgui/MainWindow/UI/MainWindowUI.ui ¶
r1543f0c r8ac3551 8 8 <y>0</y> 9 9 <width>915</width> 10 <height> 527</height>10 <height>762</height> 11 11 </rect> 12 12 </property> … … 25 25 <y>0</y> 26 26 <width>915</width> 27 <height>2 1</height>27 <height>26</height> 28 28 </rect> 29 29 </property> … … 108 108 <addaction name="actionChain_Fitting"/> 109 109 <addaction name="separator"/> 110 <addaction name="actionAdd_Custom_Model"/> 110 111 <addaction name="actionEdit_Custom_Model"/> 112 <addaction name="actionManage_Custom_Models"/> 111 113 </widget> 112 114 <widget class="QMenu" name="menuWindow"> … … 126 128 <string>Analysis</string> 127 129 </property> 130 <addaction name="actionCorfunc"/> 128 131 <addaction name="actionFitting"/> 132 <addaction name="actionInvariant"/> 129 133 <addaction name="actionInversion"/> 130 <addaction name="actionInvariant"/>131 134 </widget> 132 135 <widget class="QMenu" name="menuHelp"> … … 505 508 </property> 506 509 </action> 510 <action name="actionAdd_Custom_Model"> 511 <property name="text"> 512 <string>Add Custom Model</string> 513 </property> 514 </action> 515 <action name="actionManage_Custom_Models"> 516 <property name="text"> 517 <string>Manage Custom Models</string> 518 </property> 519 </action> 520 <action name="actionCorfunc"> 521 <property name="checkable"> 522 <bool>true</bool> 523 </property> 524 <property name="text"> 525 <string>Correlation Function</string> 526 </property> 527 </action> 507 528 </widget> 508 529 <resources/> -
TabularUnified src/sas/qtgui/Perspectives/Fitting/ConstraintWidget.py ¶
re4c475b7 r8b480d27 111 111 112 112 # Disconnect all local slots 113 tab_object.disconnect()113 #tab_object.disconnect() 114 114 115 115 # Reconnect tab signals to local slots … … 171 171 # No such tab! 172 172 return 173 sim_fitter_list, fitter_id = tab_object.prepareFitters(fitter=sim_fitter_list[0], fit_id=fitter_id) 173 sim_fitter_list, fitter_id = \ 174 tab_object.prepareFitters(fitter=sim_fitter_list[0], fit_id=fitter_id) 174 175 page_ids.append([tab_object.page_id]) 175 176 except ValueError: … … 178 179 "Not all tabs chosen for fitting have parameters selected for fitting." 179 180 QtWidgets.QMessageBox.warning(self, 180 181 182 181 'Warning', 182 no_params_msg, 183 QtWidgets.QMessageBox.Ok) 183 184 184 185 return … … 674 675 675 676 constraint.func = constraint_text 677 # param1 is the parameter we're constraining 676 678 constraint.param = param1 679 677 680 # Find the right tab 678 681 constrained_tab = self.getObjectByName(model1) -
TabularUnified src/sas/qtgui/Perspectives/Fitting/FittingPerspective.py ¶
r14ec91c5 r3b3b40b 8 8 from bumps import fitters 9 9 10 import sas.qtgui.Utilities.LocalConfig as LocalConfig 10 11 import sas.qtgui.Utilities.ObjectLibrary as ObjectLibrary 11 12 … … 116 117 tab = FittingWidget(parent=self.parent, data=data, tab_id=self.maxIndex+1) 117 118 tab.is_batch_fitting = is_batch 119 118 120 # Add this tab to the object library so it can be retrieved by scripting/jupyter 119 121 tab_name = self.getTabName(is_batch=is_batch) … … 123 125 self.updateFitDict(data, tab_name) 124 126 self.maxIndex += 1 125 self.addTab(tab, tab_name) 127 icon = QtGui.QIcon() 128 if is_batch: 129 icon.addPixmap(QtGui.QPixmap("src/sas/qtgui/images/icons/layers.svg")) 130 self.addTab(tab, icon, tab_name) 131 # Show the new tab 132 self.setCurrentIndex(self.maxIndex-1) 133 # Notify listeners 126 134 self.tabsModifiedSignal.emit() 127 135 … … 140 148 ObjectLibrary.addObject(tab_name, tab) 141 149 self.tabs.append(tab) 142 self.addTab(tab, tab_name) 150 icon = QtGui.QIcon() 151 icon.addPixmap(QtGui.QPixmap("src/sas/qtgui/images/icons/link.svg")) 152 self.addTab(tab, icon, tab_name) 153 154 # This will be the last tab, so set the index accordingly 155 self.setCurrentIndex(self.count()-1) 143 156 144 157 def updateFitDict(self, item_key, tab_name): -
TabularUnified src/sas/qtgui/Perspectives/Fitting/FittingWidget.py ¶
re4c475b7 r8b480d27 24 24 import sas.qtgui.Utilities.GuiUtils as GuiUtils 25 25 import sas.qtgui.Utilities.LocalConfig as LocalConfig 26 from sas.qtgui.Utilities.GridPanel import BatchOutputPanel 26 27 from sas.qtgui.Utilities.CategoryInstaller import CategoryInstaller 27 28 from sas.qtgui.Plotting.PlotterData import Data1D … … 36 37 from sas.qtgui.Perspectives.Fitting.FittingLogic import FittingLogic 37 38 from sas.qtgui.Perspectives.Fitting import FittingUtilities 39 from sas.qtgui.Perspectives.Fitting import ModelUtilities 38 40 from sas.qtgui.Perspectives.Fitting.SmearingWidget import SmearingWidget 39 41 from sas.qtgui.Perspectives.Fitting.OptionsWidget import OptionsWidget … … 50 52 CATEGORY_DEFAULT = "Choose category..." 51 53 CATEGORY_STRUCTURE = "Structure Factor" 54 CATEGORY_CUSTOM = "Plugin Models" 52 55 STRUCTURE_DEFAULT = "None" 53 56 … … 83 86 constraintAddedSignal = QtCore.pyqtSignal(list) 84 87 newModelSignal = QtCore.pyqtSignal() 88 fittingFinishedSignal = QtCore.pyqtSignal(tuple) 89 batchFittingFinishedSignal = QtCore.pyqtSignal(tuple) 90 85 91 def __init__(self, parent=None, data=None, tab_id=1): 86 92 … … 211 217 self.page_stack = [] 212 218 self.all_data = [] 219 # custom plugin models 220 # {model.name:model} 221 self.custom_models = self.customModels() 213 222 # Polydisp widget table default index for function combobox 214 223 self.orig_poly_index = 3 … … 415 424 self.onSelectModel() 416 425 426 @classmethod 427 def customModels(cls): 428 """ Reads in file names in the custom plugin directory """ 429 return ModelUtilities._find_models() 430 417 431 def initializeControls(self): 418 432 """ … … 465 479 self._poly_model.itemChanged.connect(self.onPolyModelChange) 466 480 self._magnet_model.itemChanged.connect(self.onMagnetModelChange) 481 self.lstParams.selectionModel().selectionChanged.connect(self.onSelectionChanged) 482 483 # Local signals 484 self.batchFittingFinishedSignal.connect(self.batchFitComplete) 485 self.fittingFinishedSignal.connect(self.fitComplete) 467 486 468 487 # Signals from separate tabs asking for replot 469 488 self.options_widget.plot_signal.connect(self.onOptionsUpdate) 489 490 # Signals from other widgets 491 self.communicate.customModelDirectoryChanged.connect(self.onCustomModelChange) 470 492 471 493 def modelName(self): … … 576 598 # widget.params[0] is the parameter we're constraining 577 599 constraint.param = mc_widget.params[0] 578 # Functionshould have the model name preamble600 # parameter should have the model name preamble 579 601 model_name = self.kernel_module.name 580 constraint.func = model_name + "." + c_text 602 # param_used is the parameter we're using in constraining function 603 param_used = mc_widget.params[1] 604 # Replace param_used with model_name.param_used 605 updated_param_used = model_name + "." + param_used 606 new_func = c_text.replace(param_used, updated_param_used) 607 constraint.func = new_func 581 608 # Which row is the constrained parameter in? 582 609 row = self.getRowFromName(constraint.param) … … 677 704 Delete constraints from selected parameters. 678 705 """ 679 self.deleteConstraintOnParameter(param=None) 706 params = [s.data() for s in self.lstParams.selectionModel().selectedRows() 707 if self.isCheckable(s.row())] 708 for param in params: 709 self.deleteConstraintOnParameter(param=param) 680 710 681 711 def deleteConstraintOnParameter(self, param=None): … … 686 716 max_col = self.lstParams.itemDelegate().param_max 687 717 for row in range(self._model_model.rowCount()): 718 if not self.rowHasConstraint(row): 719 continue 688 720 # Get the Constraint object from of the model item 689 721 item = self._model_model.item(row, 1) 690 if not item.hasChildren(): 691 continue 692 constraint = item.child(0).data() 722 constraint = self.getConstraintForRow(row) 693 723 if constraint is None: 694 724 continue … … 816 846 return constraints 817 847 848 def getConstraintsForFitting(self): 849 """ 850 Return a list of constraints in format ready for use in fiting 851 """ 852 # Get constraints 853 constraints = self.getComplexConstraintsForModel() 854 # See if there are any constraints across models 855 multi_constraints = [cons for cons in constraints if self.isConstraintMultimodel(cons[1])] 856 857 if multi_constraints: 858 # Let users choose what to do 859 msg = "The current fit contains constraints relying on other fit pages.\n" 860 msg += "Parameters with those constraints are:\n" +\ 861 '\n'.join([cons[0] for cons in multi_constraints]) 862 msg += "\n\nWould you like to remove these constraints or cancel fitting?" 863 msgbox = QtWidgets.QMessageBox(self) 864 msgbox.setIcon(QtWidgets.QMessageBox.Warning) 865 msgbox.setText(msg) 866 msgbox.setWindowTitle("Existing Constraints") 867 # custom buttons 868 button_remove = QtWidgets.QPushButton("Remove") 869 msgbox.addButton(button_remove, QtWidgets.QMessageBox.YesRole) 870 button_cancel = QtWidgets.QPushButton("Cancel") 871 msgbox.addButton(button_cancel, QtWidgets.QMessageBox.RejectRole) 872 retval = msgbox.exec_() 873 if retval == QtWidgets.QMessageBox.RejectRole: 874 # cancel fit 875 raise ValueError("Fitting cancelled") 876 else: 877 # remove constraint 878 for cons in multi_constraints: 879 self.deleteConstraintOnParameter(param=cons[0]) 880 # re-read the constraints 881 constraints = self.getComplexConstraintsForModel() 882 883 return constraints 884 818 885 def showModelDescription(self): 819 886 """ … … 874 941 self.respondToModelStructure(model=model, structure_factor=structure) 875 942 943 def onCustomModelChange(self): 944 """ 945 Reload the custom model combobox 946 """ 947 self.custom_models = self.customModels() 948 self.readCustomCategoryInfo() 949 # See if we need to update the combo in-place 950 if self.cbCategory.currentText() != CATEGORY_CUSTOM: return 951 952 current_text = self.cbModel.currentText() 953 self.cbModel.blockSignals(True) 954 self.cbModel.clear() 955 self.cbModel.blockSignals(False) 956 self.enableModelCombo() 957 self.disableStructureCombo() 958 # Retrieve the list of models 959 model_list = self.master_category_dict[CATEGORY_CUSTOM] 960 # Populate the models combobox 961 self.cbModel.addItems(sorted([model for (model, _) in model_list])) 962 new_index = self.cbModel.findText(current_text) 963 if new_index != -1: 964 self.cbModel.setCurrentIndex(self.cbModel.findText(current_text)) 965 966 def onSelectionChanged(self): 967 """ 968 React to parameter selection 969 """ 970 rows = self.lstParams.selectionModel().selectedRows() 971 # Clean previous messages 972 self.communicate.statusBarUpdateSignal.emit("") 973 if len(rows) == 1: 974 # Show constraint, if present 975 row = rows[0].row() 976 if self.rowHasConstraint(row): 977 func = self.getConstraintForRow(row).func 978 if func is not None: 979 self.communicate.statusBarUpdateSignal.emit("Active constrain: "+func) 980 876 981 def replaceConstraintName(self, old_name, new_name=""): 877 982 """ … … 886 991 new_func = func.replace(old_name, new_name) 887 992 self._model_model.item(row, 1).child(0).data().func = new_func 993 994 def isConstraintMultimodel(self, constraint): 995 """ 996 Check if the constraint function text contains current model name 997 """ 998 current_model_name = self.kernel_module.name 999 if current_model_name in constraint: 1000 return False 1001 else: 1002 return True 888 1003 889 1004 def updateData(self): … … 1105 1220 except ValueError as ex: 1106 1221 # This should not happen! GUI explicitly forbids this situation 1107 self.communicate.statusBarUpdateSignal.emit( 'Fitting attempt without parameters.')1222 self.communicate.statusBarUpdateSignal.emit(str(ex)) 1108 1223 return 1109 1224 1110 1225 # Create the fitting thread, based on the fitter 1111 completefn = self.batchFit Complete if self.is_batch_fitting else self.fitComplete1226 completefn = self.batchFittingCompleted if self.is_batch_fitting else self.fittingCompleted 1112 1227 1113 1228 calc_fit = FitThread(handler=handler, … … 1146 1261 pass 1147 1262 1263 def batchFittingCompleted(self, result): 1264 """ 1265 Send the finish message from calculate threads to main thread 1266 """ 1267 self.batchFittingFinishedSignal.emit(result) 1268 1148 1269 def batchFitComplete(self, result): 1149 1270 """ … … 1152 1273 #re-enable the Fit button 1153 1274 self.setFittingStopped() 1275 # Show the grid panel 1276 self.grid_window = BatchOutputPanel(parent=self, output_data=result[0]) 1277 self.grid_window.show() 1278 1279 def fittingCompleted(self, result): 1280 """ 1281 Send the finish message from calculate threads to main thread 1282 """ 1283 self.fittingFinishedSignal.emit(result) 1154 1284 1155 1285 def fitComplete(self, result): … … 1162 1292 1163 1293 if result is None: 1164 msg = "Fitting failed after: %s s.\n" % GuiUtils.formatNumber(elapsed)1294 msg = "Fitting failed." 1165 1295 self.communicate.statusBarUpdateSignal.emit(msg) 1166 1296 return … … 1228 1358 smearing, accuracy, smearing_min, smearing_max = self.smearing_widget.state() 1229 1359 1360 # Get the constraints. 1230 1361 constraints = self.getComplexConstraintsForModel() 1362 if fitter is None: 1363 # For single fits - check for inter-model constraints 1364 constraints = self.getConstraintsForFitting() 1365 1231 1366 smearer = None 1232 1367 handler = None … … 1242 1377 constraints=constraints) 1243 1378 except ValueError as ex: 1244 logging.error("Setting model parameters failed with: %s" % ex) 1245 return 1379 raise ValueError("Setting model parameters failed with: %s" % ex) 1246 1380 1247 1381 qmin, qmax, _ = self.logic.computeRangeFromData(data) … … 1409 1543 if not dict: 1410 1544 return 1411 if self._m odel_model.rowCount() == 0:1545 if self._magnet_model.rowCount() == 0: 1412 1546 return 1413 1547 … … 1555 1689 self.models[model.name] = model 1556 1690 1691 self.readCustomCategoryInfo() 1692 1693 def readCustomCategoryInfo(self): 1694 """ 1695 Reads the custom model category 1696 """ 1697 #Looking for plugins 1698 self.plugins = list(self.custom_models.values()) 1699 plugin_list = [] 1700 for name, plug in self.custom_models.items(): 1701 self.models[name] = plug 1702 plugin_list.append([name, True]) 1703 self.master_category_dict[CATEGORY_CUSTOM] = plugin_list 1704 1557 1705 def regenerateModelDict(self): 1558 1706 """ … … 1662 1810 Setting model parameters into QStandardItemModel based on selected _model_ 1663 1811 """ 1664 kernel_module = generate.load_kernel_module(model_name) 1812 name = model_name 1813 if self.cbCategory.currentText() == CATEGORY_CUSTOM: 1814 # custom kernel load requires full path 1815 name = os.path.join(ModelUtilities.find_plugins_dir(), model_name+".py") 1816 kernel_module = generate.load_kernel_module(name) 1665 1817 self.model_parameters = modelinfo.make_parameter_table(getattr(kernel_module, 'parameters', [])) 1666 1818 -
TabularUnified src/sas/qtgui/Perspectives/Fitting/ModelUtilities.py ¶
- Property mode changed from 100755 to 100644
rb3e8629 r3b3b40b 179 179 plugins = {} 180 180 for filename in os.listdir(directory): 181 181 182 name, ext = os.path.splitext(filename) 182 183 if ext == '.py' and not name == '__init__': … … 184 185 try: 185 186 model = load_custom_model(path) 186 model.name = PLUGIN_NAME_BASE + model.name187 #model.name = PLUGIN_NAME_BASE + model.name 187 188 plugins[model.name] = model 188 189 except Exception: … … 415 416 implement model 416 417 """ 417 __modelmanager = ModelManagerBase() 418 cat_model_list = [__modelmanager.model_dictionary[model_name] for model_name \ 419 in list(__modelmanager.model_dictionary.keys()) \ 420 if model_name not in list(__modelmanager.stored_plugins.keys())] 421 422 CategoryInstaller.check_install(model_list=cat_model_list) 418 def __init__(self): 419 self.__modelmanager = ModelManagerBase() 420 self.cat_model_list = [self.__modelmanager.model_dictionary[model_name] for model_name \ 421 in list(self.__modelmanager.model_dictionary.keys()) \ 422 if model_name not in list(self.__modelmanager.stored_plugins.keys())] 423 424 CategoryInstaller.check_install(model_list=self.cat_model_list) 425 423 426 def findModels(self): 424 427 return self.__modelmanager.findModels() -
TabularUnified src/sas/qtgui/Perspectives/Fitting/MultiConstraint.py ¶
r14ec91c5 r3b3b40b 114 114 115 115 # 3. parameter name should be a separate word, but can have "()[]*+-/ " around 116 valid_neighbours = "()[]*+-/ "117 start_loc = parameter_string_start -1118 end_loc = parameter_string_end119 if not any([constraint_text[start_loc] == ch for ch in valid_neighbours]):120 return False121 if end_loc < len(constraint_text):122 if not any([constraint_text[end_loc] == ch for ch in valid_neighbours]):123 return False116 #valid_neighbours = "()[]*+-/ " 117 #start_loc = parameter_string_start -1 118 #end_loc = parameter_string_end 119 #if not any([constraint_text[start_loc] == ch for ch in valid_neighbours]): 120 # return False 121 #if end_loc < len(constraint_text): 122 # if not any([constraint_text[end_loc] == ch for ch in valid_neighbours]): 123 # return False 124 124 125 125 # 4. replace parameter name with "1" and try to evaluate the expression -
TabularUnified src/sas/qtgui/Perspectives/Fitting/UI/FittingWidgetUI.ui ¶
re4c475b7 r3b3b40b 7 7 <x>0</x> 8 8 <y>0</y> 9 <width> 568</width>9 <width>680</width> 10 10 <height>605</height> 11 11 </rect> -
TabularUnified src/sas/qtgui/Perspectives/Fitting/UnitTesting/ConstraintWidgetTest.py ¶
- Property mode changed from 100755 to 100644
r725d9c06 r3b3b40b 22 22 if not QtWidgets.QApplication.instance(): 23 23 app = QtWidgets.QApplication(sys.argv) 24 #app = QtWidgets.QApplication(sys.argv)25 24 26 25 class ConstraintWidgetTest(unittest.TestCase): … … 71 70 # click on "batch" 72 71 QTest.mouseClick(self.widget.btnBatch, QtCore.Qt.LeftButton) 73 app.processEvents()72 QtWidgets.QApplication.processEvents() 74 73 # See what the current type is now 75 74 self.assertEqual(self.widget.currentType, "BatchPage") … … 78 77 # Go back to single fit 79 78 QTest.mouseClick(self.widget.btnSingle, QtCore.Qt.LeftButton) 80 app.processEvents()79 QtWidgets.QApplication.processEvents() 81 80 # See what the current type is now 82 81 self.assertEqual(self.widget.currentType, "FitPage") -
TabularUnified src/sas/qtgui/Utilities/GuiUtils.py ¶
r63319b0 r3b3b40b 213 213 plotRequestedSignal = QtCore.pyqtSignal(list) 214 214 215 # Plot from file names 216 plotFromFilenameSignal = QtCore.pyqtSignal(str) 217 215 218 # Plot update requested from a perspective 216 219 plotUpdateSignal = QtCore.pyqtSignal(list) … … 236 239 # Send result of Data Operation Utility panel to Data Explorer 237 240 updateModelFromDataOperationPanelSignal = QtCore.pyqtSignal(QtGui.QStandardItem, dict) 241 242 # Notify about a new custom plugin being written/deleted/modified 243 customModelDirectoryChanged = QtCore.pyqtSignal() 238 244 239 245 def updateModelItemWithPlot(item, update_data, name=""): … … 872 878 raise TypeError 873 879 880 def findNextFilename(filename, directory): 881 """ 882 Finds the next available (non-existing) name for 'filename' in 'directory'. 883 plugin.py -> plugin (n).py - for first 'n' for which the file doesn't exist 884 """ 885 basename, ext = os.path.splitext(filename) 886 # limit the number of copies 887 MAX_FILENAMES = 1000 888 # Start with (1) 889 number_ext = 1 890 proposed_filename = "" 891 found_filename = False 892 # Find the next available filename or exit if too many copies 893 while not found_filename or number_ext > MAX_FILENAMES: 894 proposed_filename = basename + " ("+str(number_ext)+")" + ext 895 if os.path.exists(os.path.join(directory, proposed_filename)): 896 number_ext += 1 897 else: 898 found_filename = True 899 900 return proposed_filename 901 902 874 903 class DoubleValidator(QtGui.QDoubleValidator): 875 904 """ -
TabularUnified src/sas/qtgui/Utilities/LocalConfig.py ¶
rc3eb858 r3b3b40b 141 141 # Time out for updating sasview 142 142 UPDATE_TIMEOUT = 2 143 144 # Logging levels to disable, if any 145 DISABLE_LOGGING = logging.DEBUG 146 143 147 def printEVT(message): 144 148 """ -
TabularUnified .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 -
TabularUnified run.py ¶
r4992ff2 rd744767 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/InversionLogic.py ¶
- Property mode changed from 100755 to 100644
-
TabularUnified src/sas/qtgui/Perspectives/Inversion/InversionPerspective.py ¶
re90988c r304e42f 29 29 BACKGROUND_INPUT = 0.0 30 30 MAX_DIST = 140.0 31 32 # TODO: Modify plot references, don't just send new 31 DICT_KEYS = ["Calculator", "PrPlot", "DataPlot", "DMaxWindow", 32 "Logic", "NFuncEst"] 33 34 35 # TODO: Remove not working 36 # TODO: Explore window not working 33 37 # TODO: Update help with batch capabilities 34 38 # TODO: Method to export results in some meaningful way … … 50 54 51 55 self._manager = parent 52 self._model_item = QtGui.QStandardItem()53 56 self.communicate = GuiUtils.Communicate() 54 57 55 58 self.logic = InversionLogic() 56 57 # Reference to Dmax window58 self.dmaxWindow = None59 59 60 60 # The window should not close 61 61 self._allow_close = False 62 62 63 # Visible data set items 63 64 # current QStandardItem showing on the panel 64 65 self._data = None 65 66 # current Data1D as referenced by self._data 66 67 self._data_set = None 67 68 # p(r) calculator 68 # Reference to Dmax window for self._data 69 self.dmaxWindow = None 70 # p(r) calculator for self._data 69 71 self._calculator = Invertor() 70 self._last_calculator = None 72 # Default to background estimate 73 self._calculator.set_est_bck(True) 74 # plots of self._data 75 self.pr_plot = None 76 self.data_plot = None 77 # suggested nTerms 78 self.nTermsSuggested = NUMBER_OF_TERMS 79 80 # Calculation threads used by all data items 71 81 self.calc_thread = None 72 82 self.estimation_thread = None 73 83 74 # Current data object in view 75 self._data_index = 0 76 # list mapping data to p(r) calculation 84 # Mapping for all data items 85 # list mapping data to all parameters 77 86 self._data_list = {} 78 87 if not isinstance(data, list): … … 80 89 if data is not None: 81 90 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 = {} 91 self.updateDataList(datum) 92 93 self.enableButtons() 97 94 98 95 self.model = QtGui.QStandardItemModel(self) … … 158 155 159 156 self.backgroundInput.editingFinished.connect( 160 lambda: self._calculator.set_ est_bck(int(is_float(self.backgroundInput.text()))))157 lambda: self._calculator.set_background(is_float(self.backgroundInput.text()))) 161 158 self.minQInput.editingFinished.connect( 162 159 lambda: self._calculator.set_qmin(is_float(self.minQInput.text()))) … … 308 305 309 306 def displayChange(self): 307 """Switch to another item in the data list""" 310 308 ref_item = self.dataList.itemData(self.dataList.currentIndex()) 311 self. _model_item = ref_item309 self.updateDataList(ref_item) 312 310 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) 311 self.updateGuiValues() 332 312 333 313 ###################################################################### 334 314 # GUI Interaction Events 335 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 315 341 316 def update_calculator(self): … … 355 330 InversionWindow.__init__(self.parent(), list(self._data_list.keys())) 356 331 exit(0) 357 # TODO: Only send plot first time - otherwise, update in complete358 332 if self.pr_plot is not None: 359 333 title = self.pr_plot.name … … 363 337 GuiUtils.updateModelItemWithPlot(self._data, self.data_plot, title) 364 338 if self.dmaxWindow is not None: 365 366 367 339 self.dmaxWindow.pr_state = self._calculator 340 self.dmaxWindow.nfunc = self.getNFunc() 341 self.dmaxWindow.modelChanged() 368 342 self.mapper.toFirst() 369 343 … … 386 360 if sender is self.estimateBgd: 387 361 self.backgroundInput.setEnabled(False) 362 self._calculator.set_est_bck = True 388 363 else: 389 364 self.backgroundInput.setEnabled(True) 365 self._calculator.set_est_bck = False 390 366 391 367 def openExplorerWindow(self): … … 410 386 if not isinstance(data_item, list): 411 387 msg = "Incorrect type passed to the P(r) Perspective" 412 raise AttributeError 388 raise AttributeError(msg) 413 389 414 390 for data in data_item: … … 417 393 return 418 394 # Create initial internal mappings 419 self._data_list[data] = self._calculator.clone()420 395 self._data_set = GuiUtils.dataFromItem(data) 421 self.data_plot_list[data] = self.data_plot 422 self.pr_plot_list[data] = self.pr_plot 396 self.logic = InversionLogic(self._data_set) 423 397 self.populateDataComboBox(self._data_set.filename, data) 398 # Estimate q range 399 qmin, qmax = self.logic.computeDataRange() 400 self._calculator.set_qmin(qmin) 401 self._calculator.set_qmax(qmax) 402 self.updateDataList(data) 424 403 self.setCurrentData(data) 425 426 404 # Estimate initial values from data 427 405 self.performEstimate() 428 self.logic = InversionLogic(self._data_set)429 430 # Estimate q range431 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.model435 self.model_item = data436 437 406 self.enableButtons() 407 self.updateGuiValues() 408 409 def updateDataList(self, dataRef): 410 """Save the current data state of the window into self._data_list""" 411 if dataRef is None: 412 return 413 self._data_list[dataRef] = { 414 DICT_KEYS[0]: self._calculator, 415 DICT_KEYS[1]: self.pr_plot, 416 DICT_KEYS[2]: self.data_plot, 417 DICT_KEYS[3]: self.dmaxWindow, 418 DICT_KEYS[4]: self.logic, 419 DICT_KEYS[5]: self.nTermsSuggested 420 } 438 421 439 422 def getNFunc(self): … … 448 431 449 432 def setCurrentData(self, data_ref): 450 """Get the current data and display as necessary""" 451 433 """Get the data by reference and display as necessary""" 452 434 if data_ref is None: 453 435 return 454 455 436 if not isinstance(data_ref, QtGui.QStandardItem): 456 437 msg = "Incorrect type passed to the P(r) Perspective" 457 raise AttributeError 458 438 raise AttributeError(msg) 459 439 # Data references 460 440 self._data = data_ref 461 441 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] 442 self._calculator = self._data_list[data_ref].get(DICT_KEYS[0]) 443 self.pr_plot = self._data_list[data_ref].get(DICT_KEYS[1]) 444 self.data_plot = self._data_list[data_ref].get(DICT_KEYS[2]) 445 self.dmaxWindow = self._data_list[data_ref].get(DICT_KEYS[3]) 446 self.logic = self._data_list[data_ref].get(DICT_KEYS[4]) 447 self.nTermsSuggested = self._data_list[data_ref].get(DICT_KEYS[5]) 448 self.updateGuiValues() 449 450 def updateGuiValues(self): 451 pr = self._calculator 452 out = self._calculator.out 453 cov = self._calculator.cov 454 elapsed = self._calculator.elapsed 455 alpha = self._calculator.suggested_alpha 456 nterms = self._calculator.nfunc 457 self.model.setItem(WIDGETS.W_QMIN, 458 QtGui.QStandardItem("{:.4g}".format(pr.get_qmin()))) 459 self.model.setItem(WIDGETS.W_QMAX, 460 QtGui.QStandardItem("{:.4g}".format(pr.get_qmax()))) 461 self.model.setItem(WIDGETS.W_BACKGROUND_INPUT, 462 QtGui.QStandardItem("{:.3f}".format(pr.background))) 463 self.model.setItem(WIDGETS.W_BACKGROUND_OUTPUT, 464 QtGui.QStandardItem("{:.3g}".format(pr.background))) 465 self.model.setItem(WIDGETS.W_COMP_TIME, 466 QtGui.QStandardItem("{:.4g}".format(elapsed))) 467 if alpha != 0: 468 self.regConstantSuggestionButton.setText("{:-3.2g}".format(alpha)) 469 self.regConstantSuggestionButton.setEnabled(alpha != self._calculator.alpha) 470 if nterms != self.nTermsSuggested: 471 self.noOfTermsSuggestionButton.setText( 472 "{:n}".format(self.nTermsSuggested)) 473 self.noOfTermsSuggestionButton.setEnabled(nterms != self.nTermsSuggested) 474 self.model.setItem(WIDGETS.W_COMP_TIME, 475 QtGui.QStandardItem("{:.2g}".format(elapsed))) 476 477 if isinstance(pr.chi2, np.ndarray): 478 self.model.setItem(WIDGETS.W_CHI_SQUARED, 479 QtGui.QStandardItem("{:.3g}".format(pr.chi2[0]))) 480 if out is not None: 481 self.model.setItem(WIDGETS.W_RG, 482 QtGui.QStandardItem("{:.3g}".format(pr.rg(out)))) 483 self.model.setItem(WIDGETS.W_I_ZERO, 484 QtGui.QStandardItem( 485 "{:.3g}".format(pr.iq0(out)))) 486 self.model.setItem(WIDGETS.W_OSCILLATION, QtGui.QStandardItem( 487 "{:.3g}".format(pr.oscillations(out)))) 488 self.model.setItem(WIDGETS.W_POS_FRACTION, QtGui.QStandardItem( 489 "{:.3g}".format(pr.get_positive(out)))) 490 if cov is not None: 491 self.model.setItem(WIDGETS.W_SIGMA_POS_FRACTION, 492 QtGui.QStandardItem( 493 "{:.3g}".format( 494 pr.get_pos_err(out, cov)))) 495 496 def removeData(self): 497 """Remove the existing data reference from the P(r) Persepective""" 498 if self.dmaxWindow is not None: 499 self.dmaxWindow = None 500 self.dataList.removeItem(self.dataList.currentIndex()) 501 self._data_list.pop(self._data) 502 # Last file removed 503 if len(self._data_list) == 0: 504 self._data = None 505 self.pr_plot = None 506 self._data_set = None 507 self._calculator = Invertor() 508 self.logic = InversionLogic() 509 self.enableButtons() 510 self.dataList.setCurrentIndex(0) 511 self.updateGuiValues() 465 512 466 513 ###################################################################### 467 514 # Thread Creators 468 515 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 516 for data_ref in self._data_list.keys(): 517 self.setCurrentData(data_ref) 472 518 self.startThread() 473 519 … … 554 600 """ 555 601 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) 602 self.updateGuiValues() 560 603 if message: 561 604 logging.info(message) … … 576 619 """ 577 620 nterms, alpha, message, elapsed = output_tuple 621 self._calculator.elapsed = elapsed 622 self._calculator.suggested_alpha = alpha 623 self.nTermsSuggested = nterms 578 624 # 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))) 625 self.updateGuiValues() 584 626 if message: 585 627 logging.info(message) … … 605 647 pr.elapsed = elapsed 606 648 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 649 # Save Pr invertor 621 650 self._calculator = pr 622 # Append data to data list623 self._data_list[self._data] = self._calculator.clone()624 625 # Update model dict626 self._models[self.model_item] = self.model627 651 628 652 # Create new P(r) and fit plots 629 653 if self.pr_plot is None: 630 654 self.pr_plot = self.logic.newPRPlot(out, self._calculator, cov) 631 self.pr_plot_list[self._data] = self.pr_plot632 else:633 # FIXME: this should update the existing plot, not create a new one634 self.pr_plot = self.logic.newPRPlot(out, self._calculator, cov)635 self.pr_plot_list[self._data] = self.pr_plot636 655 if self.data_plot is None: 637 656 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 657 self.updateDataList(self._data) 658 self.updateGuiValues() 643 659 644 660 def _threadError(self, error): -
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
-
src/sas/qtgui/Perspectives/Inversion/UI/__init__.py ¶
- Property mode changed from 100755 to 100644
-
src/sas/qtgui/Perspectives/Inversion/__init__.py ¶
- Property mode changed from 100755 to 100644
-
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
Note: See TracChangeset
for help on using the changeset viewer.