source: sasview/fittingview/src/sans/perspectives/fitting/fitpanel.py @ 4c2c93f

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 4c2c93f was 5e48acb, checked in by Jae Cho <jhjcho@…>, 13 years ago

fixed group_id related problem when set data in fitpage and added feature that send multiple data sets on import

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