source: sasview/sansview/perspectives/fitting/fitpanel.py @ 67ae937

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 67ae937 was 4225aed, checked in by Gervaise Alina <gervyh@…>, 13 years ago

working on batch

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