Changes in / [304e42f:8289ae3] in sasview


Ignore:
Files:
17 added
15 edited

Legend:

Unmodified
Added
Removed
  • setup.py

    • Property mode changed from 100644 to 100755
    r14ec91c5 r3b3b40b  
    351351    "src", "sas", "qtgui", "Utilities") 
    352352packages.append("sas.qtgui.Utilities") 
     353package_dir["sas.qtgui.UtilitiesUI"] = os.path.join( 
     354    "src", "sas", "qtgui", "Utilities","UI") 
     355packages.append("sas.qtgui.Utilities.UI") 
    353356 
    354357package_dir["sas.qtgui.Calculators"] = os.path.join( 
  • src/sas/qtgui/GUITests.py

    rda9a0722 r3b3b40b  
    4444from Utilities.UnitTesting import GuiUtilsTest 
    4545from Utilities.UnitTesting import SasviewLoggerTest 
     46from Utilities.UnitTesting import GridPanelTest 
     47from Utilities.UnitTesting import ModelEditorTest 
     48from Utilities.UnitTesting import PluginDefinitionTest 
     49from Utilities.UnitTesting import TabbedModelEditorTest 
    4650 
    4751# Unit Testing 
     
    99103        unittest.makeSuite(GuiUtilsTest.DoubleValidatorTest,      'test'), 
    100104        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'), 
    101109 
    102110        # Calculators 
  • src/sas/qtgui/MainWindow/DataExplorer.py

    re90988c r8ac3551  
    473473        Notify the gui manager about the new perspective chosen. 
    474474        """ 
    475         self.communicator.perspectiveChangedSignal.emit(self.cbFitting.currentText()) 
     475        self.communicator.perspectiveChangedSignal.emit(self.cbFitting.itemText(index)) 
    476476        self.chkBatch.setEnabled(self.parent.perspective().allowBatch()) 
    477477 
    478     def displayData(self, data_list): 
     478    def displayFile(self, filename=None, is_data=True): 
    479479        """ 
    480480        Forces display of charts for the given filename 
    481481        """ 
    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 
    490483        # Now query the model item for available plots 
    491484        plots = GuiUtils.plotsFromFilename(filename, model) 
     
    506499        if new_plots: 
    507500            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) 
    508512 
    509513    def addDataPlot2D(self, plot_set, item): 
     
    10271031        pass 
    10281032 
     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 
    10291043    def loadComplete(self, output): 
    10301044        """ 
  • src/sas/qtgui/MainWindow/GuiManager.py

    r14ec91c5 r8ac3551  
    1919 
    2020import sas.qtgui.Utilities.ObjectLibrary as ObjectLibrary 
     21from sas.qtgui.Utilities.TabbedModelEditor import TabbedModelEditor 
     22from sas.qtgui.Utilities.PluginManager import PluginManager 
    2123from sas.qtgui.MainWindow.UI.AcknowledgementsUI import Ui_Acknowledgements 
    2224from sas.qtgui.MainWindow.AboutBox import AboutBox 
     
    4749    Main SasView window functionality 
    4850    """ 
    49  
    5051    def __init__(self, parent=None): 
    5152        """ 
     
    182183        Respond to change of the perspective signal 
    183184        """ 
    184  
    185         # Save users from themselves... 
    186         #if isinstance(self._current_perspective, Perspectives.PERSPECTIVES[str(perspective_name)]): 
    187         self.setupPerspectiveMenubarOptions(self._current_perspective) 
    188         #    return 
    189  
    190185        # Close the previous perspective 
    191186        self.clearPerspectiveMenubarOptions(self._current_perspective) 
     
    197192        # Default perspective 
    198193        self._current_perspective = Perspectives.PERSPECTIVES[str(perspective_name)](parent=self) 
     194 
     195        self.setupPerspectiveMenubarOptions(self._current_perspective) 
    199196 
    200197        subwindow = self._workspace.workspace.addSubWindow(self._current_perspective) 
     
    358355        self.communicate.updateTheoryFromPerspectiveSignal.connect(self.updateTheoryFromPerspective) 
    359356        self.communicate.plotRequestedSignal.connect(self.showPlot) 
     357        self.communicate.plotFromFilenameSignal.connect(self.showPlotFromFilename) 
    360358        self.communicate.updateModelFromDataOperationPanelSignal.connect(self.updateModelFromDataOperationPanel) 
    361359 
     
    406404        self._workspace.actionFit_Results.triggered.connect(self.actionFit_Results) 
    407405        self._workspace.actionChain_Fitting.triggered.connect(self.actionChain_Fitting) 
     406        self._workspace.actionAdd_Custom_Model.triggered.connect(self.actionAdd_Custom_Model) 
    408407        self._workspace.actionEdit_Custom_Model.triggered.connect(self.actionEdit_Custom_Model) 
     408        self._workspace.actionManage_Custom_Models.triggered.connect(self.actionManage_Custom_Models) 
    409409        # Window 
    410410        self._workspace.actionCascade.triggered.connect(self.actionCascade) 
     
    417417        self._workspace.actionInversion.triggered.connect(self.actionInversion) 
    418418        self._workspace.actionInvariant.triggered.connect(self.actionInvariant) 
     419        self._workspace.actionCorfunc.triggered.connect(self.actionCorfunc) 
    419420        # Help 
    420421        self._workspace.actionDocumentation.triggered.connect(self.actionDocumentation) 
     
    662663        pass 
    663664 
     665    def actionAdd_Custom_Model(self): 
     666        """ 
     667        """ 
     668        self.model_editor = TabbedModelEditor(self) 
     669        self.model_editor.show() 
     670 
    664671    def actionEdit_Custom_Model(self): 
    665672        """ 
    666673        """ 
    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() 
    669682 
    670683    #============ ANALYSIS ================= 
     
    674687        """ 
    675688        self.perspectiveChanged("Fitting") 
     689        # Notify other widgets 
     690        self.filesWidget.onAnalysisUpdate("Fitting") 
    676691 
    677692    def actionInversion(self): 
     
    679694        Change to the Inversion perspective 
    680695        """ 
    681         # For now we'll just update the analysis menu status but when the inversion is implemented delete from here 
    682         self.checkAnalysisOption(self._workspace.actionInversion) 
    683         # to here and uncomment the following line 
    684696        self.perspectiveChanged("Inversion") 
     697        self.filesWidget.onAnalysisUpdate("Inversion") 
    685698 
    686699    def actionInvariant(self): 
     
    689702        """ 
    690703        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") 
    691712 
    692713    #============ WINDOW ================= 
     
    779800        self.filesWidget.model.appendRow(new_item) 
    780801        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) 
    781809 
    782810    def showPlot(self, plot): 
     
    828856        elif isinstance(perspective, Perspectives.PERSPECTIVES["Invariant"]): 
    829857            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) 
  • src/sas/qtgui/MainWindow/MainWindow.py

    r8353d90 r8ac3551  
    3030        except Exception as ex: 
    3131            import logging 
    32             logging.error("Application failed with: ", ex) 
     32            logging.error("Application failed with: " + str(ex)) 
    3333            print("Application failed with: ", ex) 
    3434 
  • src/sas/qtgui/MainWindow/UI/MainWindowUI.ui

    r1543f0c r8ac3551  
    88    <y>0</y> 
    99    <width>915</width> 
    10     <height>527</height> 
     10    <height>762</height> 
    1111   </rect> 
    1212  </property> 
     
    2525     <y>0</y> 
    2626     <width>915</width> 
    27      <height>21</height> 
     27     <height>26</height> 
    2828    </rect> 
    2929   </property> 
     
    108108    <addaction name="actionChain_Fitting"/> 
    109109    <addaction name="separator"/> 
     110    <addaction name="actionAdd_Custom_Model"/> 
    110111    <addaction name="actionEdit_Custom_Model"/> 
     112    <addaction name="actionManage_Custom_Models"/> 
    111113   </widget> 
    112114   <widget class="QMenu" name="menuWindow"> 
     
    126128     <string>Analysis</string> 
    127129    </property> 
     130    <addaction name="actionCorfunc"/> 
    128131    <addaction name="actionFitting"/> 
     132    <addaction name="actionInvariant"/> 
    129133    <addaction name="actionInversion"/> 
    130     <addaction name="actionInvariant"/> 
    131134   </widget> 
    132135   <widget class="QMenu" name="menuHelp"> 
     
    505508   </property> 
    506509  </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> 
    507528 </widget> 
    508529 <resources/> 
  • src/sas/qtgui/Perspectives/Fitting/ConstraintWidget.py

    re4c475b7 r8b480d27  
    111111 
    112112        # Disconnect all local slots 
    113         tab_object.disconnect() 
     113        #tab_object.disconnect() 
    114114 
    115115        # Reconnect tab signals to local slots 
     
    171171                    # No such tab! 
    172172                    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) 
    174175                page_ids.append([tab_object.page_id]) 
    175176        except ValueError: 
     
    178179                            "Not all tabs chosen for fitting have parameters selected for fitting." 
    179180            QtWidgets.QMessageBox.warning(self, 
    180                                            'Warning', 
    181                                             no_params_msg, 
    182                                             QtWidgets.QMessageBox.Ok) 
     181                                          'Warning', 
     182                                           no_params_msg, 
     183                                           QtWidgets.QMessageBox.Ok) 
    183184 
    184185            return 
     
    674675 
    675676        constraint.func = constraint_text 
     677        # param1 is the parameter we're constraining 
    676678        constraint.param = param1 
     679 
    677680        # Find the right tab 
    678681        constrained_tab = self.getObjectByName(model1) 
  • src/sas/qtgui/Perspectives/Fitting/FittingPerspective.py

    r14ec91c5 r3b3b40b  
    88from bumps import fitters 
    99 
     10import sas.qtgui.Utilities.LocalConfig as LocalConfig 
    1011import sas.qtgui.Utilities.ObjectLibrary as ObjectLibrary 
    1112 
     
    116117        tab     = FittingWidget(parent=self.parent, data=data, tab_id=self.maxIndex+1) 
    117118        tab.is_batch_fitting = is_batch 
     119 
    118120        # Add this tab to the object library so it can be retrieved by scripting/jupyter 
    119121        tab_name = self.getTabName(is_batch=is_batch) 
     
    123125            self.updateFitDict(data, tab_name) 
    124126        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 
    126134        self.tabsModifiedSignal.emit() 
    127135 
     
    140148        ObjectLibrary.addObject(tab_name, tab) 
    141149        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) 
    143156 
    144157    def updateFitDict(self, item_key, tab_name): 
  • src/sas/qtgui/Perspectives/Fitting/FittingWidget.py

    re4c475b7 r8b480d27  
    2424import sas.qtgui.Utilities.GuiUtils as GuiUtils 
    2525import sas.qtgui.Utilities.LocalConfig as LocalConfig 
     26from sas.qtgui.Utilities.GridPanel import BatchOutputPanel 
    2627from sas.qtgui.Utilities.CategoryInstaller import CategoryInstaller 
    2728from sas.qtgui.Plotting.PlotterData import Data1D 
     
    3637from sas.qtgui.Perspectives.Fitting.FittingLogic import FittingLogic 
    3738from sas.qtgui.Perspectives.Fitting import FittingUtilities 
     39from sas.qtgui.Perspectives.Fitting import ModelUtilities 
    3840from sas.qtgui.Perspectives.Fitting.SmearingWidget import SmearingWidget 
    3941from sas.qtgui.Perspectives.Fitting.OptionsWidget import OptionsWidget 
     
    5052CATEGORY_DEFAULT = "Choose category..." 
    5153CATEGORY_STRUCTURE = "Structure Factor" 
     54CATEGORY_CUSTOM = "Plugin Models" 
    5255STRUCTURE_DEFAULT = "None" 
    5356 
     
    8386    constraintAddedSignal = QtCore.pyqtSignal(list) 
    8487    newModelSignal = QtCore.pyqtSignal() 
     88    fittingFinishedSignal = QtCore.pyqtSignal(tuple) 
     89    batchFittingFinishedSignal = QtCore.pyqtSignal(tuple) 
     90 
    8591    def __init__(self, parent=None, data=None, tab_id=1): 
    8692 
     
    211217        self.page_stack = [] 
    212218        self.all_data = [] 
     219        # custom plugin models 
     220        # {model.name:model} 
     221        self.custom_models = self.customModels() 
    213222        # Polydisp widget table default index for function combobox 
    214223        self.orig_poly_index = 3 
     
    415424            self.onSelectModel() 
    416425 
     426    @classmethod 
     427    def customModels(cls): 
     428        """ Reads in file names in the custom plugin directory """ 
     429        return ModelUtilities._find_models() 
     430 
    417431    def initializeControls(self): 
    418432        """ 
     
    465479        self._poly_model.itemChanged.connect(self.onPolyModelChange) 
    466480        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) 
    467486 
    468487        # Signals from separate tabs asking for replot 
    469488        self.options_widget.plot_signal.connect(self.onOptionsUpdate) 
     489 
     490        # Signals from other widgets 
     491        self.communicate.customModelDirectoryChanged.connect(self.onCustomModelChange) 
    470492 
    471493    def modelName(self): 
     
    576598        # widget.params[0] is the parameter we're constraining 
    577599        constraint.param = mc_widget.params[0] 
    578         # Function should have the model name preamble 
     600        # parameter should have the model name preamble 
    579601        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 
    581608        # Which row is the constrained parameter in? 
    582609        row = self.getRowFromName(constraint.param) 
     
    677704        Delete constraints from selected parameters. 
    678705        """ 
    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) 
    680710 
    681711    def deleteConstraintOnParameter(self, param=None): 
     
    686716        max_col = self.lstParams.itemDelegate().param_max 
    687717        for row in range(self._model_model.rowCount()): 
     718            if not self.rowHasConstraint(row): 
     719                continue 
    688720            # Get the Constraint object from of the model item 
    689721            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) 
    693723            if constraint is None: 
    694724                continue 
     
    816846        return constraints 
    817847 
     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 
    818885    def showModelDescription(self): 
    819886        """ 
     
    874941        self.respondToModelStructure(model=model, structure_factor=structure) 
    875942 
     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 
    876981    def replaceConstraintName(self, old_name, new_name=""): 
    877982        """ 
     
    886991                    new_func = func.replace(old_name, new_name) 
    887992                    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 
    8881003 
    8891004    def updateData(self): 
     
    11051220        except ValueError as ex: 
    11061221            # 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)) 
    11081223            return 
    11091224 
    11101225        # Create the fitting thread, based on the fitter 
    1111         completefn = self.batchFitComplete if self.is_batch_fitting else self.fitComplete 
     1226        completefn = self.batchFittingCompleted if self.is_batch_fitting else self.fittingCompleted 
    11121227 
    11131228        calc_fit = FitThread(handler=handler, 
     
    11461261        pass 
    11471262 
     1263    def batchFittingCompleted(self, result): 
     1264        """ 
     1265        Send the finish message from calculate threads to main thread 
     1266        """ 
     1267        self.batchFittingFinishedSignal.emit(result) 
     1268 
    11481269    def batchFitComplete(self, result): 
    11491270        """ 
     
    11521273        #re-enable the Fit button 
    11531274        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) 
    11541284 
    11551285    def fitComplete(self, result): 
     
    11621292 
    11631293        if result is None: 
    1164             msg = "Fitting failed after: %s s.\n" % GuiUtils.formatNumber(elapsed) 
     1294            msg = "Fitting failed." 
    11651295            self.communicate.statusBarUpdateSignal.emit(msg) 
    11661296            return 
     
    12281358        smearing, accuracy, smearing_min, smearing_max = self.smearing_widget.state() 
    12291359 
     1360        # Get the constraints. 
    12301361        constraints = self.getComplexConstraintsForModel() 
     1362        if fitter is None: 
     1363            # For single fits - check for inter-model constraints 
     1364            constraints = self.getConstraintsForFitting() 
     1365 
    12311366        smearer = None 
    12321367        handler = None 
     
    12421377                             constraints=constraints) 
    12431378            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) 
    12461380 
    12471381            qmin, qmax, _ = self.logic.computeRangeFromData(data) 
     
    14091543        if not dict: 
    14101544            return 
    1411         if self._model_model.rowCount() == 0: 
     1545        if self._magnet_model.rowCount() == 0: 
    14121546            return 
    14131547 
     
    15551689            self.models[model.name] = model 
    15561690 
     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 
    15571705    def regenerateModelDict(self): 
    15581706        """ 
     
    16621810        Setting model parameters into QStandardItemModel based on selected _model_ 
    16631811        """ 
    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) 
    16651817        self.model_parameters = modelinfo.make_parameter_table(getattr(kernel_module, 'parameters', [])) 
    16661818 
  • src/sas/qtgui/Perspectives/Fitting/ModelUtilities.py

    • Property mode changed from 100755 to 100644
    rb3e8629 r3b3b40b  
    179179    plugins = {} 
    180180    for filename in os.listdir(directory): 
     181 
    181182        name, ext = os.path.splitext(filename) 
    182183        if ext == '.py' and not name == '__init__': 
     
    184185            try: 
    185186                model = load_custom_model(path) 
    186                 model.name = PLUGIN_NAME_BASE + model.name 
     187                #model.name = PLUGIN_NAME_BASE + model.name 
    187188                plugins[model.name] = model 
    188189            except Exception: 
     
    415416    implement model 
    416417    """ 
    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 
    423426    def findModels(self): 
    424427        return self.__modelmanager.findModels() 
  • src/sas/qtgui/Perspectives/Fitting/MultiConstraint.py

    r14ec91c5 r3b3b40b  
    114114 
    115115        # 3. parameter name should be a separate word, but can have "()[]*+-/ " around 
    116         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 
     116        #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 
    124124 
    125125        # 4. replace parameter name with "1" and try to evaluate the expression 
  • src/sas/qtgui/Perspectives/Fitting/UI/FittingWidgetUI.ui

    re4c475b7 r3b3b40b  
    77    <x>0</x> 
    88    <y>0</y> 
    9     <width>568</width> 
     9    <width>680</width> 
    1010    <height>605</height> 
    1111   </rect> 
  • src/sas/qtgui/Perspectives/Fitting/UnitTesting/ConstraintWidgetTest.py

    • Property mode changed from 100755 to 100644
    r725d9c06 r3b3b40b  
    2222if not QtWidgets.QApplication.instance(): 
    2323    app = QtWidgets.QApplication(sys.argv) 
    24 #app = QtWidgets.QApplication(sys.argv) 
    2524 
    2625class ConstraintWidgetTest(unittest.TestCase): 
     
    7170        # click on "batch" 
    7271        QTest.mouseClick(self.widget.btnBatch, QtCore.Qt.LeftButton) 
    73         app.processEvents() 
     72        QtWidgets.QApplication.processEvents() 
    7473        # See what the current type is now 
    7574        self.assertEqual(self.widget.currentType, "BatchPage") 
     
    7877        # Go back to single fit 
    7978        QTest.mouseClick(self.widget.btnSingle, QtCore.Qt.LeftButton) 
    80         app.processEvents() 
     79        QtWidgets.QApplication.processEvents() 
    8180        # See what the current type is now 
    8281        self.assertEqual(self.widget.currentType, "FitPage") 
  • src/sas/qtgui/Utilities/GuiUtils.py

    r63319b0 r3b3b40b  
    213213    plotRequestedSignal = QtCore.pyqtSignal(list) 
    214214 
     215    # Plot from file names 
     216    plotFromFilenameSignal = QtCore.pyqtSignal(str) 
     217 
    215218    # Plot update requested from a perspective 
    216219    plotUpdateSignal = QtCore.pyqtSignal(list) 
     
    236239    # Send result of Data Operation Utility panel to Data Explorer 
    237240    updateModelFromDataOperationPanelSignal = QtCore.pyqtSignal(QtGui.QStandardItem, dict) 
     241 
     242    # Notify about a new custom plugin being written/deleted/modified 
     243    customModelDirectoryChanged = QtCore.pyqtSignal() 
    238244 
    239245def updateModelItemWithPlot(item, update_data, name=""): 
     
    872878        raise TypeError 
    873879 
     880def 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 
    874903class DoubleValidator(QtGui.QDoubleValidator): 
    875904    """ 
  • src/sas/qtgui/Utilities/LocalConfig.py

    rc3eb858 r3b3b40b  
    141141# Time out for updating sasview 
    142142UPDATE_TIMEOUT = 2 
     143 
     144# Logging levels to disable, if any 
     145DISABLE_LOGGING = logging.DEBUG 
     146 
    143147def printEVT(message): 
    144148    """ 
Note: See TracChangeset for help on using the changeset viewer.