source: sasview/src/sas/qtgui/Perspectives/Fitting/FittingPerspective.py @ 14fa542

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalc
Last change on this file since 14fa542 was 14fa542, checked in by krzywon, 7 years ago

Auto-populate OpenCL dialog with available options to finalize GUI.

  • Property mode set to 100644
File size: 7.8 KB
RevLine 
[6f7f652]1import numpy
[0efe791]2
[4c7dd9f]3from PyQt4 import QtCore
[60af928]4from PyQt4 import QtGui
[4c7dd9f]5
[2d0e0c1]6from bumps import options
7from bumps import fitters
8
[61a92d4]9import sas.qtgui.Utilities.ObjectLibrary as ObjectLibrary
[5236449]10
[1bc27f1]11from sas.qtgui.Perspectives.Fitting.FittingWidget import FittingWidget
[2d0e0c1]12from sas.qtgui.Perspectives.Fitting.FittingOptions import FittingOptions
[06ce180]13from sas.qtgui.Perspectives.Fitting.GPUOptions import GPUOptions
[125c4be]14from sas.qtgui.Perspectives.Fitting import ModelUtilities
[8bdb6f5]15
[60af928]16class FittingWindow(QtGui.QTabWidget):
[0efe791]17    """
18    """
[60af928]19    name = "Fitting" # For displaying in the combo box in DataExplorer
[811bec1]20    def __init__(self, parent=None, data=None):
[f46f6dc]21        super(FittingWindow, self).__init__()
22
23        self.parent = parent
24        self._data = data
25
26        # List of active fits
27        self.tabs = []
28
29        # Max index for adding new, non-clashing tab names
30        self.maxIndex = 0
31
32        # Index of the current tab
33        self.currentTab = 0
34
[38eb433]35        # The default optimizer
[1bc27f1]36        self.optimizer = 'Levenberg-Marquardt'
[f46f6dc]37
[38eb433]38        # Dataset inde -> Fitting tab mapping
39        self.dataToFitTab = {}
40
[f46f6dc]41        # The tabs need to be closeable
42        self.setTabsClosable(True)
[60af928]43
[811bec1]44        self.communicate = self.parent.communicator()
45
[60af928]46        # Initialize the first tab
[f46f6dc]47        self.addFit(None)
48
49        # Deal with signals
50        self.tabCloseRequested.connect(self.tabCloses)
[38eb433]51        self.communicate.dataDeletedSignal.connect(self.dataDeleted)
[f46f6dc]52
[b1e36a3]53        # Perspective window not allowed to close by default
54        self._allow_close = False
55
[2d0e0c1]56        # Fit options - uniform for all tabs
57        self.fit_options = options.FIT_CONFIG
58        self.fit_options_widget = FittingOptions(self, config=self.fit_options)
59        self.fit_options.selected_id = fitters.LevenbergMarquardtFit.id
60
61        # Listen to GUI Manager signal updating fit options
62        self.fit_options_widget.fit_option_changed.connect(self.onFittingOptionsChange)
63
[06ce180]64        # GPU Options
[14fa542]65        self.gpu_options_widget = GPUOptions(self)
[06ce180]66
[125c4be]67        self.menu_manager = ModelUtilities.ModelManager()
68        # TODO: reuse these in FittingWidget properly
69        self.model_list_box = self.menu_manager.get_model_list()
70        self.model_dictionary = self.menu_manager.get_model_dictionary()
71
[2d0e0c1]72        #self.setWindowTitle('Fit panel - Active Fitting Optimizer: %s' % self.optimizer)
73        self.updateWindowTitle()
74
75    def updateWindowTitle(self):
76        """
77        Update the window title with the current optimizer name
78        """
79        self.optimizer = self.fit_options.selected_name
[f46f6dc]80        self.setWindowTitle('Fit panel - Active Fitting Optimizer: %s' % self.optimizer)
[60af928]81
[2d0e0c1]82
[b1e36a3]83    def setClosable(self, value=True):
84        """
85        Allow outsiders close this widget
86        """
87        assert isinstance(value, bool)
88
89        self._allow_close = value
90
91    def closeEvent(self, event):
92        """
93        Overwrite QDialog close method to allow for custom widget close
94        """
[2add354]95        # Invoke fit page events
96        for tab in self.tabs:
97            tab.close()
[b1e36a3]98        if self._allow_close:
99            # reset the closability flag
100            self.setClosable(value=False)
101            event.accept()
102        else:
103            # Maybe we should just minimize
104            self.setWindowState(QtCore.Qt.WindowMinimized)
[2add354]105            event.ignore()
[b1e36a3]106
[ee18d33]107    def addFit(self, data, is_batch=False):
[60af928]108        """
109        Add a new tab for passed data
110        """
[1bc27f1]111        tab     = FittingWidget(parent=self.parent, data=data, tab_id=self.maxIndex+1)
[ee18d33]112        tab.is_batch_fitting = is_batch
[61a92d4]113        # Add this tab to the object library so it can be retrieved by scripting/jupyter
[38eb433]114        tab_name = self.tabName(is_batch=is_batch)
115        ObjectLibrary.addObject(tab_name, tab)
[f46f6dc]116        self.tabs.append(tab)
[38eb433]117        if data:
118            self.updateFitDict(data, tab_name)
[f46f6dc]119        self.maxIndex += 1
[38eb433]120        self.addTab(tab, tab_name)
121
122    def updateFitDict(self, item_key, tab_name):
123        """
124        Create a list if none exists and append if there's already a list
125        """
126        if item_key in self.dataToFitTab.keys():
127            self.dataToFitTab[item_key].append(tab_name)
128        else:
129            self.dataToFitTab[item_key] = [tab_name]
[f46f6dc]130
[38eb433]131        #print "CURRENT dict: ", self.dataToFitTab
132
133    def tabName(self, is_batch=False):
[f46f6dc]134        """
135        Get the new tab name, based on the number of fitting tabs so far
136        """
[38eb433]137        page_name = "BatchPage" if is_batch else "FitPage"
138        page_name = page_name + str(self.maxIndex)
[f46f6dc]139        return page_name
140
[38eb433]141    def resetTab(self, index):
142        """
143        Adds a new tab and removes the last tab
144        as a way of resetting the fit tabs
145        """
146        # If data on tab empty - do nothing
[377ade1]147        if index in self.tabs and not self.tabs[index].data:
[38eb433]148            return
149        # Add a new, empy tab
150        self.addFit(None)
151        # Remove the previous last tab
152        self.tabCloses(index)
153
[f46f6dc]154    def tabCloses(self, index):
155        """
156        Update local bookkeeping on tab close
157        """
[38eb433]158        #assert len(self.tabs) >= index
[f46f6dc]159        # don't remove the last tab
160        if len(self.tabs) <= 1:
[38eb433]161            self.resetTab(index)
[f46f6dc]162            return
[38eb433]163        try:
164            ObjectLibrary.deleteObjectByRef(self.tabs[index])
165            self.removeTab(index)
166            del self.tabs[index]
167        except IndexError:
168            # The tab might have already been deleted previously
169            pass
170
171    def closeTabByName(self, tab_name):
172        """
173        Given name of the fitting tab - close it
174        """
175        for tab_index in xrange(len(self.tabs)):
176            if self.tabText(tab_index) == tab_name:
177                self.tabCloses(tab_index)
178        pass # debug hook
179
180    def dataDeleted(self, index_list):
181        """
182        Delete fit tabs referencing given data
183        """
184        if not index_list or not self.dataToFitTab:
185            return
186        for index_to_delete in index_list:
187            if index_to_delete in self.dataToFitTab.keys():
188                for tab_name in self.dataToFitTab[index_to_delete]:
189                    # delete tab #index after corresponding data got removed
190                    self.closeTabByName(tab_name)
191                self.dataToFitTab.pop(index_to_delete)
192
193        #print "CURRENT dict: ", self.dataToFitTab
[f46f6dc]194
195    def allowBatch(self):
196        """
197        Tell the caller that we accept multiple data instances
198        """
199        return True
200
[ee18d33]201    def setData(self, data_item=None, is_batch=False):
[f46f6dc]202        """
203        Assign new dataset to the fitting instance
[5236449]204        Obtain a QStandardItem object and dissect it to get Data1D/2D
205        Pass it over to the calculator
[f46f6dc]206        """
[cbcdd2c]207        assert data_item is not None
[68c96d3]208
[5236449]209        if not isinstance(data_item, list):
210            msg = "Incorrect type passed to the Fitting Perspective"
211            raise AttributeError, msg
212
213        if not isinstance(data_item[0], QtGui.QStandardItem):
214            msg = "Incorrect type passed to the Fitting Perspective"
215            raise AttributeError, msg
216
[ee18d33]217        items = [data_item] if is_batch else data_item
218
219        for data in items:
[454670d]220            # Find the first unassigned tab.
221            # If none, open a new tab.
222            available_tabs = list(map(lambda tab: tab.acceptsData(), self.tabs))
223
224            if numpy.any(available_tabs):
[38eb433]225                first_good_tab = available_tabs.index(True)
226                self.tabs[first_good_tab].data = data
227                tab_name = str(self.tabText(first_good_tab))
228                self.updateFitDict(data, tab_name)
[454670d]229            else:
[ee18d33]230                self.addFit(data, is_batch=is_batch)
[2d0e0c1]231
[b0c5e8c]232    def onFittingOptionsChange(self, fit_engine):
[2d0e0c1]233        """
[b0c5e8c]234        React to the fitting algorithm change by modifying window title
[2d0e0c1]235        """
236        fitter = [f.id for f in options.FITTERS if f.name == str(fit_engine)][0]
237        # set the optimizer
238        self.fit_options.selected_id = str(fitter)
239        # Update the title
240        self.updateWindowTitle()
241
242        pass
Note: See TracBrowser for help on using the repository browser.