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

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalcmagnetic_scattrelease-4.2.2ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since c9ecd1b was c9ecd1b, checked in by krzywon, 7 years ago

Comment NXcanSAS reader and remove unused variable from fitpanel.

  • Property mode set to 100644
File size: 22.3 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, StatusEvent
13from sas.sasgui.guiframe.dataFitting import check_data_validity
14from sas.sasgui.perspectives.fitting.simfitpage import SimultaneousFitPage
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        # Iterate through all pages and check for batch fitting
86        batch_state = None
87        if self.sim_page is not None:
88            batch_state = self.sim_page.set_state()
89
90        for uid, page in self.opened_pages.iteritems():
91            data = page.get_data()
92            # state must be cloned
93            state = page.get_state().clone()
94            # data_list only populated with real data
95            # Fake object in data from page.get_data() if model is selected
96            if len(page.data_list) is not 0 and page.model is not None:
97                new_doc = self._manager.state_reader.write_toXML(data,
98                                                                 state,
99                                                                 batch_state)
100                # Fit #2 through #n are append to first fit
101                if doc is not None and hasattr(doc, "firstChild"):
102                    # Only append if properly formed new_doc
103                    if new_doc is not None and hasattr(new_doc, "firstChild"):
104                        child = new_doc.firstChild.firstChild
105                        doc.firstChild.appendChild(child)
106                # First fit defines the main document
107                else:
108                    doc = new_doc
109
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.plugins_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        # use while-loop, for-loop will not do the job well.
196        while (self.GetPageCount() > 0):
197            page = self.GetPage(self.GetPageCount() - 1)
198            if self._manager.parent.panel_on_focus == page:
199                self._manager.parent.panel_on_focus = None
200            self._close_helper(selected_page=page)
201            self.DeletePage(self.GetPageCount() - 1)
202        # Clear list of names
203        self.fit_page_name = {}
204        # Clear list of opened pages
205        self.opened_pages = {}
206        self.fit_page_index = 0
207        self.batch_page_index = 0
208
209    def set_state(self, state):
210        """
211        Restore state of the panel
212        """
213        page_is_opened = False
214        if state is not None:
215            for uid, panel in self.opened_pages.iteritems():
216                # Don't return any panel is the exact same page is created
217                if uid == panel.uid and panel.data == state.data:
218                    # the page is still opened
219                    panel.reset_page(state=state)
220                    panel.save_current_state()
221                    page_is_opened = True
222            if not page_is_opened:
223                if state.data.__class__.__name__ != 'list':
224                    # To support older state file format
225                    list_data = [state.data]
226                else:
227                    # Todo: need new file format for the list
228                    list_data = state.data
229                panel = self._manager.add_fit_page(data=list_data)
230                # add data associated to the page created
231                if panel is not None:
232                    self._manager.store_data(uid=panel.uid,
233                                             data_list=list_data,
234                                             caption=panel.window_caption)
235                    panel.reset_page(state=state)
236                    panel.save_current_state()
237
238    def clear_panel(self):
239        """
240        Clear and close all panels, used by guimanager
241        """
242        # close all panels only when svs file opened
243        self.close_all()
244        self.sim_page = None
245        self.batch_page = None
246
247    def on_close_page(self, event=None):
248        """
249        close page and remove all references to the closed page
250        """
251        selected_page = self.GetPage(self.GetSelection())
252        if self.GetPageCount() == 1:
253            if selected_page.get_data() is not None:
254                if event is not None:
255                    event.Veto()
256                return
257        self._close_helper(selected_page=selected_page)
258
259    def close_page_with_data(self, deleted_data):
260        """
261        close a fit page when its data is completely remove from the graph
262        """
263        if deleted_data is None:
264            return
265        for index in range(self.GetPageCount()):
266            selected_page = self.GetPage(index)
267            if hasattr(selected_page, "get_data"):
268                data = selected_page.get_data()
269
270                if data is None:
271                    # the fitpanel exists and only the initial fit page is open
272                    # with no selected data
273                    return
274                if data.id == deleted_data.id:
275                    self._close_helper(selected_page)
276                    self.DeletePage(index)
277                    break
278
279    def set_manager(self, manager):
280        """
281        set panel manager
282
283        :param manager: instance of plugin fitting
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 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        if self.batch_on:
346            from batchfitpage import BatchFitPage
347            panel = BatchFitPage(parent=self)
348            self.batch_page_index += 1
349            caption = "BatchPage" + str(self.batch_page_index)
350            panel.set_index_model(self.batch_page_index)
351        else:
352            # Increment index of fit page
353            from fitpage import FitPage
354            panel = FitPage(parent=self)
355            self.fit_page_index += 1
356            caption = "FitPage" + str(self.fit_page_index)
357            panel.set_index_model(self.fit_page_index)
358        panel.batch_on = self.batch_on
359        panel._set_save_flag(not panel.batch_on)
360        panel.set_model_dictionary(self.model_dictionary)
361        panel.populate_box(model_dict=self.model_list_box)
362        panel.formfactor_combo_init()
363        panel.set_manager(self._manager)
364        panel.window_caption = caption
365        panel.window_name = caption
366        self.AddPage(panel, caption, select=True)
367        self.opened_pages[panel.uid] = panel
368        self._manager.create_fit_problem(panel.uid)
369        self._manager.page_finder[panel.uid].add_data(panel.get_data())
370        self.enable_close_button()
371        panel.on_set_focus(None)
372        return panel
373
374    def enable_close_button(self):
375        """
376        display the close button on tab for more than 1 tabs else remove the
377        close button
378        """
379        if self.GetPageCount() <= 1:
380            style = self.GetWindowStyleFlag()
381            flag = wx.aui.AUI_NB_CLOSE_ON_ACTIVE_TAB
382            if style & wx.aui.AUI_NB_CLOSE_ON_ACTIVE_TAB == flag:
383                style = style & ~wx.aui.AUI_NB_CLOSE_ON_ACTIVE_TAB
384                self.SetWindowStyle(style)
385        else:
386            style = self.GetWindowStyleFlag()
387            flag = wx.aui.AUI_NB_CLOSE_ON_ACTIVE_TAB
388            if style & wx.aui.AUI_NB_CLOSE_ON_ACTIVE_TAB != flag:
389                style |= wx.aui.AUI_NB_CLOSE_ON_ACTIVE_TAB
390                self.SetWindowStyle(style)
391
392    def delete_data(self, data):
393        """
394        Delete the given data
395        """
396        if data.__class__.__name__ != "list":
397            raise ValueError, "Fitpanel delete_data expect list of id"
398        else:
399            for page in self.opened_pages.values():
400                pos = self.GetPageIndex(page)
401                temp_data = page.get_data()
402                if temp_data is not None and temp_data.id in data:
403                    self.close_page_with_data(temp_data)
404            if self.sim_page is not None:
405                if len(self.sim_page.model_list) == 0:
406                    pos = self.GetPageIndex(self.sim_page)
407                    self.SetSelection(pos)
408                    self.on_close_page(event=None)
409                    self.DeletePage(pos)
410                    self.sim_page = None
411                    self.batch_on = False
412            if self.GetPageCount() == 0:
413                self._manager.on_add_new_page(event=None)
414
415    def set_data_on_batch_mode(self, data_list):
416        """
417        Add all data to a single tab when the application is on Batch mode.
418        However all data in the set of data must be either 1D or 2D type.
419        This method presents option to select the data type before creating a
420        tab.
421        """
422        data_1d_list = []
423        data_2d_list = []
424        group_id_1d = wx.NewId()
425        # separate data into data1d and data2d list
426        for data in data_list:
427            if data.__class__.__name__ == "Data1D":
428                data.group_id = group_id_1d
429                data_1d_list.append(data)
430            if data.__class__.__name__ == "Data2D":
431                data.group_id = wx.NewId()
432                data_2d_list.append(data)
433        page = None
434        for p in self.opened_pages.values():
435            # check if there is an empty page to fill up
436            if not check_data_validity(p.get_data()) and p.batch_on:
437
438                # make sure data get placed in 1D empty tab if data is 1D
439                # else data get place on 2D tab empty tab
440                enable2D = p.get_view_mode()
441                if (data.__class__.__name__ == "Data2D" and enable2D)\
442                or (data.__class__.__name__ == "Data1D" and not enable2D):
443                    page = p
444                    break
445        if data_1d_list and data_2d_list:
446            # need to warning the user that this batch is a special case
447            from sas.sasgui.perspectives.fitting.fitting_widgets import \
448                BatchDataDialog
449            dlg = BatchDataDialog(self)
450            if dlg.ShowModal() == wx.ID_OK:
451                data_type = dlg.get_data()
452                dlg.Destroy()
453                if page is None:
454                    page = self.add_empty_page()
455                if data_type == 1:
456                    # user has selected only data1D
457                    page.fill_data_combobox(data_1d_list)
458                elif data_type == 2:
459                    page.fill_data_combobox(data_2d_list)
460            else:
461                # the batch analysis is canceled
462                dlg.Destroy()
463                return None
464        else:
465            if page is None:
466                page = self.add_empty_page()
467            if data_1d_list and not data_2d_list:
468                # only on type of data
469                page.fill_data_combobox(data_1d_list)
470            elif not data_1d_list and data_2d_list:
471                page.fill_data_combobox(data_2d_list)
472
473        pos = self.GetPageIndex(page)
474        page.batch_on = self.batch_on
475        page._set_save_flag(not page.batch_on)
476        self.SetSelection(pos)
477        self.opened_pages[page.uid] = page
478        return page
479
480    def set_data(self, data_list):
481        """
482        Add a fitting page on the notebook contained by fitpanel
483
484        :param data_list: data to fit
485
486        :return panel : page just added for further used.
487        is used by fitting module
488
489        """
490        if not data_list:
491            return None
492        if self.batch_on:
493            return self.set_data_on_batch_mode(data_list)
494        else:
495            data = None
496            try:
497                data = data_list[0]
498            except Exception:
499                # for 'fitv' files
500                data_list = [data]
501                data = data_list[0]
502
503            if data is None:
504                return None
505        for page in self.opened_pages.values():
506            # check if the selected data existing in the fitpanel
507            pos = self.GetPageIndex(page)
508            if not check_data_validity(page.get_data()) and not page.batch_on:
509                # make sure data get placed in 1D empty tab if data is 1D
510                # else data get place on 2D tab empty tab
511                enable2D = page.get_view_mode()
512                if (data.__class__.__name__ == "Data2D" and enable2D)\
513                   or (data.__class__.__name__ == "Data1D" and not enable2D):
514                    page.batch_on = self.batch_on
515                    page._set_save_flag(not page.batch_on)
516                    page.fill_data_combobox(data_list)
517                    # caption = "FitPage" + str(self.fit_page_index)
518                    self.SetPageText(pos, page.window_caption)
519                    self.SetSelection(pos)
520                    return page
521        # create new page and add data
522        page = self.add_empty_page()
523        pos = self.GetPageIndex(page)
524        page.fill_data_combobox(data_list)
525        self.opened_pages[page.uid] = page
526        self.SetSelection(pos)
527        return page
528
529    def _onGetstate(self, event):
530        """
531        copy the state of a page
532        """
533        page = event.page
534        if page.uid in self.fit_page_name:
535            self.fit_page_name[page.uid].appendItem(page.createMemento())
536
537    def _onUndo(self, event):
538        """
539        return the previous state of a given page is available
540        """
541        page = event.page
542        if page.uid in self.fit_page_name:
543            if self.fit_page_name[page.uid].getCurrentPosition() == 0:
544                state = None
545            else:
546                state = self.fit_page_name[page.uid].getPreviousItem()
547                page._redo.Enable(True)
548            page.reset_page(state)
549
550    def _onRedo(self, event):
551        """
552        return the next state available
553        """
554        page = event.page
555        if page.uid in self.fit_page_name:
556            length = len(self.fit_page_name[page.uid])
557            if self.fit_page_name[page.uid].getCurrentPosition() == length - 1:
558                state = None
559                page._redo.Enable(False)
560                page._redo.Enable(True)
561            else:
562                state = self.fit_page_name[page.uid].getNextItem()
563            page.reset_page(state)
564
565    def _close_helper(self, selected_page):
566        """
567        Delete the given page from the notebook
568        """
569        # remove hint page
570        # if selected_page == self.hint_page:
571        #    return
572        # removing sim_page
573        if selected_page == self.sim_page:
574            self._manager.sim_page = None
575            return
576        if selected_page == self.batch_page:
577            self._manager.batch_page = None
578            return
579        # closing other pages
580        state = selected_page.createMemento()
581        page_finder = self._manager.get_page_finder()
582        # removing fit page
583        data = selected_page.get_data()
584        # Don' t remove plot for 2D
585        flag = True
586        if data.__class__.__name__ == 'Data2D':
587            flag = False
588        if selected_page in page_finder:
589            # Delete the name of the page into the list of open page
590            for uid, list in self.opened_pages.iteritems():
591                # Don't return any panel is the exact same page is created
592                if flag and selected_page.uid == uid:
593                    self._manager.remove_plot(uid, theory=False)
594                    break
595            del page_finder[selected_page]
596
597        # Delete the name of the page into the list of open page
598        for uid, list in self.opened_pages.iteritems():
599            # Don't return any panel is the exact same page is created
600            if selected_page.uid == uid:
601                del self.opened_pages[selected_page.uid]
602                break
603        # remove the check box link to the model name of the selected_page
604        try:
605            self.sim_page.draw_page()
606        except:
607            # that page is already deleted no need to remove check box on
608            # non existing page
609            pass
610        try:
611            self.batch_page.draw_page()
612        except:
613            # that page is already deleted no need to remove check box on
614            # non existing page
615            pass
Note: See TracBrowser for help on using the repository browser.