source: sasview/fittingview/src/sans/perspectives/fitting/fitpanel.py @ 56acb5d

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 56acb5d was f32d144, checked in by Mathieu Doucet <doucetm@…>, 13 years ago

Pep-8-ification

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