source: sasview/sansview/perspectives/fitting/fitpanel.py @ 9ade6ad

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 9ade6ad was 00ea2a7, checked in by Jae Cho <jhjcho@…>, 13 years ago

allows set multiple model with one data set: caution may produce a bug

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