source: sasview/src/sas/perspectives/fitting/fitpanel.py @ 6f16e25

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 6f16e25 was 6f16e25, checked in by Paul Kienzle <pkienzle@…>, 9 years ago

clean up wx id handling in fitting perspective

  • 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        """
[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        """
122        temp = self.menu_mng.pulgins_reset()
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        """
156        pos = self.GetSelection()
157        if pos != -1:
158            selected_page = self.GetPage(pos)
[2f4b430]159            wx.PostEvent(self._manager.parent,
[fa09d62]160                         PanelOnFocusEvent(panel=selected_page))
[2f4b430]161
[fa09d62]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()
[2f4b430]170
[fa09d62]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)
[2f4b430]179
[fa09d62]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()
[2f4b430]188
[fa09d62]189    def close_all(self):
190        """
191        remove all pages, used when a svs file is opened
192        """
[2f4b430]193
[fa09d62]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
[2f4b430]205
[fa09d62]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 = {}
[2f4b430]210
[fa09d62]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()
[2f4b430]239
[fa09d62]240    def clear_panel(self):
241        """
242        Clear and close all panels, used by guimanager
243        """
[2f4b430]244
[fa09d62]245        #close all panels only when svs file opened
246        self.close_all()
247        self._manager.mypanels = []
[2f4b430]248
[fa09d62]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)
[2f4b430]261
[fa09d62]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()
[2f4b430]272
[fa09d62]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
[2f4b430]281
[fa09d62]282    def set_manager(self, manager):
283        """
284        set panel manager
[2f4b430]285
[fa09d62]286        :param manager: instance of plugin fitting
[2f4b430]287
[fa09d62]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
[2f4b430]298
[fa09d62]299        :param m_dict: dictionnary made of model name as key and model class
[ac7be54]300            as value
[fa09d62]301        """
302        self.model_list_box = dict
[2f4b430]303
[fa09d62]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
[2f4b430]314
[fa09d62]315        """
316        return self.GetPage(self.GetSelection())
[2f4b430]317
[fa09d62]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,
[6f16e25]326                                                 id= wx.ID_ANY, batch_on=False)
[fa09d62]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
[2f4b430]344
[fa09d62]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
[2f4b430]386
[fa09d62]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)
[2f4b430]404
[fa09d62]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)
[2f4b430]423
[fa09d62]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.
[2f4b430]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
[fa09d62]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:
[2f4b430]446
[fa09d62]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.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)
[2f4b430]480
[fa09d62]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
[2f4b430]487
[fa09d62]488    def set_data(self, data_list):
489        """
490        Add a fitting page on the notebook contained by fitpanel
[2f4b430]491
[fa09d62]492        :param data: data to fit
[2f4b430]493
[fa09d62]494        :return panel : page just added for further used.
495        is used by fitting module
[2f4b430]496
[fa09d62]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]
[2f4b430]510
[fa09d62]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
[2f4b430]536
[fa09d62]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())
[2f4b430]544
[fa09d62]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)
[2f4b430]557
[fa09d62]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)
[2f4b430]572
[fa09d62]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
[2f4b430]607
[fa09d62]608                if flag and selected_page.uid == uid:
609                    self._manager.remove_plot(uid, theory=False)
610                    break
611            del page_finder[selected_page]
[2f4b430]612
[fa09d62]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.