source: sasview/fittingview/src/sans/perspectives/fitting/fitpanel.py @ 09b3505

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

corrected misspell: broke simul_fit

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