source: sasview/src/sas/perspectives/fitting/fitpanel.py @ 2f4b430

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 2f4b430 was 2f4b430, checked in by Doucet, Mathieu <doucetm@…>, 9 years ago

Take care of white spaces (pylint)

  • Property mode set to 100644
File size: 22.5 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 sas.guiframe.panel_base import PanelBase
12from sas.guiframe.events import PanelOnFocusEvent
13from sas.guiframe.events import StatusEvent
14from sas.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.model_dictionary = self.menu_mng.get_model_dictionary()
52        self.pageClosedEvent = wx.aui.EVT_AUINOTEBOOK_PAGE_CLOSE
53
54        self.Bind(self.pageClosedEvent, self.on_close_page)
55        ## save the title of the last page tab added
56        self.fit_page_name = {}
57        ## list of existing fit page
58        self.opened_pages = {}
59        #index of fit page
60        self.fit_page_index = 0
61        #index of batch page
62        self.batch_page_index = 0
63        #page of simultaneous fit
64        self.sim_page = None
65        self.batch_page = None
66        self.fit_engine_type = "bumps"
67        ## get the state of a page
68        self.Bind(basepage.EVT_PAGE_INFO, self._onGetstate)
69        self.Bind(basepage.EVT_PREVIOUS_STATE, self._onUndo)
70        self.Bind(basepage.EVT_NEXT_STATE, self._onRedo)
71        self.Bind(wx.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, self.on_page_changing)
72        self.Bind(wx.aui.EVT_AUINOTEBOOK_PAGE_CLOSED, self.on_closed)
73
74    def on_closed(self, event):
75        """
76        """
77        if self.GetPageCount() == 0:
78            self.add_empty_page()
79            self.enable_close_button()
80
81    def save_project(self, doc=None):
82        """
83        return an xml node containing state of the panel
84         that guiframe can write to file
85        """
86        msg = ""
87        for uid, page in self.opened_pages.iteritems():
88            if page.batch_on:
89                pos = self.GetPageIndex(page)
90                if pos != -1 and page not in [self.sim_page, self.batch_page]:
91                    msg += "%s .\n" % str(self.GetPageText(pos))
92            else:
93                data = page.get_data()
94                # state must be cloned
95                state = page.get_state().clone()
96                if data is not None and page.model is not None:
97                    new_doc = self._manager.state_reader.write_toXML(data,
98                                                                     state)
99                    if doc != None and hasattr(doc, "firstChild"):
100                        child = new_doc.firstChild.firstChild
101                        doc.firstChild.appendChild(child)
102                    else:
103                        doc = new_doc
104        if msg.strip() != "":
105            temp = "Save Project is not supported for Batch page.\n"
106            temp += "The following pages will not be save:\n"
107            message = temp + msg
108            wx.PostEvent(self._manager.parent, StatusEvent(status=message,
109                                                            info="warning"))
110        return doc
111
112    def update_model_list(self):
113        """
114        """
115        temp = self.menu_mng.update()
116        if len(temp):
117            self.model_list_box = temp
118        return temp
119
120    def reset_pmodel_list(self):
121        """
122        """
123        temp = self.menu_mng.pulgins_reset()
124        if len(temp):
125            self.model_list_box = temp
126        return temp
127
128    def get_page_by_id(self, uid):
129        """
130        """
131        if uid not in self.opened_pages:
132            msg = "Fitpanel cannot find ID: %s in self.opened_pages" % str(uid)
133            raise ValueError, msg
134        else:
135            return self.opened_pages[uid]
136
137    def on_page_changing(self, event):
138        """
139        calls the function when the current event handler has exited. avoiding
140        to call panel on focus on a panel that is currently deleted
141        """
142        wx.CallAfter(self.helper_on_page_change)
143
144    def helper_on_page_change(self):
145        """
146        """
147        pos = self.GetSelection()
148        if pos != -1:
149            selected_page = self.GetPage(pos)
150            wx.PostEvent(self._manager.parent,
151                         PanelOnFocusEvent(panel=selected_page))
152        self.enable_close_button()
153
154    def on_set_focus(self, event):
155        """
156        """
157        pos = self.GetSelection()
158        if pos != -1:
159            selected_page = self.GetPage(pos)
160            wx.PostEvent(self._manager.parent,
161                         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(self, 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    def close_all(self):
191        """
192        remove all pages, used when a svs file is opened
193        """
194
195        #get number of pages
196        nop = self.GetPageCount()
197        #use while-loop, for-loop will not do the job well.
198        while (nop > 0):
199            #delete the first page until no page exists
200            page = self.GetPage(0)
201            if self._manager.parent.panel_on_focus == page:
202                self._manager.parent.panel_on_focus = None
203            self._close_helper(selected_page=page)
204            self.DeletePage(0)
205            nop = nop - 1
206
207        ## save the title of the last page tab added
208        self.fit_page_name = {}
209        ## list of existing fit page
210        self.opened_pages = {}
211
212    def set_state(self, state):
213        """
214        Restore state of the panel
215        """
216        page_is_opened = False
217        if state is not None:
218            for uid, panel in self.opened_pages.iteritems():
219                #Don't return any panel is the exact same page is created
220                if uid == panel.uid and panel.data == state.data:
221                    # the page is still opened
222                    panel.reset_page(state=state)
223                    panel.save_current_state()
224                    page_is_opened = True
225            if not page_is_opened:
226                if state.data.__class__.__name__ != 'list':
227                    #To support older state file format
228                    list_data = [state.data]
229                else:
230                    #Todo: need new file format for the list
231                    list_data = state.data
232                panel = self._manager.add_fit_page(data=list_data)
233                # add data associated to the page created
234                if panel is not None:
235                    self._manager.store_data(uid=panel.uid,
236                                             data_list=list_data,
237                                             caption=panel.window_caption)
238                    panel.reset_page(state=state)
239                    panel.save_current_state()
240
241    def clear_panel(self):
242        """
243        Clear and close all panels, used by guimanager
244        """
245
246        #close all panels only when svs file opened
247        self.close_all()
248        self._manager.mypanels = []
249
250    def on_close_page(self, event=None):
251        """
252        close page and remove all references to the closed page
253        """
254        nbr_page = self.GetPageCount()
255        selected_page = self.GetPage(self.GetSelection())
256        if nbr_page == 1:
257            if selected_page.get_data() == None:
258                if event is not None:
259                    event.Veto()
260                return
261        self._close_helper(selected_page=selected_page)
262
263    def close_page_with_data(self, deleted_data):
264        """
265        close a fit page when its data is completely remove from the graph
266        """
267        if deleted_data is None:
268            return
269        for index in range(self.GetPageCount()):
270            selected_page = self.GetPage(index)
271            if hasattr(selected_page, "get_data"):
272                data = selected_page.get_data()
273
274                if data is None:
275                    #the fitpanel exists and only the initial fit page is open
276                    #with no selected data
277                    return
278                if data.id == deleted_data.id:
279                    self._close_helper(selected_page)
280                    self.DeletePage(index)
281                    break
282
283    def set_manager(self, manager):
284        """
285        set panel manager
286
287        :param manager: instance of plugin fitting
288
289        """
290        self._manager = manager
291        for pos in range(self.GetPageCount()):
292            page = self.GetPage(pos)
293            if page is not None:
294                page.set_manager(self._manager)
295
296    def set_model_list(self, dict):
297        """
298        copy a dictionary of model into its own dictionary
299
300        :param m_dict: dictionnary made of model name as key and model class
301        as value
302        """
303        self.model_list_box = dict
304
305    def set_model_dict(self, m_dict):
306        """
307        copy a dictionary of model name -> model object
308
309        :param m_dict: dictionary linking model name -> model object
310        """
311
312    def get_current_page(self):
313        """
314        :return: the current page selected
315
316        """
317        return self.GetPage(self.GetSelection())
318
319    def add_sim_page(self, caption="Const & Simul Fit"):
320        """
321        Add the simultaneous fit page
322        """
323        from simfitpage import SimultaneousFitPage
324        page_finder = self._manager.get_page_finder()
325        if caption == "Const & Simul Fit":
326            self.sim_page = SimultaneousFitPage(self, page_finder=page_finder,
327                                                 id= -1, batch_on=False)
328            self.sim_page.window_caption = caption
329            self.sim_page.window_name = caption
330            self.sim_page.uid = wx.NewId()
331            self.AddPage(self.sim_page, caption, True)
332            self.sim_page.set_manager(self._manager)
333            self.enable_close_button()
334            return self.sim_page
335        else:
336            self.batch_page = SimultaneousFitPage(self, batch_on=True,
337                                                   page_finder=page_finder)
338            self.batch_page.window_caption = caption
339            self.batch_page.window_name = caption
340            self.batch_page.uid = wx.NewId()
341            self.AddPage(self.batch_page, caption, True)
342            self.batch_page.set_manager(self._manager)
343            self.enable_close_button()
344            return self.batch_page
345
346    def add_empty_page(self):
347        """
348        add an empty page
349        """
350        """
351        if self.batch_on:
352            from batchfitpage import BatchFitPage
353            panel = BatchFitPage(parent=self)
354            #Increment index of batch page
355            self.batch_page_index += 1
356            index = self.batch_page_index
357        else:
358        """
359        from fitpage import FitPage
360        from batchfitpage import BatchFitPage
361        if self.batch_on:
362            panel = BatchFitPage(parent=self)
363            self.batch_page_index += 1
364            caption = "BatchPage" + str(self.batch_page_index)
365            panel.set_index_model(self.batch_page_index)
366        else:
367            #Increment index of fit page
368            panel = FitPage(parent=self)
369            self.fit_page_index += 1
370            caption = "FitPage" + str(self.fit_page_index)
371            panel.set_index_model(self.fit_page_index)
372        panel.batch_on = self.batch_on
373        panel._set_save_flag(not panel.batch_on)
374        panel.set_model_dictionary(self.model_dictionary)
375        panel.populate_box(model_dict=self.model_list_box)
376        panel.formfactor_combo_init()
377        panel.set_manager(self._manager)
378        panel.window_caption = caption
379        panel.window_name = caption
380        self.AddPage(panel, caption, select=True)
381        self.opened_pages[panel.uid] = 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 sas.perspectives.fitting.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.