source: sasview/sansview/perspectives/fitting/fitting.py @ a284455

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 a284455 was fa65e99, checked in by Gervaise Alina <gervyh@…>, 14 years ago

working on toggle1d \2d view

  • Property mode set to 100644
File size: 54.6 KB
Line 
1
2
3################################################################################
4#This software was developed by the University of Tennessee as part of the
5#Distributed Data Analysis of Neutron Scattering Experiments (DANSE)
6#project funded by the US National Science Foundation.
7#
8#See the license text in license.txt
9#
10#copyright 2009, University of Tennessee
11################################################################################
12
13
14import re
15import sys
16import wx
17import logging
18import numpy
19#import math
20import string
21import time
22#import thread
23from copy import deepcopy
24
25import models
26import fitpage
27
28from DataLoader.loader import Loader
29from sans.guiframe.dataFitting import Data2D
30from sans.guiframe.dataFitting import Data1D
31from sans.guiframe.events import NewPlotEvent
32from sans.guiframe.events import StatusEvent 
33from sans.guiframe.events import EVT_SLICER_PANEL
34from sans.guiframe.events import EVT_REMOVE_DATA
35from sans.guiframe.events import EVT_SLICER_PARS_UPDATE
36from sans.guiframe.gui_style import GUIFRAME_ID
37from sans.guiframe.plugin_base import PluginBase
38
39
40from .console import ConsoleUpdate
41from .fitproblem import FitProblem
42from .fitpanel import FitPanel
43from .fit_thread import FitThread
44from .pagestate import Reader
45from .fitpage import Chi2UpdateEvent
46
47DEFAULT_BEAM = 0.005
48DEFAULT_QMIN = 0.001
49DEFAULT_QMAX = 0.13
50DEFAULT_NPTS = 50
51MAX_NBR_DATA = 4
52
53
54(PageInfoEvent, EVT_PAGE_INFO)   = wx.lib.newevent.NewEvent()
55
56   
57
58class Plugin(PluginBase):
59    """
60    Fitting plugin is used to perform fit
61    """
62    def __init__(self, standalone=False):
63        PluginBase.__init__(self, name="Fitting", standalone=standalone)
64       
65        #Provide list of models existing in the application
66        self.menu_mng = models.ModelManager()
67       
68        #list of panel to send to guiframe
69        self.mypanels = []
70        # reference to the current running thread
71        self.calc_2D = None
72        self.calc_1D = None
73        self.calc_fit = None
74       
75        # Start with a good default
76        self.elapsed = 0.022
77        # the type of optimizer selected, park or scipy
78        self.fitter  = None
79        #let fit ready
80        self.fitproblem_count = None
81        #Flag to let the plug-in know that it is running stand alone
82        self.standalone = True
83        ## dictionary of page closed and id
84        self.closed_page_dict = {}
85        ## Fit engine
86        self._fit_engine = 'scipy'
87        #List of selected data
88        self.selected_data_list = []
89        ## list of slicer panel created to display slicer parameters and results
90        self.slicer_panels = []
91        # model 2D view
92        self.model2D_id = None
93        #keep reference of the simultaneous fit page
94        self.sim_page = None
95   
96        #Create a reader for fit page's state
97        self.state_reader = None 
98        self._extensions = '.fitv'
99        self.temp_state = []
100        self.state_index = 0
101        self.sfile_ext = None
102        # take care of saving  data, model and page associated with each other
103        self.page_finder = {}
104        # Log startup
105        logging.info("Fitting plug-in started") 
106       
107    def populate_menu(self, owner):
108        """
109        Create a menu for the Fitting plug-in
110       
111        :param id: id to create a menu
112        :param owner: owner of menu
113       
114        :return: list of information to populate the main menu
115       
116        """
117        #Menu for fitting
118        self.menu1 = wx.Menu()
119       
120        #Set park engine
121        id3 = wx.NewId()
122        scipy_help= "Scipy Engine: Perform Simple fit. More in Help window...."
123        self.menu1.AppendCheckItem(id3, "Simple Fit  [Scipy]",scipy_help) 
124        wx.EVT_MENU(owner, id3,  self._onset_engine_scipy)
125       
126        id3 = wx.NewId()
127        park_help = "Park Engine: Perform Complex fit. More in Help window...."
128        self.menu1.AppendCheckItem(id3, "Complex Fit  [Park]",park_help) 
129        wx.EVT_MENU(owner, id3,  self._onset_engine_park)
130       
131        self.menu1.FindItemByPosition(0).Check(True)
132        self.menu1.FindItemByPosition(1).Check(False)
133           
134        self.menu1.AppendSeparator()
135        id1 = wx.NewId()
136        simul_help = "Simultaneous Fit"
137        self.menu1.Append(id1, '&Simultaneous Page',simul_help)
138        wx.EVT_MENU(owner, id1, self.on_add_sim_page)
139       
140        id1 = wx.NewId()
141        simul_help = "Add new fit page"
142        self.menu1.Append(id1, '&Create New Page',simul_help)
143        wx.EVT_MENU(owner, id1, self.on_add_new_page)
144       
145        #self.fit_panel.set_model_list(self.menu_mng.get_model_list())
146   
147        #create  menubar items
148        return [(self.menu1, "Fitting")]
149               
150    def on_add_sim_page(self, event):
151        """
152        Create a page to access simultaneous fit option
153        """
154        if self.sim_page != None:
155            msg= "Simultaneous Fit page already opened"
156            wx.PostEvent(self.parent, StatusEvent(status= msg))
157            return 
158       
159        self.sim_page= self.fit_panel.add_sim_page()
160       
161    def help(self, evt):
162        """
163        Show a general help dialog.
164        """
165        from help_panel import  HelpWindow
166        frame = HelpWindow(None, -1, 'HelpWindow')   
167        frame.Show(True)
168       
169    def get_context_menu(self, plotpanel=None):
170        """
171        Get the context menu items available for P(r).them allow fitting option
172        for Data2D and Data1D only.
173       
174        :param graph: the Graph object to which we attach the context menu
175       
176        :return: a list of menu items with call-back function
177       
178        :note: if Data1D was generated from Theory1D 
179                the fitting option is not allowed
180               
181        """
182        graph = plotpanel.graph
183        fit_option = "Select data for fitting"
184        fit_hint =  "Dialog with fitting parameters "
185       
186        if graph.selected_plottable not in plotpanel.plots:
187            return []
188        item = plotpanel.plots[graph.selected_plottable]
189        if item.__class__.__name__ is "Data2D": 
190            if hasattr(item,"is_data"):
191                if item.is_data:
192                    return [[fit_option, fit_hint, self._onSelect]]
193                else:
194                    return [] 
195            return [[fit_option, fit_hint, self._onSelect]]
196        else:
197           
198            # if is_data is true , this in an actual data loaded
199            #else it is a data created from a theory model
200            if hasattr(item,"is_data"):
201                if item.is_data:
202                    return [[fit_option, fit_hint,
203                              self._onSelect]]
204                else:
205                    return [] 
206        return []   
207
208
209    def get_panels(self, parent):
210        """
211        Create and return a list of panel objects
212        """
213        self.parent = parent
214        #self.parent.Bind(EVT_FITSTATE_UPDATE, self.on_set_state_helper)
215        # Creation of the fit panel
216        self.fit_panel = FitPanel(parent=self.parent, manager=self)
217        self.on_add_new_page(event=None)
218        #Set the manager for the main panel
219        self.fit_panel.set_manager(self)
220        # List of windows used for the perspective
221        self.perspective = []
222        self.perspective.append(self.fit_panel.window_name)
223       
224        #index number to create random model name
225        self.index_model = 0
226        self.index_theory= 0
227        self.parent.Bind(EVT_SLICER_PANEL, self._on_slicer_event)
228       
229        self.parent.Bind(EVT_REMOVE_DATA, self._closed_fitpage)
230        self.parent.Bind(EVT_SLICER_PARS_UPDATE, self._onEVT_SLICER_PANEL)
231        self.parent._mgr.Bind(wx.aui.EVT_AUI_PANE_CLOSE,self._onclearslicer)   
232        #Create reader when fitting panel are created
233        self.state_reader = Reader(self.set_state)   
234        #append that reader to list of available reader
235        loader = Loader()
236        loader.associate_file_reader(".fitv", self.state_reader)
237        loader.associate_file_reader(".svs", self.state_reader)
238        from sans.perspectives.calculator.sld_panel import SldPanel
239        #Send the fitting panel to guiframe
240        self.mypanels.append(self.fit_panel) 
241        self.mypanels.append(SldPanel(parent=self.parent, base=self.parent))
242        return self.mypanels
243   
244    def clear_panel(self):
245        """
246        """
247        self.fit_panel.clear_panel()
248       
249    def set_default_perspective(self):
250        """
251        Call back method that True to notify the parent that the current plug-in
252        can be set as default  perspective.
253        when returning False, the plug-in is not candidate for an automatic
254        default perspective setting
255        """
256        return True
257   
258    def set_data(self, data_list):
259        """
260        receive a list of data to fit
261        """
262        selected_data_list = []
263        if len(data_list) > MAX_NBR_DATA :
264            from fitting_widgets import DataDialog
265            dlg = DataDialog(data_list=data_list, nb_data=MAX_NBR_DATA)
266            if dlg.ShowModal() == wx.ID_OK:
267                selected_data_list = dlg.get_data()
268        else:
269            selected_data_list = data_list
270        for data in selected_data_list:
271            self.add_fit_page(data=data)
272            wx.PostEvent(self.parent, NewPlotEvent(plot=data, 
273                                                   title=str(data.title)))
274           
275    def set_state(self, state=None, datainfo=None, format=None):
276        """
277        Call-back method for the fit page state reader.
278        This method is called when a .fitv/.svs file is loaded.
279       
280        : param state: PageState object
281        : param datainfo: data
282        """
283        #state = self.state_reader.get_state()
284        if state != None:
285            # store fitting state in temp_state
286            self.temp_state.append(state) 
287        else:
288            self.temp_state = []
289        # index to start with for a new set_state
290        self.state_index = 0
291        # state file format
292        self.sfile_ext = format
293       
294        self.on_set_state_helper(event=None)
295
296    def  on_set_state_helper(self,event=None):
297        """
298        Set_state_helper. This actually sets state after plotting data from state file.
299       
300        : event: FitStateUpdateEvent called by dataloader.plot_data from guiframe
301        """
302        if len(self.temp_state) == 0:
303            if self.state_index==0 and len(self.mypanels) <= 0 and self.sfile_ext =='.svs':
304                self.fit_panel.add_default_pages()
305                self.temp_state = []
306                self.state_index = 0
307            return
308       
309        try:
310            # Load fitting state
311            state = self.temp_state[self.state_index]
312            #panel state should have model selection to set_state
313            if state.formfactorcombobox != None:
314                #set state
315                data = self.parent.create_gui_data(state.data)
316               
317                data.group_id = state.data.group_id
318                self.parent.add_data(data_list=[data])
319                wx.PostEvent(self.parent, NewPlotEvent(plot=data,
320                                        title=data.title))
321                page = self.fit_panel.set_state(state)   
322            else:
323                self.parent.add_data(data_list=[data])
324                wx.PostEvent(self.parent, NewPlotEvent(plot=data,
325                                        title=data.title))
326                #just set data because set_state won't work
327                data = self.parent.create_gui_data(state.data)
328                data.group_id = state.data.group_id
329                self.add_fit_page(data)
330                caption = panel.window_name
331                self.store_data(page=panel.id, data=data, caption=caption)
332                self.mypanels.append(panel) 
333               
334            # get ready for the next set_state
335            self.state_index += 1
336
337            #reset state variables to default when all set_state is finished.
338            if len(self.temp_state) == self.state_index:
339               
340                self.temp_state = []
341                #self.state_index = 0
342                # Make sure the user sees the fitting panel after loading
343                #self.parent.set_perspective(self.perspective)
344                self.on_perspective(event=None)
345        except:
346            self.state_index==0
347            self.temp_state = []
348            raise
349                 
350    def save_fit_state(self, filepath, fitstate): 
351        """
352        save fit page state into file
353        """
354        self.state_reader.write(filename=filepath, fitstate=fitstate)
355       
356    def set_fit_range(self, id, qmin, qmax):
357        """
358        Set the fitting range of a given page
359        """
360        self.page_finder[id].set_range(qmin=qmin, qmax=qmax)
361                   
362    def schedule_for_fit(self,value=0, id=None,fitproblem =None): 
363        """
364        Set the fit problem field to 0 or 1 to schedule that problem to fit.
365        Schedule the specified fitproblem or get the fit problem related to
366        the current page and set value.
367       
368        :param value: integer 0 or 1
369        :param fitproblem: fitproblem to schedule or not to fit
370       
371        """   
372        if fitproblem !=None:
373            fitproblem.schedule_tofit(value)
374        else:
375            self.page_finder[id].schedule_tofit(value)
376         
377    def get_page_finder(self):
378        """
379        return self.page_finder used also by simfitpage.py
380        """ 
381        return self.page_finder
382   
383    def set_page_finder(self,modelname,names,values):
384        """
385        Used by simfitpage.py to reset a parameter given the string constrainst.
386         
387        :param modelname: the name ot the model for with the parameter has to reset
388        :param value: can be a string in this case.
389        :param names: the paramter name
390         
391        :note: expecting park used for fit.
392         
393        """ 
394        sim_page_id = self.sim_page.id
395        for id, value in self.page_finder.iteritems():
396            if id != sim_page_id:
397                list = value.get_model()
398                model = list[0]
399                if model.name == modelname:
400                    value.set_model_param(names, values)
401                    break
402         
403    def split_string(self,item): 
404        """
405        receive a word containing dot and split it. used to split parameterset
406        name into model name and parameter name example: ::
407       
408            paramaterset (item) = M1.A
409            Will return model_name = M1 , parameter name = A
410           
411        """
412        if string.find(item,".")!=-1:
413            param_names= re.split("\.",item)
414            model_name=param_names[0]           
415            ##Assume max len is 3; eg., M0.radius.width
416            if len(param_names) == 3:
417                param_name=param_names[1]+"."+param_names[2]
418            else:
419                param_name=param_names[1]                   
420            return model_name,param_name
421       
422    def stop_fit(self):
423        """
424        Stop the fit engine
425        """
426        if self.calc_fit!= None and self.calc_fit.isrunning():
427            self.calc_fit.stop()
428            wx.PostEvent(self.parent, StatusEvent(status="Fitting  \
429                is cancelled" , type="stop"))
430 
431    def set_smearer(self, id, smearer, qmin=None, qmax=None, draw=True):
432        """
433        Get a smear object and store it to a fit problem
434       
435        :param smearer: smear object to allow smearing data
436       
437        """   
438        if id not in self.page_finder.keys():
439            msg = "Cannot find ID: %s in page_finder" % str(id)
440            raise ValueError, msg
441        self.page_finder[id].set_smearer(smearer)
442        if draw:
443            ## draw model 1D with smeared data
444            data =  self.page_finder[id].get_fit_data()
445            model = self.page_finder[id].get_model()
446            if model is None:
447                return
448            ## if user has already selected a model to plot
449            ## redraw the model with data smeared
450            smear = self.page_finder[id].get_smearer()
451            self.draw_model(model=model, data=data, id=id, smearer=smear,
452                qmin=qmin, qmax=qmax)
453
454    def draw_model(self, model, id, data=None, smearer=None,
455                   enable1D=True, enable2D=False,
456                   state=None,
457                   toggle_mode_on=False,
458                   qmin=DEFAULT_QMIN, qmax=DEFAULT_QMAX, qstep=DEFAULT_NPTS):
459        """
460        Draw model.
461       
462        :param model: the model to draw
463        :param name: the name of the model to draw
464        :param data: the data on which the model is based to be drawn
465        :param description: model's description
466        :param enable1D: if true enable drawing model 1D
467        :param enable2D: if true enable drawing model 2D
468        :param qmin:  Range's minimum value to draw model
469        :param qmax:  Range's maximum value to draw model
470        :param qstep: number of step to divide the x and y-axis
471             
472        """
473        if data.__class__.__name__ == "Data1D" or not enable2D:   
474            ## draw model 1D with no loaded data
475           
476            self._draw_model1D(model=model, 
477                               data=data,
478                               id=id,
479                               enable1D=enable1D, 
480                               smearer=smearer,
481                               qmin=qmin,
482                               qmax=qmax, 
483                               toggle_mode_on=toggle_mode_on,
484                               state=state,
485                               qstep=qstep)
486        else:     
487            ## draw model 2D with no initial data
488             self._draw_model2D(model=model,
489                                id=id,
490                                data=data,
491                                enable2D=enable2D,
492                                smearer=smearer,
493                                qmin=qmin,
494                                qmax=qmax,
495                                state=state,
496                                toggle_mode_on=toggle_mode_on,
497                                qstep=qstep)
498           
499    def onFit(self):
500        """
501        perform fit
502        """
503        ##  count the number of fitproblem schedule to fit
504        fitproblem_count = 0
505        for value in self.page_finder.itervalues():
506            if value.get_scheduled() == 1:
507                fitproblem_count += 1
508               
509        ## if simultaneous fit change automatically the engine to park
510        if fitproblem_count > 1:
511            self._on_change_engine(engine='park')
512           
513        self.fitproblem_count = fitproblem_count 
514         
515        from sans.fit.Fitting import Fit
516        self.fitter = Fit(self._fit_engine)
517       
518        if self._fit_engine == "park":
519            engineType = "Simultaneous Fit"
520        else:
521            engineType = "Single Fit"
522           
523        fproblemId = 0
524        self.current_pg = None
525        for id, value in self.page_finder.iteritems():
526            try:
527                if value.get_scheduled() == 1:
528                    #Get list of parameters name to fit
529                    pars = []
530                    templist = []
531                    page = self.fit_panel.get_page_by_id(id)
532                    templist = page.get_param_list()
533                    for element in templist:
534                        name = str(element[1])
535                        pars.append(name)
536                    #Set Engine  (model , data) related to the page on
537                    self._fit_helper(value=value, pars=pars,
538                                      id=fproblemId, title=engineType) 
539                    fproblemId += 1 
540                    self.current_page_id = page
541            except:
542                raise
543                #msg= "%s error: %s" % (engineType, sys.exc_value)
544                #wx.PostEvent(self.parent, StatusEvent(status=msg, info="error",
545                #                                      type="stop"))
546                #return
547        ## If a thread is already started, stop it
548        #if self.calc_fit!= None and self.calc_fit.isrunning():
549        #    self.calc_fit.stop()
550         #Handler used for park engine displayed message
551        handler = ConsoleUpdate(parent=self.parent,improvement_delta=0.1)
552        ## perform single fit
553        if fitproblem_count == 1:
554            calc_fit = FitThread(parent=self.parent,
555                                    handler = handler,
556                                    fn=self.fitter,
557                                   pars=pars,
558                                   updatefn=handler.update_fit,
559                                   completefn=self._single_fit_completed)
560        else:
561            ## Perform more than 1 fit at the time
562            calc_fit = FitThread(parent=self.parent,
563                                handler=handler,
564                                    fn= self.fitter,
565                                   completefn= self._simul_fit_completed,
566                                  updatefn=handler.update_fit)
567       
568        calc_fit.queue()
569        self.ready_fit(calc_fit=calc_fit)
570     
571    def ready_fit(self, calc_fit):
572        """
573        Ready for another fit
574        """
575        if self.fitproblem_count != None and self.fitproblem_count > 1:
576            calc_fit.ready(2.5)
577           
578        else:
579            time.sleep(0.4)
580           
581    def remove_plot(self, id, theory=False):
582        """
583        remove model plot when a fit page is closed
584        """
585        fitproblem = self.page_finder[id]
586        data = fitproblem.get_fit_data()
587        model = fitproblem.get_model()
588        id = None
589        if model is not None:
590            id = data.id + name
591        if theory:
592            id = data.id
593        group_id = data.group_id[len(data.group_id)-1]
594        wx.PostEvent(self.parent, NewPlotEvent(id=id,
595                                                   group_id=group_id,
596                                                   remove=True))
597           
598    def store_data(self, id, data=None, caption=None):
599        """
600        Helper to save page reference into the plug-in
601       
602        :param page: page to store
603       
604        """
605        #create a fitproblem storing all link to data,model,page creation
606        if not id in self.page_finder.keys():
607            self.page_finder[id] = FitProblem()
608        self.page_finder[id].set_fit_data(data)
609        self.page_finder[id].set_fit_tab_caption(caption)
610       
611    def on_add_new_page(self, event=None):
612        """
613        ask fit panel to create a new empty page
614        """
615        try:
616            page = self.fit_panel.add_empty_page()
617            page_caption = page.window_name
618            # add data associated to the page created
619            if page != None: 
620                self.store_data(id=page.id, caption=page_caption,
621                                data=page.get_data())
622                wx.PostEvent(self.parent, StatusEvent(status="Page Created",
623                                               info="info"))
624            else:
625                msg = "Page was already Created"
626                wx.PostEvent(self.parent, StatusEvent(status=msg,
627                                                       info="warning"))
628        except:
629            raise
630            #msg = "Creating Fit page: %s"%sys.exc_value
631            #wx.PostEvent(self.parent, StatusEvent(status=msg, info="error"))
632       
633       
634    def add_fit_page(self, data):
635        """
636        given a data, ask to the fitting panel to create a new fitting page,
637        get this page and store it into the page_finder of this plug-in
638        """
639       
640        page = self.fit_panel.set_data(data)
641        page_caption = page.window_name
642        #append Data1D to the panel containing its theory
643        #if theory already plotted
644       
645        if page.id in self.page_finder:
646            theory_data = self.page_finder[page.id].get_theory_data()
647            if issubclass(data.__class__, Data2D):
648                data.group_id.append(wx.NewId())
649            else:
650                if theory_data is not None:
651                    group_id = theory_data.group_id[len(theory_data.group_id)-1]
652                    if group_id not in data.group_id:
653                        data.group_id.append(group_id)
654            self.parent.update_data(prev_data=theory_data, new_data=data)
655                   
656        self.store_data(id=page.id, data=data, caption=page.window_name)
657        if self.sim_page is not None:
658            self.sim_page.draw_page()
659           
660    def _onEVT_SLICER_PANEL(self, event):
661        """
662        receive and event telling to update a panel with a name starting with
663        event.panel_name. this method update slicer panel for a given interactor.
664       
665        :param event: contains type of slicer , paramaters for updating the panel
666            and panel_name to find the slicer 's panel concerned.
667        """
668        for item in self.parent.panels:
669            if self.parent.panels[item].window_caption.startswith(event.panel_name):
670                self.parent.panels[item].set_slicer(event.type, event.params)
671               
672        self.parent._mgr.Update()
673   
674    def _closed_fitpage(self, event):   
675        """
676        request fitpanel to close a given page when its unique data is removed
677        from the plot. close fitpage only when the a loaded data is removed
678        """   
679        if event is None or event.data is None:
680            return
681       
682        if hasattr(event.data,"is_data"):
683            if not event.data.is_data or \
684                event.data.__class__.__name__=="Data1D":
685                self.fit_panel.close_page_with_data(event.data) 
686       
687    def _add_page_onmenu(self, name,fitproblem=None):
688        """
689        Add name of a closed page of fitpanel in a menu
690        """
691        list = self.menu1.GetMenuItems()
692        for item in list:
693            if name == item.GetItemLabel():
694                self.closed_page_dict[name][1] = fitproblem
695               
696        if not name in self.closed_page_dict.keys():   
697            # Post paramters
698            event_id = wx.NewId()
699            self.menu1.Append(event_id, name, "Show %s fit panel" % name)
700            self.closed_page_dict[name]= [event_id, fitproblem]
701            wx.EVT_MENU(self.parent,event_id,  self._open_closed_page)
702       
703    def _open_closed_page(self, event):   
704        """
705        reopen a closed page
706        """
707        for name, value in self.closed_page_dict.iteritems():
708            if event.GetId() in value:
709                id,fitproblem = value
710                if name !="Model":
711                    data= fitproblem.get_fit_data()
712                    page = self.fit_panel.add_fit_page(data= data,reset=True)
713                    if fitproblem != None:
714                        self.page_finder[id] = fitproblem
715                        if self.sim_page != None:
716                            self.sim_page.draw_page()
717                           
718                else:
719                    model = fitproblem
720                    self.fit_panel.add_model_page(model=model, topmenu=True,
721                                                  reset= True)
722                    break
723   
724    def _reset_schedule_problem(self, id, value=0):
725        """
726        unschedule or schedule all fitproblem to be fit
727        """
728        for page_id in self.page_finder.keys():
729            self.page_finder[id].schedule_tofit(value)
730           
731    def _fit_helper(self, pars, value, id, title="Single Fit " ):
732        """
733        helper for fitting
734        """
735        metadata = value.get_fit_data()
736        model = value.get_model()
737        smearer = value.get_smearer()
738        qmin, qmax = value.get_range()
739        self.fit_id = id
740        #Create list of parameters for fitting used
741        templist = []
742       
743        try:
744            #Extra list of parameters and their constraints
745            listOfConstraint = []
746           
747            param = value.get_model_param()
748            if len(param) > 0:
749                for item in param:
750                    ## check if constraint
751                    if item[0] != None and item[1] != None:
752                        listOfConstraint.append((item[0],item[1]))
753                   
754            #Do the single fit
755            self.fitter.set_model(model, self.fit_id,
756                                   pars, constraints=listOfConstraint)
757           
758            self.fitter.set_data(data=metadata, id=self.fit_id,
759                                 smearer=smearer, qmin=qmin, qmax=qmax)
760           
761            self.fitter.select_problem_for_fit(id=self.fit_id,
762                                               value=value.get_scheduled())
763            value.clear_model_param()
764        except:
765            raise
766            #msg = title + " error: %s" % sys.exc_value
767            #wx.PostEvent(self.parent, StatusEvent(status=msg, type="stop"))
768         
769    def _onSelect(self,event):
770        """
771        when Select data to fit a new page is created .Its reference is
772        added to self.page_finder
773        """
774        self.panel = event.GetEventObject()
775        Plugin.on_perspective(self, event=event)
776        for plottable in self.panel.graph.plottables:
777            if plottable.__class__.__name__ in ["Data1D", "Theory1D"]:
778                if plottable.name == self.panel.graph.selected_plottable:
779                    data = plottable
780                    self.add_fit_page(data=data)
781                    return
782            else:
783                data = plottable
784                self.add_fit_page(data=data)
785           
786    def _single_fit_completed(self,result, pars, elapsed=None):
787        """
788        Display fit result on one page of the notebook.
789       
790        :param result: result of fit
791        :param pars: list of names of parameters fitted
792        :param current_pg: the page where information will be displayed
793        :param qmin: the minimum value of x to replot the model
794        :param qmax: the maximum value of x to replot model
795         
796        """     
797        try:
798            if result ==None:
799                msg= "Simple Fitting Stop !!!"
800                wx.PostEvent(self.parent, StatusEvent(status=msg,info="warning",
801                                                      type="stop"))
802                return
803            if not numpy.isfinite(result.fitness) or \
804                    numpy.any(result.pvec == None) or \
805                    not numpy.all(numpy.isfinite(result.pvec)):
806                msg = "Single Fitting did not converge!!!"
807                wx.PostEvent(self.parent, 
808                             StatusEvent(status=msg, type="stop"))
809                return
810            page_id = None
811            for id, value in self.page_finder.iteritems():   
812                if value.get_scheduled()==1:
813                    model = value.get_model()
814                    page_id = id
815                    break
816            param_name = []
817            i = 0
818            for name in pars:
819                param_name.append(name)
820               
821            cpage = self.fit_panel.get_page_by_id(page_id)
822            cpage.onsetValues(result.fitness, 
823                              param_name, result.pvec,result.stderr)
824           
825        except:
826            msg = "Single Fit completed but Following"
827            msg += " error occurred:%s" % sys.exc_value
828            wx.PostEvent(self.parent, StatusEvent(status=msg, info="error",
829                                                  type="stop"))
830            return
831       
832    def _simul_fit_completed(self, result, pars=None, elapsed=None):
833        """
834        Parameter estimation completed,
835        display the results to the user
836       
837        :param alpha: estimated best alpha
838        :param elapsed: computation time
839       
840        """
841        ## fit more than 1 model at the same time
842        try:
843            msg = "" 
844            if result ==None:
845                msg= "Complex Fitting Stop !!!"
846                wx.PostEvent(self.parent, StatusEvent(status=msg,type="stop"))
847                return
848            if not numpy.isfinite(result.fitness) or numpy.any(result.pvec ==None )or not numpy.all(numpy.isfinite(result.pvec) ):
849                msg= "Simultaneous Fitting did not converge!!!"
850                wx.PostEvent(self.parent, StatusEvent(status=msg,type="stop"))
851                return
852             
853            for id, value in self.page_finder.iteritems():   
854                if value.get_scheduled() == 1:
855                    model = value.get_model()
856                    data =  value.get_fit_data()
857                    small_param_name = []
858                    small_out = []
859                    small_cov = []
860                    i = 0
861                    #Separate result in to data corresponding to each page
862                    for p in result.parameters:
863                        model_name,param_name = self.split_string(p.name) 
864                        if model.name == model_name:
865                            p_name= model.name+"."+param_name
866                            if p.name == p_name:     
867                                if p.value != None and numpy.isfinite(p.value):
868                                    small_out.append(p.value )
869                                    small_param_name.append(param_name)
870                                    small_cov.append(p.stderr)
871
872                    # Display result on each page
873                    cpage = self.fit_panel.get_page_by_id(id)
874                    cpage.onsetValues(result.fitness,
875                                      small_param_name,
876                                      small_out,small_cov)
877        except:
878            raise
879            #msg= "Simultaneous Fit completed"
880            #msg +=" but Following error occurred:%s"%sys.exc_value
881            #wx.PostEvent(self.parent, StatusEvent(status=msg,type="stop"))
882            #return
883             
884    def _on_show_panel(self, event):
885        """
886        """
887        pass
888       
889    def _onset_engine_park(self,event):
890        """
891        set engine to park
892        """
893        self._on_change_engine('park')
894       
895    def _onset_engine_scipy(self,event):
896        """
897        set engine to scipy
898        """
899        self._on_change_engine('scipy')
900       
901    def _on_slicer_event(self, event):
902        """
903        Receive a panel as event and send it to guiframe
904       
905        :param event: event containing a panel
906       
907        """
908        if event.panel is not None:
909            new_panel = event.panel
910            self.slicer_panels.append(event.panel)
911            # Set group ID if available
912            event_id = self.parent.popup_panel(new_panel)
913            #self.menu3.Append(event_id, new_panel.window_caption,
914            #                 "Show %s plot panel" % new_panel.window_caption)
915            # Set id to allow us to reference the panel later
916         
917            new_panel.uid = event_id
918            self.mypanels.append(new_panel) 
919       
920    def _onclearslicer(self, event):
921        """
922        Clear the boxslicer when close the panel associate with this slicer
923        """
924        name =event.GetPane().caption
925   
926        for panel in self.slicer_panels:
927            if panel.window_caption==name:
928               
929                for item in self.parent.panels:
930                    if hasattr(self.parent.panels[item],"uid"):
931                        if self.parent.panels[item].uid ==panel.base.uid:
932                            self.parent.panels[item].onClearSlicer(event)
933                            self.parent._mgr.Update()
934                            break 
935                break
936   
937    def _return_engine_type(self):
938        """
939        return the current type of engine
940        """
941        return self._fit_engine
942     
943     
944    def _on_change_engine(self, engine='park'):
945        """
946        Allow to select the type of engine to perform fit
947       
948        :param engine: the key work of the engine
949       
950        """
951        ## saving fit engine name
952        self._fit_engine = engine
953        ## change menu item state
954        if engine=="park":
955            self.menu1.FindItemByPosition(0).Check(False)
956            self.menu1.FindItemByPosition(1).Check(True)
957        else:
958            self.menu1.FindItemByPosition(0).Check(True)
959            self.menu1.FindItemByPosition(1).Check(False)
960        ## post a message to status bar
961        msg = "Engine set to: %s" % self._fit_engine
962        wx.PostEvent(self.parent, 
963                     StatusEvent(status=msg))
964        ## Bind every open fit page with a newevent to
965        #know the current fitting engine
966        #import fitpage
967        event = fitpage.FitterTypeEvent()
968        event.type = self._fit_engine
969        wx.PostEvent(self.fit_panel, event)
970       
971    def _on_model_panel(self, evt):
972        """
973        react to model selection on any combo box or model menu.plot the model 
974       
975        :param evt: wx.combobox event
976       
977        """
978        model = evt.model
979        id = evt.id
980        qmin = evt.qmin
981        qmax = evt.qmax
982        smearer = evt.smearer
983        if model == None:
984            return
985       
986        if self.page_finder[id].get_model() is None:
987            model.name = "M" + str(self.index_model)
988            self.index_model += 1 
989        else:
990            model.name = self.page_finder[id].get_model().name
991        # save the name containing the data name with the appropriate model
992        self.page_finder[id].set_model(model)
993        self.page_finder[id].set_range(qmin=qmin, qmax=qmax)
994        if self.sim_page is not None:
995            self.sim_page.draw_page()
996       
997    def _update1D(self,x, output):
998        """
999        Update the output of plotting model 1D
1000        """
1001        msg = "Plot updating ... "
1002        wx.PostEvent(self.parent, StatusEvent(status=msg,type="update"))
1003        self.ready_fit()
1004        #self.calc_thread.ready(0.01)
1005   
1006    def _fill_default_model2D(self, theory,id, qmax,qstep, qmin=None):
1007        """
1008        fill Data2D with default value
1009       
1010        :param theory: Data2D to fill
1011       
1012        """
1013        from DataLoader.data_info import Detector, Source
1014       
1015        detector = Detector()
1016        theory.detector.append(detector)         
1017        theory.source= Source()
1018       
1019        ## Default values   
1020        theory.detector[0].distance= 8000   # mm       
1021        theory.source.wavelength= 6         # A     
1022        theory.detector[0].pixel_size.x= 5  # mm
1023        theory.detector[0].pixel_size.y= 5  # mm
1024       
1025        theory.detector[0].beam_center.x= qmax
1026        theory.detector[0].beam_center.y= qmax
1027   
1028        ## create x_bins and y_bins of the model 2D
1029        pixel_width_x = theory.detector[0].pixel_size.x
1030        pixel_width_y = theory.detector[0].pixel_size.y
1031        center_x      = theory.detector[0].beam_center.x/pixel_width_x
1032        center_y      = theory.detector[0].beam_center.y/pixel_width_y
1033
1034        # theory default: assume the beam
1035        #center is located at the center of sqr detector
1036        xmax = qmax
1037        xmin = -qmax
1038        ymax = qmax
1039        ymin = -qmax
1040       
1041        x=  numpy.linspace(start= -1*qmax,
1042                               stop=qmax,
1043                               num=qstep,
1044                               endpoint=True) 
1045        y = numpy.linspace(start=-1*qmax,
1046                               stop= qmax,
1047                               num= qstep,
1048                               endpoint=True)
1049         
1050        ## use data info instead
1051        new_x = numpy.tile(x, (len(y),1))
1052        new_y = numpy.tile(y, (len(x),1))
1053        new_y = new_y.swapaxes(0,1)
1054       
1055        # all data reuire now in 1d array
1056        qx_data = new_x.flatten()
1057        qy_data = new_y.flatten()
1058       
1059        q_data = numpy.sqrt(qx_data*qx_data+qy_data*qy_data)
1060        # set all True (standing for unmasked) as default
1061        mask    = numpy.ones(len(qx_data), dtype = bool)
1062       
1063        # calculate the range of qx and qy: this way,
1064        # it is a little more independent
1065        x_size = xmax- xmin
1066        y_size = ymax -ymin
1067       
1068        # store x and y bin centers in q space
1069        x_bins  = x
1070        y_bins  = y
1071        # bin size: x- & y-directions
1072        xstep = x_size/len(x_bins-1)
1073        ystep = y_size/len(y_bins-1)
1074       
1075        #theory.data = numpy.zeros(len(mask))
1076        theory.err_data = numpy.ones(len(mask))
1077        theory.qx_data = qx_data
1078        theory.qy_data = qy_data 
1079        theory.q_data = q_data
1080        theory.mask = mask           
1081        theory.x_bins = x_bins 
1082        theory.y_bins = y_bins   
1083       
1084        # max and min taking account of the bin sizes
1085        theory.xmin = xmin
1086        theory.xmax = xmax
1087        theory.ymin = ymin
1088        theory.ymax = ymax
1089        theory.group_id = str(id) + "Model2D"
1090        theory.id = str(id) + "Mode2D"
1091 
1092               
1093    def _complete1D(self, x,y, id, elapsed,index,model,
1094                    toggle_mode_on=False,state=None, data=None):
1095        """
1096        Complete plotting 1D data
1097        """ 
1098        try:
1099            new_plot = Data1D(x=x, y=y)
1100            new_plot.symbol = GUIFRAME_ID.CURVE_SYMBOL_NUM
1101            if data != None:
1102                _xaxis, _xunit = data.get_xaxis() 
1103                _yaxis, _yunit = data.get_yaxis() 
1104                new_plot.title = data.name
1105                #if the theory is already plotted use the same group id
1106                #to replot
1107                if id in self.page_finder:
1108                    theory_data = self.page_finder[id].get_theory_data()
1109                    if theory_data is not None:
1110                        group_id = theory_data.group_id[len(theory_data.group_id)-1]
1111                        if group_id not in data.group_id:
1112                            data.group_id.append(group_id)
1113                #data is plotted before the theory, then take its group_id
1114                #assign to the new theory
1115                group_id = data.group_id[len(data.group_id)-1]
1116                if group_id not in new_plot.group_id:
1117                    new_plot.group_id.append(group_id)
1118            else:
1119                _xaxis, _xunit = "\\rm{Q}", 'A^{-1}'
1120                _yaxis, _yunit = "\\rm{Intensity} ", "cm^{-1}"
1121                new_plot.title = "Analytical model 1D "
1122                #find a group id to plot theory without data
1123                group_id =  str(id) + " Model1D" 
1124                if group_id not in new_plot.group_id:
1125                    new_plot.group_id.append(group_id)
1126                new_plot.is_data = False 
1127            new_plot.id =  str(id) + " Model1D" 
1128            print "new_plot.id", new_plot.id
1129            #find if this theory was already plotted and replace that plot given
1130            #the same id
1131            if id in self.page_finder:
1132                theory_data = self.page_finder[id].get_theory_data()
1133                if theory_data is not None:
1134                    temp_id = theory_data.id
1135                    print "theory_data id", temp_id
1136                    new_plot.id = temp_id
1137            if toggle_mode_on:
1138                new_plot.id =  str(id) + " Model" 
1139            new_plot.name = model.name + " ["+ str(model.__class__.__name__)+ "]"
1140            new_plot.xaxis(_xaxis, _xunit)
1141            new_plot.yaxis(_yaxis, _yunit)
1142            self.page_finder[id].set_theory_data(new_plot)
1143            if data is None:
1144                theory_data = self.page_finder[id].get_theory_data()
1145                self.parent.add_data_helper(data_list=[theory_data])
1146                data_id = theory_data.id
1147            else:
1148                data_id = data.id
1149            self.parent.append_theory(data_id=data_id, 
1150                                          theory=new_plot, state=state)
1151           
1152            wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot,
1153                                            title= str(new_plot.title)))
1154            current_pg = self.fit_panel.get_page_by_id(id)
1155            wx.PostEvent(current_pg,
1156                Chi2UpdateEvent(output=self._cal_chisqr(data=data,
1157                                                        id=id,
1158                                                        index=index)))
1159            msg = "Plot 1D  complete !"
1160            wx.PostEvent( self.parent, StatusEvent(status=msg, type="stop" ))
1161        except:
1162            raise
1163            #msg = " Error occurred when drawing %s Model 1D: " % new_plot.name
1164            #msg += " %s"  % sys.exc_value
1165            #wx.PostEvent(self.parent, StatusEvent(status=msg, type="stop"))
1166   
1167    def _update2D(self, output,time=None):
1168        """
1169        Update the output of plotting model
1170        """
1171        wx.PostEvent(self.parent, StatusEvent(status="Plot \
1172        #updating ... ", type="update"))
1173        self.ready_fit()
1174        #self.calc_thread.ready(0.01)
1175   
1176    def _complete2D(self, image, data, model, id,  elapsed, index, qmin,
1177                     qmax, toggle_mode_on=False,state=None,qstep=DEFAULT_NPTS):
1178        """
1179        Complete get the result of modelthread and create model 2D
1180        that can be plot.
1181        """
1182        err_image = numpy.zeros(numpy.shape(image))
1183       
1184        new_plot= Data2D(image=image, err_image=err_image)
1185        new_plot.name = model.name
1186       
1187        if data is None:
1188            self._fill_default_model2D(theory=new_plot, 
1189                                       qmax=qmax, 
1190                                       id=id,
1191                                       qstep=qstep,
1192                                        qmin= qmin)
1193           
1194        else:
1195            new_plot.id = str(id) + " Model2D"
1196            new_plot.group_id = str(id) + " Model2D"
1197         
1198            new_plot.x_bins = data.x_bins
1199            new_plot.y_bins = data.y_bins
1200            new_plot.detector = data.detector
1201            new_plot.source = data.source
1202            new_plot.is_data = False 
1203            new_plot.qx_data = data.qx_data
1204            new_plot.qy_data = data.qy_data
1205            new_plot.q_data = data.q_data
1206            #numpy.zeros(len(data.err_data))#data.err_data
1207            new_plot.err_data = err_image
1208            new_plot.mask = data.mask
1209            ## plot boundaries
1210            new_plot.ymin = data.ymin
1211            new_plot.ymax = data.ymax
1212            new_plot.xmin = data.xmin
1213            new_plot.xmax = data.xmax
1214        new_plot.name = model.name + " ["+ str(model.__class__.__name__)+ "]"
1215        new_plot.title = "Analytical model 2D "
1216        theory_data = deepcopy(new_plot)
1217        theory_data.name = "Unknown"
1218        self.page_finder[id].set_theory_data(theory_data)
1219       
1220        if data is None:
1221            theory_data = self.page_finder[id].get_theory_data()
1222            self.parent.add_data_helper(data_list=[theory_data])
1223            data_id = theory_data.id
1224        else:
1225            data_id = data.id
1226        self.parent.append_theory(data_id=data_id, 
1227                                          theory=new_plot, state=state)
1228        if toggle_mode_on:
1229            new_plot.id = str(id) + " Model"     
1230        ## plot
1231        wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot,
1232                         title=new_plot.title))
1233        # Chisqr in fitpage
1234        current_pg = self.fit_panel.get_page_by_id(id)
1235        wx.PostEvent(current_pg,
1236            Chi2UpdateEvent(output=self._cal_chisqr(data=data, id=id, index=index)))
1237        msg = "Plot 2D complete !"
1238        wx.PostEvent(self.parent, StatusEvent(status=msg, type="stop"))
1239   
1240    def _draw_model2D(self, model, id, data=None, smearer=None,
1241                      description=None, enable2D=False,
1242                      state=None,
1243                      toggle_mode_on=False,
1244                      qmin=DEFAULT_QMIN, qmax=DEFAULT_QMAX,
1245                       qstep=DEFAULT_NPTS):
1246        """
1247        draw model in 2D
1248       
1249        :param model: instance of the model to draw
1250        :param description: the description of the model
1251        :param enable2D: when True allows to draw model 2D
1252        :param qmin: the minimum value to  draw model 2D
1253        :param qmax: the maximum value to draw model 2D
1254        :param qstep: the number of division of Qx and Qy of the model to draw
1255           
1256        """
1257        x=  numpy.linspace(start=-1*qmax,
1258                               stop=qmax,
1259                               num=qstep,
1260                               endpoint=True) 
1261        y = numpy.linspace(start= -1*qmax,
1262                               stop=qmax,
1263                               num=qstep,
1264                               endpoint=True)
1265        if model is None:
1266            msg = "Panel with ID: %s does not contained model" % str(id)
1267            raise ValueError, msg
1268        ## use data info instead
1269        if data is not None:
1270            ## check if data2D to plot
1271            if hasattr(data, "x_bins"):
1272                enable2D = True
1273                x = data.x_bins
1274                y = data.y_bins
1275               
1276        if not enable2D:
1277            return None, None
1278        try:
1279            from model_thread import Calc2D
1280            ## If a thread is already started, stop it
1281            if (self.calc_2D is not None) and self.calc_2D.isrunning():
1282                self.calc_2D.stop()
1283
1284            self.calc_2D = Calc2D(x=x,
1285                                    y=y,
1286                                    model=model, 
1287                                    data=data,
1288                                    id=id,
1289                                    smearer=smearer,
1290                                    qmin=qmin,
1291                                    qmax=qmax,
1292                                    qstep=qstep,
1293                                    toggle_mode_on=toggle_mode_on,
1294                                    state=state,
1295                                    completefn=self._complete2D,
1296                                    updatefn=self._update2D)
1297            self.calc_2D.queue()
1298
1299        except:
1300            raise
1301            #msg = " Error occurred when drawing %s Model 2D: " % model.name
1302            #msg += " %s" % sys.exc_value
1303            #wx.PostEvent(self.parent, StatusEvent(status=msg))
1304
1305    def _draw_model1D(self, model, id, data=None, smearer=None,
1306                qmin=DEFAULT_QMIN, qmax=DEFAULT_QMAX, 
1307                state=None,
1308                toggle_mode_on=False,
1309                qstep=DEFAULT_NPTS, enable1D=True):
1310        """
1311        Draw model 1D from loaded data1D
1312       
1313        :param data: loaded data
1314        :param model: the model to plot
1315       
1316        """
1317        x=  numpy.linspace(start=qmin,
1318                           stop=qmax,
1319                           num=qstep,
1320                           endpoint=True
1321                           )
1322        if data is not None:
1323            ## check for data2D
1324            if hasattr(data,"x_bins"):
1325                return
1326            x = data.x
1327            if qmin == DEFAULT_QMIN :
1328                qmin = min(data.x)
1329            if qmax == DEFAULT_QMAX:
1330                qmax = max(data.x) 
1331        if not enable1D:
1332            return 
1333        try:
1334            from model_thread import Calc1D
1335            ## If a thread is already started, stop it
1336            if (self.calc_1D is not None) and self.calc_1D.isrunning():
1337                self.calc_1D.stop()
1338            self.calc_1D = Calc1D(x=x,
1339                                  data=data,
1340                                  model=model,
1341                                  id=id, 
1342                                  qmin=qmin,
1343                                  qmax=qmax,
1344                                  smearer=smearer,
1345                                  state=state,
1346                                  toggle_mode_on=toggle_mode_on,
1347                                  completefn=self._complete1D,
1348                                  updatefn=self._update1D)
1349            self.calc_1D.queue()
1350
1351        except:
1352            msg = " Error occurred when drawing %s Model 1D: " % model.name
1353            msg += " %s" % sys.exc_value
1354            wx.PostEvent(self.parent, StatusEvent(status=msg))
1355
1356    def _cal_chisqr(self, id, data=None, index=None): 
1357        """
1358        Get handy Chisqr using the output from draw1D and 2D,
1359        instead of calling expansive CalcChisqr in guithread
1360        """
1361        # default chisqr
1362        chisqr = None
1363       
1364        # return None if data == None
1365        if data == None: return chisqr
1366       
1367        # Get data: data I, theory I, and data dI in order
1368        if data.__class__.__name__ =="Data2D":
1369            if index == None: 
1370                index = numpy.ones(len(data.data),ntype=bool)
1371            # get rid of zero error points
1372            index = index & (data.err_data != 0) 
1373            fn = data.data[index] 
1374            theory_data = self.page_finder[id].get_theory_data()
1375            gn = theory_data.data[index]
1376            en = data.err_data[index]
1377        else:
1378            # 1 d theory from model_thread is only in the range of index
1379            if index == None:
1380                index = numpy.ones(len(data.y), ntype=bool)
1381            if data.dy == None or data.dy == []:
1382                dy = numpy.ones(len(data.y))
1383            else:
1384                ## Set consitently w/AbstractFitengine:
1385                # But this should be corrected later.
1386                dy = data.dy
1387                dy[dy==0] = 1 
1388            fn = data.y[index] 
1389            theory_data = self.page_finder[id].get_theory_data()
1390            gn = theory_data.y
1391            en = dy[index]
1392        # residual
1393        res = (fn - gn)/en
1394        # get chisqr only w/finite
1395        val = res[numpy.isfinite(res)]*res[numpy.isfinite(res)]
1396        chisqr = numpy.average(val)
1397        return chisqr
1398   
1399   
1400#def profile(fn, *args, **kw):
1401#    import cProfile, pstats, os
1402#    global call_result
1403#    def call():
1404#        global call_result
1405#        call_result = fn(*args, **kw)
1406#    cProfile.runctx('call()', dict(call=call), {}, 'profile.out')
1407#    stats = pstats.Stats('profile.out')
1408#    #stats.sort_stats('time')
1409#    stats.sort_stats('calls')
1410#    stats.print_stats()
1411#    os.unlink('profile.out')
1412#    return call_result
1413if __name__ == "__main__":
1414    i = Plugin()
1415   
1416   
1417   
1418   
Note: See TracBrowser for help on using the repository browser.