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

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 96b5f8de was f60251f, checked in by krzywon, 9 years ago

Resolves #434 - Display the active fit optimizer on the fit panel.

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