source: sasview/src/sas/perspectives/fitting/fitpanel.py @ 8d302cd

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

Take care of white spaces (pylint)

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