source: sasview/fittingview/src/sans/perspectives/fitting/fitpanel.py @ 9a10c12

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

small fix for loading a state file

  • Property mode set to 100644
File size: 21.2 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.fit_engine_type = "scipy"
64        ## get the state of a page
65        self.Bind(basepage.EVT_PAGE_INFO, self._onGetstate)
66        self.Bind(basepage.EVT_PREVIOUS_STATE, self._onUndo)
67        self.Bind(basepage.EVT_NEXT_STATE, self._onRedo)
68        self.Bind(wx.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, self.on_page_changing)
69        self.Bind(wx.aui.EVT_AUINOTEBOOK_PAGE_CLOSED, self.on_closed)
70
71    def on_closed(self, event):
72        """
73        """
74        if self.GetPageCount() == 0:
75            self.add_empty_page()
76            self.enable_close_button()
77       
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   
95   
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               
109    def update_model_list(self):
110        """
111        """
112        temp = self.menu_mng.update()
113        if len(temp):
114            self.model_list_box = temp
115        return temp
116
117    def get_page_by_id(self, uid): 
118        """
119        """
120        if uid not in self.opened_pages:
121            msg = "Fitpanel cannot find ID: %s in self.opened_pages" % str(uid)
122            raise ValueError, msg
123        else:
124            return self.opened_pages[uid]
125       
126    def on_page_changing(self, event):
127        """
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        """
136        """
137        pos = self.GetSelection()
138        if pos != -1:
139            selected_page = self.GetPage(pos)
140            wx.PostEvent(self.parent, PanelOnFocusEvent(panel=selected_page))
141        self.enable_close_button()
142       
143    def on_set_focus(self, event):
144        """
145        """
146        pos = self.GetSelection()
147        if pos != -1:
148            selected_page = self.GetPage(pos)
149            wx.PostEvent(self.parent, PanelOnFocusEvent(panel=selected_page))
150       
151    def get_data(self):
152        """
153        get the data in the current page
154        """
155        pos = self.GetSelection()
156        if pos != -1:
157            selected_page = self.GetPage(pos)
158            return selected_page.get_data()
159   
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           
169    def get_state(self):
170        """
171         return the state of the current selected page
172        """
173        pos = self.GetSelection()
174        if pos != -1:
175            selected_page = self.GetPage(pos)
176            return selected_page.get_state()
177   
178 
179 
180    def close_all(self):
181        """
182        remove all pages, used when a svs file is opened
183        """
184       
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)
191            if self._manager.parent.panel_on_focus == page:
192                self._manager.parent.panel_on_focus = None
193            self._close_helper(selected_page=page)
194            self.DeletePage(0)
195            nop = nop - 1
196           
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         
202       
203    def set_state(self, state):
204        """
205        Restore state of the panel
206        """
207        page_is_opened = False
208        if state is not None:
209            for uid, panel in self.opened_pages.iteritems():
210                #Don't return any panel is the exact same page is created
211                if uid == panel.uid and panel.data == state.data:
212                    # the page is still opened
213                    panel.reset_page(state=state)
214                    panel.save_current_state() 
215                    page_is_opened = True
216            if not page_is_opened:
217                if state.data.__class__.__name__ != 'list':
218                    #To support older state file format
219                    list_data = [state.data]
220                else:
221                    #Todo: need new file format for the list
222                    list_data = state.data
223                panel = self._manager.add_fit_page(data=list_data)
224                # add data associated to the page created
225                if panel is not None: 
226                    self._manager.store_data(uid=panel.uid, 
227                                             data_list=list_data,
228                                             caption=panel.window_caption)
229                    panel.reset_page(state=state)
230                    panel.save_current_state()
231                   
232    def clear_panel(self):
233        """
234        Clear and close all panels, used by guimanager
235        """
236       
237        #close all panels only when svs file opened
238        self.close_all()
239        self._manager.mypanels = []
240       
241                       
242    def on_close_page(self, event=None):
243        """
244        close page and remove all references to the closed page
245        """
246        nbr_page = self.GetPageCount()
247        selected_page = self.GetPage(self.GetSelection())
248        if nbr_page == 1:
249            if selected_page.get_data() == None:
250                if event is not None:
251                    event.Veto()
252                return 
253        self._close_helper(selected_page=selected_page)
254       
255    def close_page_with_data(self, deleted_data):
256        """
257        close a fit page when its data is completely remove from the graph
258        """
259        if deleted_data is None:
260            return
261        for index in range(self.GetPageCount()):
262            selected_page = self.GetPage(index) 
263            if hasattr(selected_page,"get_data"):
264                data = selected_page.get_data()
265               
266                if data is None:
267                    #the fitpanel exists and only the initial fit page is open
268                    #with no selected data
269                    return
270                if data.id == deleted_data.id:
271                    self._close_helper(selected_page)
272                    self.DeletePage(index)
273                    break
274       
275    def set_manager(self, manager):
276        """
277        set panel manager
278       
279        :param manager: instance of plugin fitting
280       
281        """
282        self._manager = manager
283        for pos in range(self.GetPageCount()):
284            page = self.GetPage(pos)
285            if page is not None:
286                page.set_manager(self._manager)
287
288    def set_model_list(self, dict):
289         """
290         copy a dictionary of model into its own dictionary
291         
292         :param dict: dictionnary made of model name as key and model class
293             as value
294         """
295         self.model_list_box = dict
296       
297    def get_current_page(self):
298        """
299        :return: the current page selected
300       
301        """
302        return self.GetPage(self.GetSelection())
303   
304    def add_sim_page(self):
305        """
306        Add the simultaneous fit page
307        """
308        from simfitpage import SimultaneousFitPage
309        page_finder= self._manager.get_page_finder()
310        self.sim_page = SimultaneousFitPage(self,page_finder=page_finder, id=-1)
311        self.sim_page.uid = wx.NewId()
312        self.AddPage(self.sim_page,"Simultaneous Fit", True)
313        self.sim_page.set_manager(self._manager)
314        self.enable_close_button()
315        return self.sim_page
316       
317 
318    def add_empty_page(self):
319        """
320        add an empty page
321        """
322        """
323        if self.batch_on:
324            from batchfitpage import BatchFitPage
325            panel = BatchFitPage(parent=self)
326            #Increment index of batch page
327            self.batch_page_index += 1
328            index = self.batch_page_index
329        else:
330        """
331        from fitpage import FitPage
332        panel = FitPage(parent=self)
333        if self.batch_on:
334            self.batch_page_index += 1
335            caption = "BatchPage" + str(self.batch_page_index)
336            panel.set_index_model(self.batch_page_index)
337        else:
338            #Increment index of fit page
339            self.fit_page_index += 1
340            caption = "FitPage" + str(self.fit_page_index)
341            panel.set_index_model(self.fit_page_index)
342        panel.batch_on = self.batch_on
343        panel.populate_box(dict=self.model_list_box)
344        panel.set_manager(self._manager)
345        panel.window_caption = caption
346        panel.window_name = caption
347        self.AddPage(panel, caption, select=True)
348        self.opened_pages[panel.uid] = panel
349        self.set_engine_helper(panel=panel)
350        self._manager.create_fit_problem(panel.uid)
351        self._manager.page_finder[panel.uid].add_data(panel.get_data())
352        self.enable_close_button()
353        return panel
354   
355    def enable_close_button(self):
356        """
357        display the close button on tab for more than 1 tabs else remove the
358        close button
359        """
360        if self.GetPageCount() <= 1:
361            style = self.GetWindowStyleFlag() 
362            flag = wx.aui.AUI_NB_CLOSE_ON_ACTIVE_TAB
363            if style & wx.aui.AUI_NB_CLOSE_ON_ACTIVE_TAB == flag:
364                style = style & ~wx.aui.AUI_NB_CLOSE_ON_ACTIVE_TAB
365                self.SetWindowStyle(style)
366        else:
367            style = self.GetWindowStyleFlag()
368            flag = wx.aui.AUI_NB_CLOSE_ON_ACTIVE_TAB
369            if style & wx.aui.AUI_NB_CLOSE_ON_ACTIVE_TAB != flag:
370                style |= wx.aui.AUI_NB_CLOSE_ON_ACTIVE_TAB
371                self.SetWindowStyle(style)
372           
373    def delete_data(self, data):
374        """
375        Delete the given data
376        """
377        if data.__class__.__name__ != "list":
378            raise ValueError, "Fitpanel delete_data expect list of id"
379        else:
380            n = self.GetPageCount()
381            for page in self.opened_pages.values():
382                pos = self.GetPageIndex(page)
383                temp_data = page.get_data()
384                #stop the fitting before deleting the page
385                page.is_fitting()
386                if temp_data is not None and temp_data.id in data:
387                    self.SetSelection(pos)
388                    self.on_close_page(event=None)
389                    temp = self.GetSelection()
390                    self.DeletePage(temp)
391            if self.GetPageCount()== 0:
392                self._manager.on_add_new_page(event=None)
393       
394    def set_data_on_batch_mode(self, data_list):
395        """
396        Add all data to a single tab when the application is on Batch mode.
397        However all data in the set of data must be either 1D or 2D type.
398        This method presents option to select the data type before creating a
399        tab.
400        """
401        data_1d_list = []
402        data_2d_list = []
403        group_id_1d = wx.NewId()
404        # separate data into data1d and data2d list
405        for data in data_list:
406            if data.__class__.__name__ == "Data1D":
407                data.group_id = group_id_1d
408                data_1d_list.append(data)
409            if data.__class__.__name__ == "Data2D":
410                data.group_id = wx.NewId()
411                data_2d_list.append(data)
412        page = None
413        for p in self.opened_pages.values():
414            #check if there is an empty page to fill up
415            if not check_data_validity(p.get_data()):
416                page = p
417                self.batch_page_index += 1
418                break
419        if data_1d_list and data_2d_list:
420            # need to warning the user that this batch is a special case
421            from .fitting_widgets import BatchDataDialog
422            dlg = BatchDataDialog(self)
423            if dlg.ShowModal() == wx.ID_OK:
424                data_type = dlg.get_data()
425                dlg.Destroy()
426                if page  is None:
427                    page = self.add_empty_page()
428                if data_type == 1:
429                    #user has selected only data1D
430                    page.fill_data_combobox(data_1d_list)
431                elif data_type == 2:
432                    page.fill_data_combobox(data_2d_list)
433            else:
434                #the batch analysis is canceled
435                 dlg.Destroy()
436                 return None
437        else:
438            if page is None:
439                page = self.add_empty_page()
440            if data_1d_list and not data_2d_list:
441                #only on type of data
442                page.fill_data_combobox(data_1d_list)
443            elif not data_1d_list and data_2d_list:
444                page.fill_data_combobox(data_2d_list)
445        pos = self.GetPageIndex(page)
446        caption = "BatchPage" + str(self.batch_page_index)
447        self.SetPageText(pos, caption)
448        page.batch_on = self.batch_on
449        page.window_caption = caption
450        page.window_name = caption
451        self.opened_pages[page.uid] = page
452        return page
453   
454    def set_data(self, data_list):
455        """
456        Add a fitting page on the notebook contained by fitpanel
457       
458        :param data: data to fit
459       
460        :return panel : page just added for further used.
461        is used by fitting module
462       
463        """
464        if not data_list:
465            return None
466        if self.batch_on:
467           return self.set_data_on_batch_mode(data_list)
468        else:
469            data = None
470            try:
471                data = data_list[0]
472            except:
473                # for 'fitv' files
474                data_list = [data]
475                data = data_list[0]
476               
477            if data is None:
478                return None
479        for page in self.opened_pages.values():
480            #check if the selected data existing in the fitpanel
481            pos = self.GetPageIndex(page)
482            if not check_data_validity(page.get_data()):
483                #make sure data get placed in 1D empty tab if data is 1D
484                #else data get place on 2D tab empty tab
485                enable2D = page.get_view_mode()
486                if (data.__class__.__name__ == "Data2D" and enable2D)\
487                or (data.__class__.__name__ == "Data1D" and not enable2D):
488                    page.batch_on = self.batch_on
489                    page.fill_data_combobox(data_list)
490                    caption = "FitPage" + str(self.fit_page_index)
491                    self.SetPageText(pos, caption)
492                    self.SetSelection(pos)
493                    return page
494        #create new page and add data
495        page = self.add_empty_page()
496        pos = self.GetPageIndex(page)
497        page.fill_data_combobox(data_list)
498        self.opened_pages[page.uid] = page
499        self.SetSelection(pos)
500        return page
501       
502    def _onGetstate(self, event):
503        """
504        copy the state of a page
505        """
506        page = event.page
507        if page.uid in self.fit_page_name:
508           self.fit_page_name[page.uid].appendItem(page.createMemento()) 
509           
510    def _onUndo(self, event):
511        """
512        return the previous state of a given page is available
513        """
514        page = event.page
515        if page.uid in self.fit_page_name:
516            if self.fit_page_name[page.uid].getCurrentPosition()==0:
517                state = None
518            else:
519                state = self.fit_page_name[page.uid].getPreviousItem()
520                page._redo.Enable(True)
521            page.reset_page(state)
522       
523    def _onRedo(self, event): 
524        """
525        return the next state available
526        """       
527        page = event.page
528        if page.uid in self.fit_page_name:
529            length= len(self.fit_page_name[page.uid])
530            if self.fit_page_name[page.uid].getCurrentPosition()== length -1:
531                state = None
532                page._redo.Enable(False)
533                page._redo.Enable(True)
534            else:
535                state =self.fit_page_name[page.uid].getNextItem()
536            page.reset_page(state) 
537                 
538    def _close_helper(self, selected_page):
539        """
540        Delete the given page from the notebook
541        """
542        #remove hint page
543        #if selected_page == self.hint_page:
544        #    return
545        ## removing sim_page
546        if selected_page == self.sim_page :
547            self._manager.sim_page=None 
548            return
549        """
550        # The below is not working when delete #5 and still have #6.
551        if selected_page.__class__.__name__ == "FitPage":
552            self.fit_page_index -= 1
553        else:
554            self.batch_page_index -= 1
555        """
556        ## closing other pages
557        state = selected_page.createMemento()
558        page_name = selected_page.window_name
559        page_finder = self._manager.get_page_finder() 
560        fitproblem = None
561        ## removing fit page
562        data = selected_page.get_data()
563        #Don' t remove plot for 2D
564        flag = True
565        if data.__class__.__name__ == 'Data2D':
566            flag = False
567        if selected_page in page_finder:
568            #Delete the name of the page into the list of open page
569            for uid, list in self.opened_pages.iteritems():
570                #Don't return any panel is the exact same page is created
571               
572                if flag and selected_page.uid == uid:
573                    self._manager.remove_plot(uid, theory=False)
574                    break 
575            del page_finder[selected_page]
576        ##remove the check box link to the model name of this page (selected_page)
577        try:
578            self.sim_page.draw_page()
579        except:
580            ## that page is already deleted no need to remove check box on
581            ##non existing page
582            pass
583               
584        #Delete the name of the page into the list of open page
585        for uid, list in self.opened_pages.iteritems():
586            #Don't return any panel is the exact same page is created
587            if selected_page.uid == uid:
588                del self.opened_pages[selected_page.uid]
589                break 
590     
Note: See TracBrowser for help on using the repository browser.