source: sasview/fittingview/src/sans/perspectives/fitting/fitpanel.py @ 6446f87

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 6446f87 was 7f76f89, checked in by Jae Cho <jhjcho@…>, 13 years ago

fixed loading fitv files

  • Property mode set to 100644
File size: 20.6 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         
[31b0c47]202    def set_state(self, state):
203        """
[5062bbf]204        Restore state of the panel
[31b0c47]205        """
206        page_is_opened = False
207        if state is not None:
[66ff250]208            for uid, panel in self.opened_pages.iteritems():
[31b0c47]209                #Don't return any panel is the exact same page is created
[4bee68d]210                if uid == panel.uid and panel.data == state.data:
[31b0c47]211                    # the page is still opened
212                    panel.reset_page(state=state)
[645f9b6]213                    panel.save_current_state() 
[31b0c47]214                    page_is_opened = True
215            if not page_is_opened:
[9f391af]216                panel = self._manager.add_fit_page(data=state.data)
[31b0c47]217                # add data associated to the page created
218                if panel is not None: 
[4bee68d]219                    self._manager.store_data(uid=panel.uid, 
220                                             data=state.data,
221                                             caption=panel.window_caption)
[31b0c47]222                    panel.reset_page(state=state)
[645f9b6]223                    panel.save_current_state()
[31b0c47]224                   
[90a7bbd]225    def clear_panel(self):
[9b18735]226        """
227        Clear and close all panels, used by guimanager
228        """
[90a7bbd]229       
230        #close all panels only when svs file opened
231        self.close_all()
232        self._manager.mypanels = []
233       
[9b18735]234                       
[ac2dc0e]235    def on_close_page(self, event=None):
[51d47b5]236        """
[5062bbf]237        close page and remove all references to the closed page
[51d47b5]238        """
[d361b462]239        nbr_page = self.GetPageCount()
[51d47b5]240        selected_page = self.GetPage(self.GetSelection())
[f22e626]241        if nbr_page == 1:
242            if selected_page.get_data() == None:
243                if event is not None:
244                    event.Veto()
245                return 
[2f189dc]246        self._close_helper(selected_page=selected_page)
[9853ad0]247       
[2f189dc]248    def close_page_with_data(self, deleted_data):
249        """
[5062bbf]250        close a fit page when its data is completely remove from the graph
[2f189dc]251        """
[784e2fa]252        if deleted_data is None:
253            return
[2f189dc]254        for index in range(self.GetPageCount()):
255            selected_page = self.GetPage(index) 
256            if hasattr(selected_page,"get_data"):
257                data = selected_page.get_data()
[ac2dc0e]258               
[784e2fa]259                if data is None:
260                    #the fitpanel exists and only the initial fit page is open
261                    #with no selected data
262                    return
[f22e626]263                if data.id == deleted_data.id:
[2f189dc]264                    self._close_helper(selected_page)
265                    self.DeletePage(index)
266                    break
[d89f09b]267       
268    def set_manager(self, manager):
269        """
[5062bbf]270        set panel manager
271       
272        :param manager: instance of plugin fitting
273       
[d89f09b]274        """
[c8deee5]275        self._manager = manager
[3cd5806]276        for pos in range(self.GetPageCount()):
277            page = self.GetPage(pos)
278            if page is not None:
279                page.set_manager(self._manager)
[a93f525]280
[ffa69b6]281    def set_model_list(self, dict):
[c77d859]282         """
[5062bbf]283         copy a dictionary of model into its own dictionary
284         
285         :param dict: dictionnary made of model name as key and model class
[c77d859]286             as value
287         """
288         self.model_list_box = dict
[51d47b5]289       
[c77d859]290    def get_current_page(self):
291        """
[5062bbf]292        :return: the current page selected
293       
[c77d859]294        """
[6bbeacd4]295        return self.GetPage(self.GetSelection())
[c77d859]296   
[51d47b5]297    def add_sim_page(self):
[925a30e]298        """
[5062bbf]299        Add the simultaneous fit page
[925a30e]300        """
[51d47b5]301        from simfitpage import SimultaneousFitPage
[c8deee5]302        page_finder= self._manager.get_page_finder()
[b28717b]303        self.sim_page = SimultaneousFitPage(self,page_finder=page_finder, id=-1)
[66ff250]304        self.sim_page.uid = wx.NewId()
[6bbeacd4]305        self.AddPage(self.sim_page,"Simultaneous Fit", True)
[c8deee5]306        self.sim_page.set_manager(self._manager)
[f22e626]307        self.enable_close_button()
[51d47b5]308        return self.sim_page
[d89f09b]309       
[6bbeacd4]310 
[2f189dc]311    def add_empty_page(self):
[9853ad0]312        """
[5062bbf]313        add an empty page
[9853ad0]314        """
[3f36675]315        """
[cc31608]316        if self.batch_on:
317            from batchfitpage import BatchFitPage
318            panel = BatchFitPage(parent=self)
319            #Increment index of batch page
320            self.batch_page_index += 1
321            index = self.batch_page_index
322        else:
[3f36675]323        """
324        from fitpage import FitPage
325        panel = FitPage(parent=self)
[aefc09f]326        if self.batch_on:
327            self.batch_page_index += 1
[55bb249c]328            caption = "BatchPage" + str(self.batch_page_index)
[aefc09f]329            panel.set_index_model(self.batch_page_index)
330        else:
331            #Increment index of fit page
332            self.fit_page_index += 1
[55bb249c]333            caption = "FitPage" + str(self.fit_page_index)
[aefc09f]334            panel.set_index_model(self.fit_page_index)
[6bbeacd4]335        panel.populate_box(dict=self.model_list_box)
[c8deee5]336        panel.set_manager(self._manager)
[aefc09f]337        panel.window_caption = caption
338        panel.window_name = caption
[ae4ade7]339        self.AddPage(panel, caption, select=True)
[66ff250]340        self.opened_pages[panel.uid] = panel
341        self.set_engine_helper(panel=panel)
[aefc09f]342        self._manager.create_fit_problem(panel.uid)
343        self._manager.page_finder[panel.uid].add_data(panel.get_data())
[f22e626]344        self.enable_close_button()
[2f189dc]345        return panel
346   
[f22e626]347    def enable_close_button(self):
348        """
349        display the close button on tab for more than 1 tabs else remove the
350        close button
351        """
352        if self.GetPageCount() <= 1:
353            style = self.GetWindowStyleFlag() 
[3f36675]354            flag = wx.aui.AUI_NB_CLOSE_ON_ACTIVE_TAB
355            if style & wx.aui.AUI_NB_CLOSE_ON_ACTIVE_TAB == flag:
[f22e626]356                style = style & ~wx.aui.AUI_NB_CLOSE_ON_ACTIVE_TAB
357                self.SetWindowStyle(style)
358        else:
359            style = self.GetWindowStyleFlag()
[3f36675]360            flag = wx.aui.AUI_NB_CLOSE_ON_ACTIVE_TAB
361            if style & wx.aui.AUI_NB_CLOSE_ON_ACTIVE_TAB != flag:
[f22e626]362                style |= wx.aui.AUI_NB_CLOSE_ON_ACTIVE_TAB
363                self.SetWindowStyle(style)
364           
[8ee56a9]365    def delete_data(self, data):
366        """
367        Delete the given data
368        """
[f22e626]369        if data.__class__.__name__ != "list":
370            raise ValueError, "Fitpanel delete_data expect list of id"
371        else:
372            n = self.GetPageCount()
373            for page in self.opened_pages.values():
374                pos = self.GetPageIndex(page)
375                temp_data = page.get_data()
376                #stop the fitting before deleting the page
377                page.is_fitting()
378                if temp_data is not None and temp_data.id in data:
379                    self.SetSelection(pos)
380                    self.on_close_page(event=None)
381                    temp = self.GetSelection()
382                    self.DeletePage(temp)
383            if self.GetPageCount()== 0:
[e4c9030]384                self._manager.on_add_new_page(event=None)
[f22e626]385       
[3f36675]386    def set_data_on_batch_mode(self, data_list):
387        """
388        Add all data to a single tab when the application is on Batch mode.
389        However all data in the set of data must be either 1D or 2D type.
390        This method presents option to select the data type before creating a
391        tab.
392        """
393        data_1d_list = []
394        data_2d_list = []
[2e643a8]395        group_id_1d = wx.NewId()
[4225aed]396        # separate data into data1d and data2d list
[3f36675]397        for data in data_list:
398            if data.__class__.__name__ == "Data1D":
[2e643a8]399                data.group_id = group_id_1d
[3f36675]400                data_1d_list.append(data)
401            if data.__class__.__name__ == "Data2D":
[017e0390]402                data.group_id = wx.NewId()
[3f36675]403                data_2d_list.append(data)
404        page = None
405        for p in self.opened_pages.values():
[aefc09f]406            #check if there is an empty page to fill up
407            if not check_data_validity(p.get_data()):
[3f36675]408                page = p
[aefc09f]409                self.batch_page_index += 1
[3f36675]410                break
411        if data_1d_list and data_2d_list:
412            # need to warning the user that this batch is a special case
413            from .fitting_widgets import BatchDataDialog
414            dlg = BatchDataDialog(self)
415            if dlg.ShowModal() == wx.ID_OK:
416                data_type = dlg.get_data()
417                dlg.Destroy()
418                if page  is None:
419                    page = self.add_empty_page()
420                if data_type == 1:
421                    #user has selected only data1D
422                    page.fill_data_combobox(data_1d_list)
423                elif data_type == 2:
424                    page.fill_data_combobox(data_2d_list)
425            else:
426                #the batch analysis is canceled
427                 dlg.Destroy()
428                 return None
429        else:
430            if page is None:
431                page = self.add_empty_page()
432            if data_1d_list and not data_2d_list:
433                #only on type of data
434                page.fill_data_combobox(data_1d_list)
435            elif not data_1d_list and data_2d_list:
436                page.fill_data_combobox(data_2d_list)
[aefc09f]437        pos = self.GetPageIndex(page)
438        caption = "BatchPage" + str(self.batch_page_index)
439        self.SetPageText(pos, caption)
440        page.window_caption = caption
441        page.window_name = caption
[3f36675]442        self.opened_pages[page.uid] = page
443        return page
444   
[cc31608]445    def set_data(self, data_list):
[2f189dc]446        """
[5062bbf]447        Add a fitting page on the notebook contained by fitpanel
448       
449        :param data: data to fit
450       
[3f36675]451        :return panel : page just added for further used.
452        is used by fitting module
[5062bbf]453       
[2f189dc]454        """
[cc31608]455        if not data_list:
[2f189dc]456            return None
[cc31608]457        if self.batch_on:
[3f36675]458           return self.set_data_on_batch_mode(data_list)
[cc31608]459        else:
460            data = None
[7f76f89]461            try:
462                data = data_list[0]
463            except:
464                # for 'fitv' files
465                data_list = [data]
466                data = data_list[0]
467               
[cc31608]468            if data is None:
469                return None
[6bbeacd4]470        for page in self.opened_pages.values():
471            #check if the selected data existing in the fitpanel
472            pos = self.GetPageIndex(page)
[aefc09f]473            if not check_data_validity(page.get_data()):
[cc31608]474                #make sure data get placed in 1D empty tab if data is 1D
475                #else data get place on 2D tab empty tab
[a5701e6]476                enable2D = page.get_view_mode()
477                if (data.__class__.__name__ == "Data2D" and enable2D)\
478                or (data.__class__.__name__ == "Data1D" and not enable2D):
[cc31608]479                    page.fill_data_combobox(data_list)
480                    self.SetSelection(pos)
481                    return page
[aefc09f]482        #create new page and add data
[6bbeacd4]483        page = self.add_empty_page()
[9f391af]484        pos = self.GetPageIndex(page)
[cc31608]485        page.fill_data_combobox(data_list)
[66ff250]486        self.opened_pages[page.uid] = page
[09ef5a60]487        self.SetSelection(pos)
[6bbeacd4]488        return page
489       
490    def _onGetstate(self, event):
[cfc0913]491        """
[5062bbf]492        copy the state of a page
[cfc0913]493        """
[6bbeacd4]494        page = event.page
[66ff250]495        if page.uid in self.fit_page_name:
496           self.fit_page_name[page.uid].appendItem(page.createMemento()) 
[cfc0913]497           
[4e78f56]498    def _onUndo(self, event):
[330573d]499        """
[5062bbf]500        return the previous state of a given page is available
[330573d]501        """
502        page = event.page
[66ff250]503        if page.uid in self.fit_page_name:
504            if self.fit_page_name[page.uid].getCurrentPosition()==0:
[330573d]505                state = None
506            else:
[66ff250]507                state = self.fit_page_name[page.uid].getPreviousItem()
[fe496eeb]508                page._redo.Enable(True)
[330573d]509            page.reset_page(state)
[fe496eeb]510       
[2f189dc]511    def _onRedo(self, event): 
[fe496eeb]512        """
[5062bbf]513        return the next state available
[fe496eeb]514        """       
515        page = event.page
[66ff250]516        if page.uid in self.fit_page_name:
517            length= len(self.fit_page_name[page.uid])
518            if self.fit_page_name[page.uid].getCurrentPosition()== length -1:
[fe496eeb]519                state = None
520                page._redo.Enable(False)
[3b9e023]521                page._redo.Enable(True)
[fe496eeb]522            else:
[66ff250]523                state =self.fit_page_name[page.uid].getNextItem()
[fe496eeb]524            page.reset_page(state) 
[2f189dc]525                 
526    def _close_helper(self, selected_page):
[3f1af74]527        """
[5062bbf]528        Delete the given page from the notebook
[3f1af74]529        """
[2f189dc]530        #remove hint page
[1b1bbf9]531        #if selected_page == self.hint_page:
532        #    return
[2f189dc]533        ## removing sim_page
534        if selected_page == self.sim_page :
[c8deee5]535            self._manager.sim_page=None 
[2f189dc]536            return
[cc31608]537        if selected_page.__class__.__name__ == "FitPage":
538            self.fit_page_index -= 1
539        else:
540            self.batch_page_index -= 1
[2f189dc]541        ## closing other pages
542        state = selected_page.createMemento()
543        page_name = selected_page.window_name
[c8deee5]544        page_finder = self._manager.get_page_finder() 
[2f189dc]545        fitproblem = None
546        ## removing fit page
[c6036f5]547        data = selected_page.get_data()
548        #Don' t remove plot for 2D
549        flag = True
550        if data.__class__.__name__ == 'Data2D':
551            flag = False
[2f189dc]552        if selected_page in page_finder:
553            #Delete the name of the page into the list of open page
[66ff250]554            for uid, list in self.opened_pages.iteritems():
[2f189dc]555                #Don't return any panel is the exact same page is created
[6bbeacd4]556               
[66ff250]557                if flag and selected_page.uid == uid:
558                    self._manager.remove_plot(uid, theory=False)
[2f189dc]559                    break 
560            del page_finder[selected_page]
561        ##remove the check box link to the model name of this page (selected_page)
562        try:
563            self.sim_page.draw_page()
564        except:
565            ## that page is already deleted no need to remove check box on
566            ##non existing page
567            pass
568               
569        #Delete the name of the page into the list of open page
[66ff250]570        for uid, list in self.opened_pages.iteritems():
[2f189dc]571            #Don't return any panel is the exact same page is created
[66ff250]572            if selected_page.uid == uid:
573                del self.opened_pages[selected_page.uid]
[2f189dc]574                break 
[f22e626]575     
[b787e68c]576 
Note: See TracBrowser for help on using the repository browser.