source: sasview/fittingview/src/sans/perspectives/fitting/fitpanel.py @ 0766d6d

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

minor changes for better flow

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