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

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.2ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since a95ae9a was a95ae9a, checked in by krzywon, 5 years ago

#189: Modified save project to include information from simultaneous fit panel. Next step is to load in the parameters and repopulate the simultaneous and constrained fit panel.

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