source: sasview/fittingview/src/sans/perspectives/fitting/fitpanel.py @ 29a4024

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalccostrafo411magnetic_scattrelease-4.1.1release-4.1.2release-4.2.2release_4.0.1ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since 29a4024 was 05efe88, checked in by Gervaise Alina <gervyh@…>, 13 years ago

make sure empty page get the batch flag when adding data to them

  • Property mode set to 100644
File size: 20.9 KB
RevLine 
[e2f1023]1
2import numpy
3import string 
[d89f09b]4import wx
[f22e626]5import sys
[1b1bbf9]6#from wx.lib.flatnotebook import FlatNotebook as nb
7from wx.aui import AuiNotebook as nb
[a93f525]8
[340c2b3]9from sans.guiframe.panel_base import PanelBase
[3cd5806]10from sans.guiframe.events import PanelOnFocusEvent
[6bbeacd4]11from sans.guiframe.events import StatusEvent
[aefc09f]12from sans.guiframe.dataFitting import check_data_validity
[66ff250]13
[cfc0913]14import basepage
[6bbeacd4]15import models
[330573d]16_BOX_WIDTH = 80
17
18
[1b1bbf9]19class FitPanel(nb, PanelBase):   
[925a30e]20
[d89f09b]21    """
[5062bbf]22    FitPanel class contains fields allowing to fit  models and  data
23   
24    :note: For Fit to be performed the user should check at least one parameter
[d89f09b]25        on fit Panel window.
26       
27    """
28    ## Internal name for the AUI manager
29    window_name = "Fit panel"
30    ## Title to appear on top of the window
31    window_caption = "Fit Panel "
[2139c3f]32    CENTER_PANE = True
[7437880]33   
[6bbeacd4]34    def __init__(self, parent, manager=None, *args, **kwargs):
[5062bbf]35        """
36        """
[1b1bbf9]37        nb.__init__(self, parent, -1,
[3244cbe1]38                    style= wx.aui.AUI_NB_WINDOWLIST_BUTTON|
39                    wx.aui.AUI_NB_DEFAULT_STYLE|
40                    wx.CLIP_CHILDREN)
[3cd5806]41        PanelBase.__init__(self, parent)
[1b1bbf9]42        #self.SetWindowStyleFlag(style=nb.FNB_FANCY_TABS)
[6bbeacd4]43        self._manager = manager
[340c2b3]44        self.parent = parent
45        self.event_owner = None
[d89f09b]46        #dictionary of miodel {model class name, model class}
[6bbeacd4]47        self.menu_mng = models.ModelManager()
[b2d9826]48        self.model_list_box = self.menu_mng.get_model_list()
[1b1bbf9]49        #pageClosedEvent = nb.EVT_FLATNOTEBOOK_PAGE_CLOSING
[f22e626]50        self.pageClosedEvent = wx.aui.EVT_AUINOTEBOOK_PAGE_CLOSE
[1b1bbf9]51       
[f22e626]52        self.Bind(self.pageClosedEvent, self.on_close_page)
[6bbeacd4]53         ## save the title of the last page tab added
[2f189dc]54        self.fit_page_name = {}
[cfc0913]55        ## list of existing fit page
[2f189dc]56        self.opened_pages = {}
[aefc09f]57        #index of fit page
[cc31608]58        self.fit_page_index = 0
59        #index of batch page
60        self.batch_page_index = 0
[2f189dc]61        #page of simultaneous fit
[ffa69b6]62        self.sim_page = None
[66ff250]63        self.fit_engine_type = "scipy"
[cfc0913]64        ## get the state of a page
65        self.Bind(basepage.EVT_PAGE_INFO, self._onGetstate)
[330573d]66        self.Bind(basepage.EVT_PREVIOUS_STATE, self._onUndo)
[fe496eeb]67        self.Bind(basepage.EVT_NEXT_STATE, self._onRedo)
[90a7bbd]68        self.Bind(wx.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, self.on_page_changing)
[f22e626]69        self.Bind(wx.aui.EVT_AUINOTEBOOK_PAGE_CLOSED, self.on_closed)
[1b1bbf9]70
[f22e626]71    def on_closed(self, event):
72        """
73        """
[394ffa7]74        if self.GetPageCount() == 0:
75            self.add_empty_page()
76            self.enable_close_button()
[f22e626]77       
[4e78f56]78    def save_project(self, doc=None):
79        """
80        return an xml node containing state of the panel
81         that guiframe can write to file
82        """
83        for uid, page in self.opened_pages.iteritems():
84            data = page.get_data()
85            # state must be cloned
86            state = page.get_state().clone()
87            if data is not None:
88                new_doc = self._manager.state_reader.write_toXML(data, state)
89                if doc != None and hasattr(doc, "firstChild"):
90                    child = new_doc.firstChild.firstChild
91                    doc.firstChild.appendChild(child) 
92                else:
93                    doc = new_doc
94        return doc   
[6bbeacd4]95   
[66ff250]96    def _on_engine_change(self, name='scipy'):
97        """
98        """
99        for panel in self.opened_pages.values():
100            self.set_engine_helper(panel=panel, name=name)
101           
102    def set_engine_helper(self, panel, name='scipy'):
103        """
104        """
105        self.fit_engine_type = name
106        if panel != self.sim_page:
107            panel._on_engine_change(name=self.fit_engine_type)
108               
[b2d9826]109    def update_model_list(self):
110        """
111        """
[9466f2d6]112        temp = self.menu_mng.update()
113        if len(temp):
114            self.model_list_box = temp
115        return temp
[aefc09f]116
[66ff250]117    def get_page_by_id(self, uid): 
[6bbeacd4]118        """
119        """
[66ff250]120        if uid not in self.opened_pages:
121            msg = "Fitpanel cannot find ID: %s in self.opened_pages" % str(uid)
[6bbeacd4]122            raise ValueError, msg
123        else:
[66ff250]124            return self.opened_pages[uid]
[31b0c47]125       
[90a7bbd]126    def on_page_changing(self, event):
[6bbeacd4]127        """
[f22e626]128        calls the function when the current event handler has exited. avoiding
129        to call panel on focus on a panel that is currently deleted
130        """
131        wx.CallAfter(self.helper_on_page_change)
132       
133       
134    def helper_on_page_change(self):
135        """
[6bbeacd4]136        """
[90a7bbd]137        pos = self.GetSelection()
138        if pos != -1:
139            selected_page = self.GetPage(pos)
140            wx.PostEvent(self.parent, PanelOnFocusEvent(panel=selected_page))
[e4c9030]141        self.enable_close_button()
[f22e626]142       
[3cd5806]143    def on_set_focus(self, event):
144        """
145        """
146        pos = self.GetSelection()
[90a7bbd]147        if pos != -1:
148            selected_page = self.GetPage(pos)
149            wx.PostEvent(self.parent, PanelOnFocusEvent(panel=selected_page))
[3cd5806]150       
[c8deee5]151    def get_data(self):
152        """
153        get the data in the current page
154        """
155        pos = self.GetSelection()
[90a7bbd]156        if pos != -1:
157            selected_page = self.GetPage(pos)
158            return selected_page.get_data()
[c8deee5]159   
[e88ebfd]160    def set_model_state(state):
161        """
162        receive a state to reset the model in the current page
163        """
164        pos = self.GetSelection()
165        if pos != -1:
166            selected_page = self.GetPage(pos)
167            selected_page.set_model_state(state)
168           
[c8deee5]169    def get_state(self):
170        """
171         return the state of the current selected page
172        """
173        pos = self.GetSelection()
[90a7bbd]174        if pos != -1:
175            selected_page = self.GetPage(pos)
176            return selected_page.get_state()
[c8deee5]177   
[f22e626]178 
[6bbeacd4]179 
[ac2dc0e]180    def close_all(self):
181        """
[8897d66]182        remove all pages, used when a svs file is opened
183        """
[90a7bbd]184       
[8897d66]185        #get number of pages
186        nop = self.GetPageCount()
187        #use while-loop, for-loop will not do the job well.
188        while (nop>0):
189            #delete the first page until no page exists
190            page = self.GetPage(0)
[90a7bbd]191            if self._manager.parent.panel_on_focus == page:
192                self._manager.parent.panel_on_focus = None
[8897d66]193            self._close_helper(selected_page=page)
194            self.DeletePage(0)
195            nop = nop - 1
[90a7bbd]196           
[ac2dc0e]197        ## save the title of the last page tab added
198        self.fit_page_name = {}
199        ## list of existing fit page
200        self.opened_pages = {} 
201         
[05efe88]202       
[31b0c47]203    def set_state(self, state):
204        """
[5062bbf]205        Restore state of the panel
[31b0c47]206        """
207        page_is_opened = False
208        if state is not None:
[66ff250]209            for uid, panel in self.opened_pages.iteritems():
[31b0c47]210                #Don't return any panel is the exact same page is created
[4bee68d]211                if uid == panel.uid and panel.data == state.data:
[31b0c47]212                    # the page is still opened
213                    panel.reset_page(state=state)
[645f9b6]214                    panel.save_current_state() 
[31b0c47]215                    page_is_opened = True
216            if not page_is_opened:
[9f391af]217                panel = self._manager.add_fit_page(data=state.data)
[31b0c47]218                # add data associated to the page created
219                if panel is not None: 
[4bee68d]220                    self._manager.store_data(uid=panel.uid, 
221                                             data=state.data,
222                                             caption=panel.window_caption)
[31b0c47]223                    panel.reset_page(state=state)
[645f9b6]224                    panel.save_current_state()
[31b0c47]225                   
[90a7bbd]226    def clear_panel(self):
[9b18735]227        """
228        Clear and close all panels, used by guimanager
229        """
[90a7bbd]230       
231        #close all panels only when svs file opened
232        self.close_all()
233        self._manager.mypanels = []
234       
[9b18735]235                       
[ac2dc0e]236    def on_close_page(self, event=None):
[51d47b5]237        """
[5062bbf]238        close page and remove all references to the closed page
[51d47b5]239        """
[d361b462]240        nbr_page = self.GetPageCount()
[51d47b5]241        selected_page = self.GetPage(self.GetSelection())
[f22e626]242        if nbr_page == 1:
243            if selected_page.get_data() == None:
244                if event is not None:
245                    event.Veto()
246                return 
[2f189dc]247        self._close_helper(selected_page=selected_page)
[9853ad0]248       
[2f189dc]249    def close_page_with_data(self, deleted_data):
250        """
[5062bbf]251        close a fit page when its data is completely remove from the graph
[2f189dc]252        """
[784e2fa]253        if deleted_data is None:
254            return
[2f189dc]255        for index in range(self.GetPageCount()):
256            selected_page = self.GetPage(index) 
257            if hasattr(selected_page,"get_data"):
258                data = selected_page.get_data()
[ac2dc0e]259               
[784e2fa]260                if data is None:
261                    #the fitpanel exists and only the initial fit page is open
262                    #with no selected data
263                    return
[f22e626]264                if data.id == deleted_data.id:
[2f189dc]265                    self._close_helper(selected_page)
266                    self.DeletePage(index)
267                    break
[d89f09b]268       
269    def set_manager(self, manager):
270        """
[5062bbf]271        set panel manager
272       
273        :param manager: instance of plugin fitting
274       
[d89f09b]275        """
[c8deee5]276        self._manager = manager
[3cd5806]277        for pos in range(self.GetPageCount()):
278            page = self.GetPage(pos)
279            if page is not None:
280                page.set_manager(self._manager)
[a93f525]281
[ffa69b6]282    def set_model_list(self, dict):
[c77d859]283         """
[5062bbf]284         copy a dictionary of model into its own dictionary
285         
286         :param dict: dictionnary made of model name as key and model class
[c77d859]287             as value
288         """
289         self.model_list_box = dict
[51d47b5]290       
[c77d859]291    def get_current_page(self):
292        """
[5062bbf]293        :return: the current page selected
294       
[c77d859]295        """
[6bbeacd4]296        return self.GetPage(self.GetSelection())
[c77d859]297   
[51d47b5]298    def add_sim_page(self):
[925a30e]299        """
[5062bbf]300        Add the simultaneous fit page
[925a30e]301        """
[51d47b5]302        from simfitpage import SimultaneousFitPage
[c8deee5]303        page_finder= self._manager.get_page_finder()
[b28717b]304        self.sim_page = SimultaneousFitPage(self,page_finder=page_finder, id=-1)
[66ff250]305        self.sim_page.uid = wx.NewId()
[6bbeacd4]306        self.AddPage(self.sim_page,"Simultaneous Fit", True)
[c8deee5]307        self.sim_page.set_manager(self._manager)
[f22e626]308        self.enable_close_button()
[51d47b5]309        return self.sim_page
[d89f09b]310       
[6bbeacd4]311 
[2f189dc]312    def add_empty_page(self):
[9853ad0]313        """
[5062bbf]314        add an empty page
[9853ad0]315        """
[3f36675]316        """
[cc31608]317        if self.batch_on:
318            from batchfitpage import BatchFitPage
319            panel = BatchFitPage(parent=self)
320            #Increment index of batch page
321            self.batch_page_index += 1
322            index = self.batch_page_index
323        else:
[3f36675]324        """
325        from fitpage import FitPage
326        panel = FitPage(parent=self)
[aefc09f]327        if self.batch_on:
328            self.batch_page_index += 1
[55bb249c]329            caption = "BatchPage" + str(self.batch_page_index)
[aefc09f]330            panel.set_index_model(self.batch_page_index)
331        else:
332            #Increment index of fit page
333            self.fit_page_index += 1
[55bb249c]334            caption = "FitPage" + str(self.fit_page_index)
[aefc09f]335            panel.set_index_model(self.fit_page_index)
[22ce7e2]336        panel.batch_on = self.batch_on
[6bbeacd4]337        panel.populate_box(dict=self.model_list_box)
[c8deee5]338        panel.set_manager(self._manager)
[aefc09f]339        panel.window_caption = caption
340        panel.window_name = caption
[ae4ade7]341        self.AddPage(panel, caption, select=True)
[66ff250]342        self.opened_pages[panel.uid] = panel
343        self.set_engine_helper(panel=panel)
[aefc09f]344        self._manager.create_fit_problem(panel.uid)
345        self._manager.page_finder[panel.uid].add_data(panel.get_data())
[f22e626]346        self.enable_close_button()
[2f189dc]347        return panel
348   
[f22e626]349    def enable_close_button(self):
350        """
351        display the close button on tab for more than 1 tabs else remove the
352        close button
353        """
354        if self.GetPageCount() <= 1:
355            style = self.GetWindowStyleFlag() 
[3f36675]356            flag = wx.aui.AUI_NB_CLOSE_ON_ACTIVE_TAB
357            if style & wx.aui.AUI_NB_CLOSE_ON_ACTIVE_TAB == flag:
[f22e626]358                style = style & ~wx.aui.AUI_NB_CLOSE_ON_ACTIVE_TAB
359                self.SetWindowStyle(style)
360        else:
361            style = self.GetWindowStyleFlag()
[3f36675]362            flag = wx.aui.AUI_NB_CLOSE_ON_ACTIVE_TAB
363            if style & wx.aui.AUI_NB_CLOSE_ON_ACTIVE_TAB != flag:
[f22e626]364                style |= wx.aui.AUI_NB_CLOSE_ON_ACTIVE_TAB
365                self.SetWindowStyle(style)
366           
[8ee56a9]367    def delete_data(self, data):
368        """
369        Delete the given data
370        """
[f22e626]371        if data.__class__.__name__ != "list":
372            raise ValueError, "Fitpanel delete_data expect list of id"
373        else:
374            n = self.GetPageCount()
375            for page in self.opened_pages.values():
376                pos = self.GetPageIndex(page)
377                temp_data = page.get_data()
378                #stop the fitting before deleting the page
379                page.is_fitting()
380                if temp_data is not None and temp_data.id in data:
381                    self.SetSelection(pos)
382                    self.on_close_page(event=None)
383                    temp = self.GetSelection()
384                    self.DeletePage(temp)
385            if self.GetPageCount()== 0:
[e4c9030]386                self._manager.on_add_new_page(event=None)
[f22e626]387       
[3f36675]388    def set_data_on_batch_mode(self, data_list):
389        """
390        Add all data to a single tab when the application is on Batch mode.
391        However all data in the set of data must be either 1D or 2D type.
392        This method presents option to select the data type before creating a
393        tab.
394        """
395        data_1d_list = []
396        data_2d_list = []
[2e643a8]397        group_id_1d = wx.NewId()
[4225aed]398        # separate data into data1d and data2d list
[3f36675]399        for data in data_list:
400            if data.__class__.__name__ == "Data1D":
[2e643a8]401                data.group_id = group_id_1d
[3f36675]402                data_1d_list.append(data)
403            if data.__class__.__name__ == "Data2D":
[017e0390]404                data.group_id = wx.NewId()
[3f36675]405                data_2d_list.append(data)
406        page = None
407        for p in self.opened_pages.values():
[aefc09f]408            #check if there is an empty page to fill up
409            if not check_data_validity(p.get_data()):
[3f36675]410                page = p
[aefc09f]411                self.batch_page_index += 1
[3f36675]412                break
413        if data_1d_list and data_2d_list:
414            # need to warning the user that this batch is a special case
415            from .fitting_widgets import BatchDataDialog
416            dlg = BatchDataDialog(self)
417            if dlg.ShowModal() == wx.ID_OK:
418                data_type = dlg.get_data()
419                dlg.Destroy()
420                if page  is None:
421                    page = self.add_empty_page()
422                if data_type == 1:
423                    #user has selected only data1D
424                    page.fill_data_combobox(data_1d_list)
425                elif data_type == 2:
426                    page.fill_data_combobox(data_2d_list)
427            else:
428                #the batch analysis is canceled
429                 dlg.Destroy()
430                 return None
431        else:
432            if page is None:
433                page = self.add_empty_page()
434            if data_1d_list and not data_2d_list:
435                #only on type of data
436                page.fill_data_combobox(data_1d_list)
437            elif not data_1d_list and data_2d_list:
438                page.fill_data_combobox(data_2d_list)
[aefc09f]439        pos = self.GetPageIndex(page)
440        caption = "BatchPage" + str(self.batch_page_index)
441        self.SetPageText(pos, caption)
[05efe88]442        page.batch_on = self.batch_on
[aefc09f]443        page.window_caption = caption
444        page.window_name = caption
[3f36675]445        self.opened_pages[page.uid] = page
446        return page
447   
[cc31608]448    def set_data(self, data_list):
[2f189dc]449        """
[5062bbf]450        Add a fitting page on the notebook contained by fitpanel
451       
452        :param data: data to fit
453       
[3f36675]454        :return panel : page just added for further used.
455        is used by fitting module
[5062bbf]456       
[2f189dc]457        """
[cc31608]458        if not data_list:
[2f189dc]459            return None
[cc31608]460        if self.batch_on:
[3f36675]461           return self.set_data_on_batch_mode(data_list)
[cc31608]462        else:
463            data = None
[7f76f89]464            try:
465                data = data_list[0]
466            except:
467                # for 'fitv' files
468                data_list = [data]
469                data = data_list[0]
470               
[cc31608]471            if data is None:
472                return None
[6bbeacd4]473        for page in self.opened_pages.values():
474            #check if the selected data existing in the fitpanel
475            pos = self.GetPageIndex(page)
[aefc09f]476            if not check_data_validity(page.get_data()):
[cc31608]477                #make sure data get placed in 1D empty tab if data is 1D
478                #else data get place on 2D tab empty tab
[a5701e6]479                enable2D = page.get_view_mode()
480                if (data.__class__.__name__ == "Data2D" and enable2D)\
481                or (data.__class__.__name__ == "Data1D" and not enable2D):
[05efe88]482                    page.batch_on = self.batch_on
[cc31608]483                    page.fill_data_combobox(data_list)
[05efe88]484                    caption = "FitPage" + str(self.fit_page_index)
485                    self.SetPageText(pos, caption)
[cc31608]486                    self.SetSelection(pos)
487                    return page
[aefc09f]488        #create new page and add data
[6bbeacd4]489        page = self.add_empty_page()
[9f391af]490        pos = self.GetPageIndex(page)
[cc31608]491        page.fill_data_combobox(data_list)
[66ff250]492        self.opened_pages[page.uid] = page
[09ef5a60]493        self.SetSelection(pos)
[6bbeacd4]494        return page
495       
496    def _onGetstate(self, event):
[cfc0913]497        """
[5062bbf]498        copy the state of a page
[cfc0913]499        """
[6bbeacd4]500        page = event.page
[66ff250]501        if page.uid in self.fit_page_name:
502           self.fit_page_name[page.uid].appendItem(page.createMemento()) 
[cfc0913]503           
[4e78f56]504    def _onUndo(self, event):
[330573d]505        """
[5062bbf]506        return the previous state of a given page is available
[330573d]507        """
508        page = event.page
[66ff250]509        if page.uid in self.fit_page_name:
510            if self.fit_page_name[page.uid].getCurrentPosition()==0:
[330573d]511                state = None
512            else:
[66ff250]513                state = self.fit_page_name[page.uid].getPreviousItem()
[fe496eeb]514                page._redo.Enable(True)
[330573d]515            page.reset_page(state)
[fe496eeb]516       
[2f189dc]517    def _onRedo(self, event): 
[fe496eeb]518        """
[5062bbf]519        return the next state available
[fe496eeb]520        """       
521        page = event.page
[66ff250]522        if page.uid in self.fit_page_name:
523            length= len(self.fit_page_name[page.uid])
524            if self.fit_page_name[page.uid].getCurrentPosition()== length -1:
[fe496eeb]525                state = None
526                page._redo.Enable(False)
[3b9e023]527                page._redo.Enable(True)
[fe496eeb]528            else:
[66ff250]529                state =self.fit_page_name[page.uid].getNextItem()
[fe496eeb]530            page.reset_page(state) 
[2f189dc]531                 
532    def _close_helper(self, selected_page):
[3f1af74]533        """
[5062bbf]534        Delete the given page from the notebook
[3f1af74]535        """
[2f189dc]536        #remove hint page
[1b1bbf9]537        #if selected_page == self.hint_page:
538        #    return
[2f189dc]539        ## removing sim_page
540        if selected_page == self.sim_page :
[c8deee5]541            self._manager.sim_page=None 
[2f189dc]542            return
[5e48acb]543        """
544        # The below is not working when delete #5 and still have #6.
[cc31608]545        if selected_page.__class__.__name__ == "FitPage":
546            self.fit_page_index -= 1
547        else:
548            self.batch_page_index -= 1
[5e48acb]549        """
[2f189dc]550        ## closing other pages
551        state = selected_page.createMemento()
552        page_name = selected_page.window_name
[c8deee5]553        page_finder = self._manager.get_page_finder() 
[2f189dc]554        fitproblem = None
555        ## removing fit page
[c6036f5]556        data = selected_page.get_data()
557        #Don' t remove plot for 2D
558        flag = True
559        if data.__class__.__name__ == 'Data2D':
560            flag = False
[2f189dc]561        if selected_page in page_finder:
562            #Delete the name of the page into the list of open page
[66ff250]563            for uid, list in self.opened_pages.iteritems():
[2f189dc]564                #Don't return any panel is the exact same page is created
[6bbeacd4]565               
[66ff250]566                if flag and selected_page.uid == uid:
567                    self._manager.remove_plot(uid, theory=False)
[2f189dc]568                    break 
569            del page_finder[selected_page]
570        ##remove the check box link to the model name of this page (selected_page)
571        try:
572            self.sim_page.draw_page()
573        except:
574            ## that page is already deleted no need to remove check box on
575            ##non existing page
576            pass
577               
578        #Delete the name of the page into the list of open page
[66ff250]579        for uid, list in self.opened_pages.iteritems():
[2f189dc]580            #Don't return any panel is the exact same page is created
[66ff250]581            if selected_page.uid == uid:
582                del self.opened_pages[selected_page.uid]
[2f189dc]583                break 
[5e48acb]584     
Note: See TracBrowser for help on using the repository browser.