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

Last change on this file since c57ecca was 377ade1, checked in by Piotr Rozyczko <rozyczko@…>, 7 years ago

Fixing unit tests + removal of unnecessary files

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