source: sasview/fittingview/src/sans/perspectives/fitting/fitpanel.py @ 99b86c1

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 99b86c1 was 99b86c1, checked in by Jae Cho <jhjcho@…>, 13 years ago

fixed page indexing

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