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

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 fd82d9b was f66d9d1, checked in by ajj, 9 years ago

SasView? loads models from sasmodels. Uses model attributes to populate various lists and dicts. Fix function name typo. Remove some redundant comments.

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