source: sasview/src/sas/sasgui/perspectives/fitting/fitpanel.py @ c9dc8c9

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 c9dc8c9 was 05228b0, checked in by Paul Kienzle <pkienzle@…>, 9 years ago

use the new bumps fitter changed event to update the active fitter indicator

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