source: sasview/src/sas/perspectives/fitting/fitting.py @ a6f3613

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 a6f3613 was 765e47c, checked in by Doucet, Mathieu <doucetm@…>, 10 years ago

Re #390. Fix how the default perspective is set.

  • Property mode set to 100644
File size: 82.8 KB
Line 
1"""
2    Fitting perspective
3"""
4################################################################################
5#This software was developed by the University of Tennessee as part of the
6#Distributed Data Analysis of Neutron Scattering Experiments (DANSE)
7#project funded by the US National Science Foundation.
8#
9#See the license text in license.txt
10#
11#copyright 2009, University of Tennessee
12################################################################################
13import re
14import sys
15import os
16import wx
17import logging
18import numpy
19import time
20from copy import deepcopy
21import models
22
23from sas.dataloader.loader import Loader
24from sas.guiframe.dataFitting import Data2D
25from sas.guiframe.dataFitting import Data1D
26from sas.guiframe.dataFitting import check_data_validity
27from sas.guiframe.events import NewPlotEvent
28from sas.guiframe.events import StatusEvent
29from sas.guiframe.events import EVT_SLICER_PANEL
30from sas.guiframe.events import EVT_SLICER_PARS_UPDATE
31from sas.guiframe.gui_style import GUIFRAME_ID
32from sas.guiframe.plugin_base import PluginBase
33from sas.guiframe.data_processor import BatchCell
34from sas.fit.BumpsFitting import BumpsFit as Fit
35from sas.perspectives.fitting.console import ConsoleUpdate
36from sas.perspectives.fitting.fitproblem import FitProblemDictionary
37from sas.perspectives.fitting.fitpanel import FitPanel
38from sas.perspectives.fitting.resultpanel import ResultPanel, PlotResultEvent
39
40from sas.perspectives.fitting.fit_thread import FitThread
41from sas.perspectives.fitting.pagestate import Reader
42from sas.perspectives.fitting.fitpage import Chi2UpdateEvent
43from sas.perspectives.calculator.model_editor import TextDialog
44from sas.perspectives.calculator.model_editor import EditorWindow
45from sas.guiframe.gui_manager import MDIFrame
46
47MAX_NBR_DATA = 4
48
49(PageInfoEvent, EVT_PAGE_INFO) = wx.lib.newevent.NewEvent()
50
51
52if sys.platform == "win32":
53    ON_MAC = False
54else:
55    ON_MAC = True
56
57
58
59class Plugin(PluginBase):
60    """
61    Fitting plugin is used to perform fit
62    """
63    def __init__(self, standalone=False):
64        PluginBase.__init__(self, name="Fitting", standalone=standalone)
65
66        #list of panel to send to guiframe
67        self.mypanels = []
68        # reference to the current running thread
69        self.calc_2D = None
70        self.calc_1D = None
71
72        self.color_dict = {}
73
74        self.fit_thread_list = {}
75        self.residuals = None
76        self.weight = None
77        self.fit_panel = None
78        self.plot_panel = None
79        # Start with a good default
80        self.elapsed = 0.022
81        self.fit_panel = None
82        ## dictionary of page closed and id
83        self.closed_page_dict = {}
84        ## Relative error desired in the sum of squares (float)
85        self.batch_reset_flag = True
86        #List of selected data
87        self.selected_data_list = []
88        ## list of slicer panel created to display slicer parameters and results
89        self.slicer_panels = []
90        # model 2D view
91        self.model2D_id = None
92        #keep reference of the simultaneous fit page
93        self.sim_page = None
94        self.sim_menu = None
95        self.batch_page = None
96        self.batch_menu = None
97        self.index_model = 0
98        self.test_model_color = None
99        #Create a reader for fit page's state
100        self.state_reader = None
101        self._extensions = '.fitv'
102        self.menu1 = None
103        self.new_model_frame = None
104
105        self.temp_state = []
106        self.state_index = 0
107        self.sfile_ext = None
108        # take care of saving  data, model and page associated with each other
109        self.page_finder = {}
110        # Log startup
111        logging.info("Fitting plug-in started")
112        self.batch_capable = self.get_batch_capable()
113
114    def get_batch_capable(self):
115        """
116        Check if the plugin has a batch capability
117        """
118        return True
119
120    def create_fit_problem(self, page_id):
121        """
122        Given an ID create a fitproblem container
123        """
124        self.page_finder[page_id] = FitProblemDictionary()
125
126    def delete_fit_problem(self, page_id):
127        """
128        Given an ID create a fitproblem container
129        """
130        if page_id in self.page_finder.iterkeys():
131            del self.page_finder[page_id]
132
133    def add_color(self, color, id):
134        """
135        adds a color as a key with a plot id as its value to a dictionary
136        """
137        self.color_dict[id] = color
138
139    def on_batch_selection(self, flag):
140        """
141        switch the the notebook of batch mode or not
142        """
143        self.batch_on = flag
144        if self.fit_panel is not None:
145            self.fit_panel.batch_on = self.batch_on
146
147    def populate_menu(self, owner):
148        """
149        Create a menu for the Fitting plug-in
150
151        :param id: id to create a menu
152        :param owner: owner of menu
153
154        :return: list of information to populate the main menu
155
156        """
157        #Menu for fitting
158        self.menu1 = wx.Menu()
159        id1 = wx.NewId()
160        simul_help = "Add new fit panel"
161        self.menu1.Append(id1, '&New Fit Page', simul_help)
162        wx.EVT_MENU(owner, id1, self.on_add_new_page)
163        self.menu1.AppendSeparator()
164        self.id_simfit = wx.NewId()
165        simul_help = "Constrained or Simultaneous Fit"
166        self.menu1.Append(self.id_simfit, '&Constrained or Simultaneous Fit', simul_help)
167        wx.EVT_MENU(owner, self.id_simfit, self.on_add_sim_page)
168        self.sim_menu = self.menu1.FindItemById(self.id_simfit)
169        self.sim_menu.Enable(False)
170        #combined Batch
171        self.id_batchfit = wx.NewId()
172        batch_help = "Combined Batch"
173        self.menu1.Append(self.id_batchfit, '&Combine Batch Fit', batch_help)
174        wx.EVT_MENU(owner, self.id_batchfit, self.on_add_sim_page)
175        self.batch_menu = self.menu1.FindItemById(self.id_batchfit)
176        self.batch_menu.Enable(False)
177
178        self.menu1.AppendSeparator()
179        self.id_bumps_options = wx.NewId()
180        bopts_help = "Fitting options"
181        self.menu1.Append(self.id_bumps_options, 'Fit &Options', bopts_help)
182        wx.EVT_MENU(owner, self.id_bumps_options, self.on_bumps_options)
183        self.bumps_options_menu = self.menu1.FindItemById(self.id_bumps_options)
184        self.bumps_options_menu.Enable(True)
185
186        self.id_result_panel = wx.NewId()
187        self.menu1.Append(self.id_result_panel, "Fit Results", "Show fit results panel")
188        wx.EVT_MENU(owner, self.id_result_panel, lambda ev: self.result_frame.Show())
189        self.menu1.AppendSeparator()
190
191        self.id_reset_flag = wx.NewId()
192        resetf_help = "BatchFit: If checked, the initial param values will be "
193        resetf_help += "propagated from the previous results. "
194        resetf_help += "Otherwise, the same initial param values will be used "
195        resetf_help += "for all fittings."
196        self.menu1.AppendCheckItem(self.id_reset_flag,
197                                   "Chain Fitting [BatchFit Only]",
198                                   resetf_help)
199        wx.EVT_MENU(owner, self.id_reset_flag, self.on_reset_batch_flag)
200        chain_menu = self.menu1.FindItemById(self.id_reset_flag)
201        chain_menu.Check(not self.batch_reset_flag)
202        chain_menu.Enable(self.batch_on)
203
204        self.menu1.AppendSeparator()
205        self.edit_model_menu = wx.Menu()
206        # Find and put files name in menu
207        try:
208            self.set_edit_menu(owner=owner)
209        except:
210            raise
211
212        self.id_edit = wx.NewId()
213        editmodel_help = "Edit customized model sample file"
214        self.menu1.AppendMenu(self.id_edit, "Edit Custom Model",
215                              self.edit_model_menu, editmodel_help)
216        #create  menubar items
217        return [(self.menu1, self.sub_menu)]
218
219    def edit_custom_model(self, event):
220        """
221        Get the python editor panel
222        """
223        id = event.GetId()
224        label = self.edit_menu.GetLabel(id)
225        from sas.perspectives.calculator.pyconsole import PyConsole
226        filename = os.path.join(models.find_plugins_dir(), label)
227        frame = PyConsole(parent=self.parent, manager=self,
228                          panel=self.fit_panel,
229                          title='Advanced Custom Model Editor',
230                          filename=filename)
231        self.put_icon(frame)
232        frame.Show(True)
233
234    def delete_custom_model(self, event):
235        """
236        Delete custom model file
237        """
238        id = event.GetId()
239        label = self.delete_menu.GetLabel(id)
240        toks = os.path.splitext(label)
241        path = os.path.join(models.find_plugins_dir(), toks[0])
242        try:
243            for ext in ['.py', '.pyc']:
244                p_path = path + ext
245                os.remove(p_path)
246            self.update_custom_combo()
247            if os.path.isfile(p_path):
248                msg = "Sorry! Could not be able to delete the default "
249                msg += "custom model... \n"
250                msg += "Please manually remove the files (.py, .pyc) "
251                msg += "in the 'plugin_models' folder \n"
252                msg += "inside of the SasView application, "
253                msg += "and try it again."
254                wx.MessageBox(msg, 'Info')
255                #wx.PostEvent(self.parent, StatusEvent(status=msg, type='stop',
256                #                                      info='warning'))
257            else:
258                self.delete_menu.Delete(id)
259                for item in self.edit_menu.GetMenuItems():
260                    if item.GetLabel() == label:
261                        self.edit_menu.DeleteItem(item)
262                        msg = "The custom model, %s, has been deleted." % label
263                        wx.PostEvent(self.parent, StatusEvent(status=msg,
264                                                type='stop', info='info'))
265                        break
266        except:
267            msg = 'Delete Error: \nCould not delete the file; Check if in use.'
268            wx.MessageBox(msg, 'Error')
269
270    def make_sum_model(self, event):
271        """
272        Edit summodel template and make one
273        """
274        id = event.GetId()
275        model_manager = models.ModelManager()
276        model_list = model_manager.get_model_name_list()
277        plug_dir = models.find_plugins_dir()
278        textdial = TextDialog(None, self, -1, 'Easy Sum/Multi(p1, p2) Editor',
279                              model_list, plug_dir)
280        self.put_icon(textdial)
281        textdial.ShowModal()
282        textdial.Destroy()
283
284    def make_new_model(self, event):
285        """
286        Make new model
287        """
288        if self.new_model_frame != None:
289            self.new_model_frame.Show(False)
290            self.new_model_frame.Show(True)
291        else:
292            id = event.GetId()
293            dir_path = models.find_plugins_dir()
294            title = "New Custom Model Function"
295            self.new_model_frame = EditorWindow(parent=self, base=self,
296                                                path=dir_path, title=title)
297            self.put_icon(self.new_model_frame)
298        self.new_model_frame.Show(True)
299
300    def update_custom_combo(self):
301        """
302        Update custom model list in the fitpage combo box
303        """
304        custom_model = 'Customized Models'
305        try:
306            # Update edit menus
307            self.set_edit_menu_helper(self.parent, self.edit_custom_model)
308            self.set_edit_menu_helper(self.parent, self.delete_custom_model)
309            temp = self.fit_panel.reset_pmodel_list()
310            if temp:
311                # Set the new custom model list for all fit pages
312                for uid, page in self.fit_panel.opened_pages.iteritems():
313                    if hasattr(page, "formfactorbox"):
314                        page.model_list_box = temp
315                        current_val = page.formfactorbox.GetLabel()
316                        #if page.plugin_rbutton.GetValue():
317                        mod_cat = page.categorybox.GetStringSelection()
318                        if mod_cat == custom_model:
319                            #pos = page.formfactorbox.GetSelection()
320                            page._show_combox_helper()
321                            new_val = page.formfactorbox.GetLabel()
322                            if current_val != new_val and new_val != '':
323                                page.formfactorbox.SetLabel(new_val)
324                            else:
325                                page.formfactorbox.SetLabel(current_val)
326        except:
327            pass
328
329
330    def set_edit_menu(self, owner):
331        """
332        Set list of the edit model menu labels
333        """
334        id = wx.NewId()
335        #new_model_menu = wx.Menu()
336        self.edit_model_menu.Append(id, 'New',
337                                   'Add a new model function')
338        wx.EVT_MENU(owner, id, self.make_new_model)
339        id = wx.NewId()
340        self.edit_model_menu.Append(id, 'Sum|Multi(p1, p2)',
341                                    'Sum of two model functions')
342        wx.EVT_MENU(owner, id, self.make_sum_model)
343        e_id = wx.NewId()
344        self.edit_menu = wx.Menu()
345        self.edit_model_menu.AppendMenu(e_id,
346                                    'Advanced', self.edit_menu)
347        self.set_edit_menu_helper(owner, self.edit_custom_model)
348
349        d_id = wx.NewId()
350        self.delete_menu = wx.Menu()
351        self.edit_model_menu.AppendMenu(d_id,
352                                        'Delete', self.delete_menu)
353        self.set_edit_menu_helper(owner, self.delete_custom_model)
354
355    def set_edit_menu_helper(self, owner=None, menu=None):
356        """
357        help for setting list of the edit model menu labels
358        """
359        if menu == None:
360            menu = self.edit_custom_model
361        list_fnames = os.listdir(models.find_plugins_dir())
362        list_fnames.sort()
363        for f_item in list_fnames:
364            name = os.path.basename(f_item)
365            toks = os.path.splitext(name)
366            if toks[-1] == '.py' and not toks[0] == '__init__':
367                if menu == self.edit_custom_model:
368                    if toks[0] == 'easy_sum_of_p1_p2':
369                        continue
370                    submenu = self.edit_menu
371                else:
372                    submenu = self.delete_menu
373                has_file = False
374                for item in submenu.GetMenuItems():
375                    if name == submenu.GetLabel(item.GetId()):
376                        has_file = True
377                if not has_file:
378                    id = wx.NewId()
379                    submenu.Append(id, name)
380                    wx.EVT_MENU(owner, id, menu)
381                    has_file = False
382
383    def put_icon(self, frame):
384        """
385        Put icon in the frame title bar
386        """
387        if hasattr(frame, "IsIconized"):
388            if not frame.IsIconized():
389                try:
390                    icon = self.parent.GetIcon()
391                    frame.SetIcon(icon)
392                except:
393                    pass
394
395    def on_add_sim_page(self, event):
396        """
397        Create a page to access simultaneous fit option
398        """
399        id = event.GetId()
400        caption = "Const & Simul Fit"
401        page = self.sim_page
402        if id == self.id_batchfit:
403            caption = "Combined Batch"
404            page = self.batch_page
405
406        def set_focus_page(page):
407            page.Show(True)
408            page.Refresh()
409            page.SetFocus()
410            #self.parent._mgr.Update()
411            msg = "%s already opened\n" % str(page.window_caption)
412            wx.PostEvent(self.parent, StatusEvent(status=msg))
413
414        if page != None:
415            return set_focus_page(page)
416        if caption == "Const & Simul Fit":
417            self.sim_page = self.fit_panel.add_sim_page(caption=caption)
418        else:
419            self.batch_page = self.fit_panel.add_sim_page(caption=caption)
420
421    def get_context_menu(self, plotpanel=None):
422        """
423        Get the context menu items available for P(r).them allow fitting option
424        for Data2D and Data1D only.
425
426        :param graph: the Graph object to which we attach the context menu
427
428        :return: a list of menu items with call-back function
429
430        :note: if Data1D was generated from Theory1D
431                the fitting option is not allowed
432
433        """
434        self.plot_panel = plotpanel
435        graph = plotpanel.graph
436        fit_option = "Select data for fitting"
437        fit_hint = "Dialog with fitting parameters "
438
439        if graph.selected_plottable not in plotpanel.plots:
440            return []
441        item = plotpanel.plots[graph.selected_plottable]
442        if item.__class__.__name__ is "Data2D":
443            if hasattr(item, "is_data"):
444                if item.is_data:
445                    return [[fit_option, fit_hint, self._onSelect]]
446                else:
447                    return []
448            return [[fit_option, fit_hint, self._onSelect]]
449        else:
450
451            # if is_data is true , this in an actual data loaded
452            #else it is a data created from a theory model
453            if hasattr(item, "is_data"):
454                if item.is_data:
455                    return [[fit_option, fit_hint, self._onSelect]]
456                else:
457                    return []
458            return [[fit_option, fit_hint, self._onSelect]]
459        return []
460
461    def get_panels(self, parent):
462        """
463        Create and return a list of panel objects
464        """
465        self.parent = parent
466        #self.parent.Bind(EVT_FITSTATE_UPDATE, self.on_set_state_helper)
467        # Creation of the fit panel
468        self.frame = MDIFrame(self.parent, None, 'None', (100, 200))
469        self.fit_panel = FitPanel(parent=self.frame, manager=self)
470        self.frame.set_panel(self.fit_panel)
471        self._frame_set_helper()
472        self.on_add_new_page(event=None)
473        #Set the manager for the main panel
474        self.fit_panel.set_manager(self)
475        # List of windows used for the perspective
476        self.perspective = []
477        self.perspective.append(self.fit_panel.window_name)
478
479        self.result_frame = MDIFrame(self.parent, None, ResultPanel.window_caption, (220, 200))
480        self.result_panel = ResultPanel(parent=self.result_frame, manager=self)
481        self.perspective.append(self.result_panel.window_name)
482
483        #index number to create random model name
484        self.index_model = 0
485        self.index_theory = 0
486        self.parent.Bind(EVT_SLICER_PANEL, self._on_slicer_event)
487        self.parent.Bind(EVT_SLICER_PARS_UPDATE, self._onEVT_SLICER_PANEL)
488        #self.parent._mgr.Bind(wx.aui.EVT_AUI_PANE_CLOSE,self._onclearslicer)
489        #Create reader when fitting panel are created
490        self.state_reader = Reader(self.set_state)
491        #append that reader to list of available reader
492        loader = Loader()
493        loader.associate_file_reader(".fitv", self.state_reader)
494        #Send the fitting panel to guiframe
495        self.mypanels.append(self.fit_panel)
496        self.mypanels.append(self.result_panel)
497        return self.mypanels
498
499    def clear_panel(self):
500        """
501        """
502        self.fit_panel.clear_panel()
503
504    def delete_data(self, data):
505        """
506        delete  the given data from panel
507        """
508        self.fit_panel.delete_data(data)
509
510    def set_data(self, data_list=None):
511        """
512        receive a list of data to fit
513        """
514        if data_list is None:
515            data_list = []
516        selected_data_list = []
517        if self.batch_on:
518            page = self.add_fit_page(data=data_list)
519        else:
520            if len(data_list) > MAX_NBR_DATA:
521                from fitting_widgets import DataDialog
522                dlg = DataDialog(data_list=data_list, nb_data=MAX_NBR_DATA)
523                if dlg.ShowModal() == wx.ID_OK:
524                    selected_data_list = dlg.get_data()
525                dlg.Destroy()
526
527            else:
528                selected_data_list = data_list
529            try:
530                group_id = wx.NewId()
531                for data in selected_data_list:
532                    if data is not None:
533                        # 2D has no same group_id
534                        if data.__class__.__name__ == 'Data2D':
535                            group_id = wx.NewId()
536                        data.group_id = group_id
537                        if group_id not in data.list_group_id:
538                            data.list_group_id.append(group_id)
539                        page = self.add_fit_page(data=[data])
540            except:
541                msg = "Fitting Set_data: " + str(sys.exc_value)
542                wx.PostEvent(self.parent, StatusEvent(status=msg, info="error"))
543
544    def set_theory(self, theory_list=None):
545        """
546        """
547        #set the model state for a given theory_state:
548        for item in theory_list:
549            try:
550                _, theory_state = item
551                self.fit_panel.set_model_state(theory_state)
552            except:
553                msg = "Fitting: cannot deal with the theory received"
554                logging.error("set_theory " + msg + "\n" + str(sys.exc_value))
555                wx.PostEvent(self.parent,
556                             StatusEvent(status=msg, info="error"))
557
558    def set_state(self, state=None, datainfo=None, format=None):
559        """
560        Call-back method for the fit page state reader.
561        This method is called when a .fitv/.svs file is loaded.
562
563        : param state: PageState object
564        : param datainfo: data
565        """
566        #state = self.state_reader.get_state()
567        if state != None:
568            state = state.clone()
569            # store fitting state in temp_state
570            self.temp_state.append(state)
571        else:
572            self.temp_state = []
573        # index to start with for a new set_state
574        self.state_index = 0
575        # state file format
576        self.sfile_ext = format
577
578        self.on_set_state_helper(event=None)
579
580    def  on_set_state_helper(self, event=None):
581        """
582        Set_state_helper. This actually sets state
583        after plotting data from state file.
584
585        : event: FitStateUpdateEvent called
586            by dataloader.plot_data from guiframe
587        """
588        if len(self.temp_state) == 0:
589            if self.state_index == 0 and len(self.mypanels) <= 0 \
590            and self.sfile_ext == '.svs':
591                self.temp_state = []
592                self.state_index = 0
593            return
594
595        try:
596            # Load fitting state
597            state = self.temp_state[self.state_index]
598            #panel state should have model selection to set_state
599            if state.formfactorcombobox != None:
600                #set state
601                data = self.parent.create_gui_data(state.data)
602                data.group_id = state.data.group_id
603                self.parent.add_data(data_list={data.id: data})
604                wx.PostEvent(self.parent, NewPlotEvent(plot=data,
605                                        title=data.title))
606                #need to be fix later make sure we are sendind guiframe.data
607                #to panel
608                state.data = data
609                page = self.fit_panel.set_state(state)
610            else:
611                #just set data because set_state won't work
612                data = self.parent.create_gui_data(state.data)
613                data.group_id = state.data.group_id
614                self.parent.add_data(data_list={data.id: data})
615                wx.PostEvent(self.parent, NewPlotEvent(plot=data,
616                                        title=data.title))
617                page = self.add_fit_page([data])
618                caption = page.window_caption
619                self.store_data(uid=page.uid, data_list=page.get_data_list(),
620                        caption=caption)
621                self.mypanels.append(page)
622
623            # get ready for the next set_state
624            self.state_index += 1
625
626            #reset state variables to default when all set_state is finished.
627            if len(self.temp_state) == self.state_index:
628
629                self.temp_state = []
630                #self.state_index = 0
631                # Make sure the user sees the fitting panel after loading
632                #self.parent.set_perspective(self.perspective)
633                self.on_perspective(event=None)
634        except:
635            self.state_index = 0
636            self.temp_state = []
637            raise
638
639    def set_param2fit(self, uid, param2fit):
640        """
641        Set the list of param names to fit for fitprobelm
642        """
643        self.page_finder[uid].set_param2fit(param2fit)
644
645    def set_graph_id(self, uid, graph_id):
646        """
647        Set graph_id for fitprobelm
648        """
649        self.page_finder[uid].set_graph_id(graph_id)
650
651    def get_graph_id(self, uid):
652        """
653        Set graph_id for fitprobelm
654        """
655        return self.page_finder[uid].get_graph_id()
656
657    def save_fit_state(self, filepath, fitstate):
658        """
659        save fit page state into file
660        """
661        self.state_reader.write(filename=filepath, fitstate=fitstate)
662
663    def set_fit_weight(self, uid, flag, is2d=False, fid=None):
664        """
665        Set the fit weights of a given page for all
666        its data by default. If fid is provide then set the range
667        only for the data with fid as id
668        :param uid: id corresponding to a fit page
669        :param fid: id corresponding to a fit problem (data, model)
670        :param weight: current dy data
671        """
672        if uid in self.page_finder.keys():
673            self.page_finder[uid].set_weight(flag=flag, is2d=is2d)
674
675    def set_fit_range(self, uid, qmin, qmax, fid=None):
676        """
677        Set the fitting range of a given page for all
678        its data by default. If fid is provide then set the range
679        only for the data with fid as id
680        :param uid: id corresponding to a fit page
681        :param fid: id corresponding to a fit problem (data, model)
682        :param qmin: minimum  value of the fit range
683        :param qmax: maximum  value of the fit range
684        """
685        if uid in self.page_finder.keys():
686            self.page_finder[uid].set_range(qmin=qmin, qmax=qmax, fid=fid)
687
688    def schedule_for_fit(self, value=0, uid=None):
689        """
690        Set the fit problem field to 0 or 1 to schedule that problem to fit.
691        Schedule the specified fitproblem or get the fit problem related to
692        the current page and set value.
693        :param value: integer 0 or 1
694        :param uid: the id related to a page contaning fitting information
695        """
696        if uid in self.page_finder.keys():
697            self.page_finder[uid].schedule_tofit(value)
698
699    def get_page_finder(self):
700        """
701        return self.page_finder used also by simfitpage.py
702        """
703        return self.page_finder
704
705    def set_page_finder(self, modelname, names, values):
706        """
707        Used by simfitpage.py to reset a parameter given the string constrainst.
708
709        :param modelname: the name ot the model for with the parameter
710                            has to reset
711        :param value: can be a string in this case.
712        :param names: the paramter name
713        """
714        sim_page_id = self.sim_page.uid
715        for uid, value in self.page_finder.iteritems():
716            if uid != sim_page_id and uid != self.batch_page.uid:
717                list = value.get_model()
718                model = list[0]
719                if model.name == modelname:
720                    value.set_model_param(names, values)
721                    break
722
723    def split_string(self, item):
724        """
725        receive a word containing dot and split it. used to split parameterset
726        name into model name and parameter name example: ::
727
728            paramaterset (item) = M1.A
729            Will return model_name = M1 , parameter name = A
730
731        """
732        if item.find(".") >= 0:
733            param_names = re.split("\.", item)
734            model_name = param_names[0]
735            ##Assume max len is 3; eg., M0.radius.width
736            if len(param_names) == 3:
737                param_name = param_names[1] + "." + param_names[2]
738            else:
739                param_name = param_names[1]
740            return model_name, param_name
741
742    def on_bumps_options(self, event=None):
743        from bumps.gui.fit_dialog import OpenFitOptions
744        OpenFitOptions()
745
746    def stop_fit(self, uid):
747        """
748        Stop the fit
749        """
750        if uid in self.fit_thread_list.keys():
751            calc_fit = self.fit_thread_list[uid]
752            if calc_fit is not  None and calc_fit.isrunning():
753                calc_fit.stop()
754                msg = "Fit stop!"
755                wx.PostEvent(self.parent, StatusEvent(status=msg, type="stop"))
756            del self.fit_thread_list[uid]
757        #set the fit button label of page when fit stop is trigger from
758        #simultaneous fit pane
759        sim_flag = self.sim_page is not None and uid == self.sim_page.uid
760        batch_flag = self.batch_page is not None and uid == self.batch_page.uid
761        if sim_flag or batch_flag:
762            for uid, value in self.page_finder.iteritems():
763                if value.get_scheduled() == 1:
764                    if uid in self.fit_panel.opened_pages.keys():
765                        panel = self.fit_panel.opened_pages[uid]
766                        panel._on_fit_complete()
767
768    def set_smearer(self, uid, smearer, fid, qmin=None, qmax=None, draw=True,
769                    enable_smearer=False):
770        """
771        Get a smear object and store it to a fit problem of fid as id. If proper
772        flag is enable , will plot the theory with smearing information.
773
774        :param smearer: smear object to allow smearing data of id fid
775        :param enable_smearer: Define whether or not all (data, model) contained
776            in the structure of id uid will be smeared before fitting.
777        :param qmin: the maximum value of the theory plotting range
778        :param qmax: the maximum value of the theory plotting range
779        :param draw: Determine if the theory needs to be plot
780        """
781        if uid not in self.page_finder.keys():
782            return
783        self.page_finder[uid].enable_smearing(flag=enable_smearer)
784        self.page_finder[uid].set_smearer(smearer, fid=fid)
785        if draw:
786            ## draw model 1D with smeared data
787            data = self.page_finder[uid].get_fit_data(fid=fid)
788            if data is None:
789                msg = "set_mearer requires at least data.\n"
790                msg += "Got data = %s .\n" % str(data)
791                return
792                #raise ValueError, msg
793            model = self.page_finder[uid].get_model(fid=fid)
794            if model is None:
795                return
796            enable1D = issubclass(data.__class__, Data1D)
797            enable2D = issubclass(data.__class__, Data2D)
798            ## if user has already selected a model to plot
799            ## redraw the model with data smeared
800            smear = self.page_finder[uid].get_smearer(fid=fid)
801
802            # compute weight for the current data
803            weight = self.page_finder[uid].get_weight(fid=fid)
804
805            self.draw_model(model=model, data=data, page_id=uid, smearer=smear,
806                enable1D=enable1D, enable2D=enable2D,
807                qmin=qmin, qmax=qmax, weight=weight)
808            self._mac_sleep(0.2)
809
810    def _mac_sleep(self, sec=0.2):
811        """
812        Give sleep to MAC
813        """
814        if ON_MAC:
815            time.sleep(sec)
816
817    def draw_model(self, model, page_id, data=None, smearer=None,
818                   enable1D=True, enable2D=False,
819                   state=None,
820                   fid=None,
821                   toggle_mode_on=False,
822                   qmin=None, qmax=None,
823                   update_chisqr=True, weight=None, source='model'):
824        """
825        Draw model.
826
827        :param model: the model to draw
828        :param name: the name of the model to draw
829        :param data: the data on which the model is based to be drawn
830        :param description: model's description
831        :param enable1D: if true enable drawing model 1D
832        :param enable2D: if true enable drawing model 2D
833        :param qmin:  Range's minimum value to draw model
834        :param qmax:  Range's maximum value to draw model
835        :param qstep: number of step to divide the x and y-axis
836        :param update_chisqr: update chisqr [bool]
837
838        """
839        #self.weight = weight
840        if issubclass(data.__class__, Data1D) or not enable2D:
841            ## draw model 1D with no loaded data
842            self._draw_model1D(model=model,
843                               data=data,
844                               page_id=page_id,
845                               enable1D=enable1D,
846                               smearer=smearer,
847                               qmin=qmin,
848                               qmax=qmax,
849                               fid=fid,
850                               weight=weight,
851                               toggle_mode_on=toggle_mode_on,
852                               state=state,
853                               update_chisqr=update_chisqr,
854                               source=source)
855        else:
856            ## draw model 2D with no initial data
857            self._draw_model2D(model=model,
858                                page_id=page_id,
859                                data=data,
860                                enable2D=enable2D,
861                                smearer=smearer,
862                                qmin=qmin,
863                                qmax=qmax,
864                                fid=fid,
865                                weight=weight,
866                                state=state,
867                                toggle_mode_on=toggle_mode_on,
868                                update_chisqr=update_chisqr,
869                                source=source)
870
871    def onFit(self, uid):
872        """
873        Get series of data, model, associates parameters and range and send then
874        to  series of fitters. Fit data and model, display result to
875        corresponding panels.
876        :param uid: id related to the panel currently calling this fit function.
877        """
878        if uid is None: raise RuntimeError("no page to fit") # Should never happen
879
880        sim_page_uid = getattr(self.sim_page, 'uid', None)
881        batch_page_uid = getattr(self.batch_page, 'uid', None)
882
883        if uid == sim_page_uid:
884            fit_type = 'simultaneous'
885        elif uid == batch_page_uid:
886            fit_type = 'combined_batch'
887        else:
888            fit_type = 'single'
889
890        fitter_list = []
891        sim_fitter = None
892        if fit_type == 'simultaneous':
893            # for simultaneous fitting only one fitter is needed
894            sim_fitter = Fit()
895            sim_fitter.fitter_id = self.sim_page.uid
896            fitter_list.append(sim_fitter)
897
898        self.current_pg = None
899        list_page_id = []
900        fit_id = 0
901        for page_id, page_info in self.page_finder.iteritems():
902            # For simulfit (uid give with None), do for-loop
903            # if uid is specified (singlefit), do it only on the page.
904            if page_id in (sim_page_uid, batch_page_uid): continue
905            if fit_type == "single" and page_id != uid: continue
906
907            try:
908                if page_info.get_scheduled() == 1:
909                    page_info.nbr_residuals_computed = 0
910                    page = self.fit_panel.get_page_by_id(page_id)
911                    self.set_fit_weight(uid=page.uid,
912                                     flag=page.get_weight_flag(),
913                                     is2d=page._is_2D())
914                    if not page.param_toFit:
915                        msg = "No fitting parameters for %s" % page.window_caption
916                        wx.PostEvent(page.parent.parent,
917                                     StatusEvent(status=msg, info="error",
918                                                 type="stop"))
919                        return False
920                    if not page._update_paramv_on_fit():
921                        msg = "Fitting range or parameter values are"
922                        msg += " invalid in %s" % \
923                                    page.window_caption
924                        wx.PostEvent(page.parent.parent,
925                                     StatusEvent(status=msg, info="error",
926                                     type="stop"))
927                        return False
928
929                    pars = [str(element[1]) for element in page.param_toFit]
930                    fitproblem_list = page_info.values()
931                    for fitproblem in  fitproblem_list:
932                        if sim_fitter is None:
933                            fitter = Fit()
934                            fitter.fitter_id = page_id
935                            fitter_list.append(fitter)
936                        else:
937                            fitter = sim_fitter
938                        self._add_problem_to_fit(fitproblem=fitproblem,
939                                             pars=pars,
940                                             fitter=fitter,
941                                             fit_id=fit_id)
942                        fit_id += 1
943                    list_page_id.append(page_id)
944                    page_info.clear_model_param()
945            except KeyboardInterrupt:
946                msg = "Fitting terminated"
947                wx.PostEvent(self.parent, StatusEvent(status=msg, info="info",
948                                                      type="stop"))
949                return True
950            except:
951                raise
952                msg = "Fitting error: %s" % str(sys.exc_value)
953                wx.PostEvent(self.parent, StatusEvent(status=msg, info="error",
954                                                      type="stop"))
955                return False
956        ## If a thread is already started, stop it
957        #if self.calc_fit!= None and self.calc_fit.isrunning():
958        #    self.calc_fit.stop()
959        msg = "Fitting is in progress..."
960        wx.PostEvent(self.parent, StatusEvent(status=msg, type="progress"))
961
962        #Handler used to display fit message
963        handler = ConsoleUpdate(parent=self.parent,
964                                manager=self,
965                                improvement_delta=0.1)
966        self._mac_sleep(0.2)
967
968        # batch fit
969        batch_inputs = {}
970        batch_outputs = {}
971        if fit_type == "simultaneous":
972            page = self.sim_page
973        elif fit_type == "combined_batch":
974            page = self.batch_page
975        else:
976            page = self.fit_panel.get_page_by_id(uid)
977        if page.batch_on:
978            calc_fit = FitThread(handler=handler,
979                                 fn=fitter_list,
980                                 pars=pars,
981                                 batch_inputs=batch_inputs,
982                                 batch_outputs=batch_outputs,
983                                 page_id=list_page_id,
984                                 completefn=self._batch_fit_complete,
985                                 reset_flag=self.batch_reset_flag)
986        else:
987            ## Perform more than 1 fit at the time
988            calc_fit = FitThread(handler=handler,
989                                    fn=fitter_list,
990                                    batch_inputs=batch_inputs,
991                                    batch_outputs=batch_outputs,
992                                    page_id=list_page_id,
993                                    updatefn=handler.update_fit,
994                                    completefn=self._fit_completed)
995        #self.fit_thread_list[current_page_id] = calc_fit
996        self.fit_thread_list[uid] = calc_fit
997        calc_fit.queue()
998        calc_fit.ready(2.5)
999        msg = "Fitting is in progress..."
1000        wx.PostEvent(self.parent, StatusEvent(status=msg, type="progress"))
1001
1002        return True
1003
1004    def remove_plot(self, uid, fid=None, theory=False):
1005        """
1006        remove model plot when a fit page is closed
1007        :param uid: the id related to the fitpage to close
1008        :param fid: the id of the fitproblem(data, model, range,etc)
1009        """
1010        if uid not in self.page_finder.keys():
1011            return
1012        fitproblemList = self.page_finder[uid].get_fit_problem(fid)
1013        for fitproblem in fitproblemList:
1014            data = fitproblem.get_fit_data()
1015            model = fitproblem.get_model()
1016            plot_id = None
1017            if model is not None:
1018                plot_id = data.id + model.name
1019            if theory:
1020                plot_id = data.id + model.name
1021            group_id = data.group_id
1022            wx.PostEvent(self.parent, NewPlotEvent(id=plot_id,
1023                                                   group_id=group_id,
1024                                                   action='remove'))
1025
1026    def store_data(self, uid, data_list=None, caption=None):
1027        """
1028        Recieve a list of data and store them ans well as a caption of
1029        the fit page where they come from.
1030        :param uid: if related to a fit page
1031        :param data_list: list of data to fit
1032        :param caption: caption of the window related to these data
1033        """
1034        if data_list is None:
1035            data_list = []
1036
1037        self.page_finder[uid].set_fit_data(data=data_list)
1038        if caption is not None:
1039            self.page_finder[uid].set_fit_tab_caption(caption=caption)
1040
1041    def on_add_new_page(self, event=None):
1042        """
1043        ask fit panel to create a new empty page
1044        """
1045        try:
1046            page = self.fit_panel.add_empty_page()
1047            # add data associated to the page created
1048            if page != None:
1049                wx.PostEvent(self.parent, StatusEvent(status="Page Created",
1050                                               info="info"))
1051            else:
1052                msg = "Page was already Created"
1053                wx.PostEvent(self.parent, StatusEvent(status=msg,
1054                                                       info="warning"))
1055        except:
1056            msg = "Creating Fit page: %s" % sys.exc_value
1057            wx.PostEvent(self.parent, StatusEvent(status=msg, info="error"))
1058
1059    def add_fit_page(self, data):
1060        """
1061        given a data, ask to the fitting panel to create a new fitting page,
1062        get this page and store it into the page_finder of this plug-in
1063        :param data: is a list of data
1064        """
1065        page = self.fit_panel.set_data(data)
1066        # page could be None when loading state files
1067        if page == None:
1068            return page
1069        #append Data1D to the panel containing its theory
1070        #if theory already plotted
1071        if page.uid in self.page_finder:
1072            data = page.get_data()
1073            theory_data = self.page_finder[page.uid].get_theory_data(data.id)
1074            if issubclass(data.__class__, Data2D):
1075                data.group_id = wx.NewId()
1076                if theory_data is not None:
1077                    group_id = str(page.uid) + " Model1D"
1078                    wx.PostEvent(self.parent,
1079                             NewPlotEvent(group_id=group_id,
1080                                               action="delete"))
1081                    self.parent.update_data(prev_data=theory_data,
1082                                             new_data=data)
1083            else:
1084                if theory_data is not None:
1085                    group_id = str(page.uid) + " Model2D"
1086                    data.group_id = theory_data.group_id
1087                    wx.PostEvent(self.parent,
1088                             NewPlotEvent(group_id=group_id,
1089                                               action="delete"))
1090                    self.parent.update_data(prev_data=theory_data,
1091                                             new_data=data)
1092        self.store_data(uid=page.uid, data_list=page.get_data_list(),
1093                        caption=page.window_caption)
1094        if self.sim_page is not None and not self.batch_on:
1095            self.sim_page.draw_page()
1096        if self.batch_page is not None and self.batch_on:
1097            self.batch_page.draw_page()
1098
1099        return page
1100
1101    def _onEVT_SLICER_PANEL(self, event):
1102        """
1103        receive and event telling to update a panel with a name starting with
1104        event.panel_name. this method update slicer panel
1105        for a given interactor.
1106
1107        :param event: contains type of slicer , paramaters for updating
1108            the panel and panel_name to find the slicer 's panel concerned.
1109        """
1110        event.panel_name
1111        for item in self.parent.panels:
1112            name = event.panel_name
1113            if self.parent.panels[item].window_caption.startswith(name):
1114                self.parent.panels[item].set_slicer(event.type, event.params)
1115
1116        #self.parent._mgr.Update()
1117
1118    def _closed_fitpage(self, event):
1119        """
1120        request fitpanel to close a given page when its unique data is removed
1121        from the plot. close fitpage only when the a loaded data is removed
1122        """
1123        if event is None or event.data is None:
1124            return
1125        if hasattr(event.data, "is_data"):
1126            if not event.data.is_data or \
1127                event.data.__class__.__name__ == "Data1D":
1128                self.fit_panel.close_page_with_data(event.data)
1129
1130    def _reset_schedule_problem(self, value=0, uid=None):
1131        """
1132        unschedule or schedule all fitproblem to be fit
1133        """
1134        # case that uid is not specified
1135        if uid == None:
1136            for page_id in self.page_finder.keys():
1137                self.page_finder[page_id].schedule_tofit(value)
1138        # when uid is given
1139        else:
1140            if uid in self.page_finder.keys():
1141                self.page_finder[uid].schedule_tofit(value)
1142
1143    def _add_problem_to_fit(self, fitproblem, pars, fitter, fit_id):
1144        """
1145        Create and set fitter with series of data and model
1146        """
1147        data = fitproblem.get_fit_data()
1148        model = fitproblem.get_model()
1149        smearer = fitproblem.get_smearer()
1150        qmin, qmax = fitproblem.get_range()
1151
1152        #Extra list of parameters and their constraints
1153        listOfConstraint = []
1154        param = fitproblem.get_model_param()
1155        if len(param) > 0:
1156            for item in param:
1157                ## check if constraint
1158                if item[0] != None and item[1] != None:
1159                    listOfConstraint.append((item[0], item[1]))
1160        new_model = model
1161        fitter.set_model(new_model, fit_id, pars, data=data,
1162                         constraints=listOfConstraint)
1163        fitter.set_data(data=data, id=fit_id, smearer=smearer, qmin=qmin,
1164                        qmax=qmax)
1165        fitter.select_problem_for_fit(id=fit_id, value=1)
1166
1167    def _onSelect(self, event):
1168        """
1169        when Select data to fit a new page is created .Its reference is
1170        added to self.page_finder
1171        """
1172        panel = self.plot_panel
1173        if panel == None:
1174            raise ValueError, "Fitting:_onSelect: NonType panel"
1175        Plugin.on_perspective(self, event=event)
1176        self.select_data(panel)
1177
1178    def select_data(self, panel):
1179        """
1180        """
1181        for plottable in panel.graph.plottables:
1182            if plottable.__class__.__name__ in ["Data1D", "Theory1D"]:
1183                data_id = panel.graph.selected_plottable
1184                if plottable == panel.plots[data_id]:
1185                    data = plottable
1186                    self.add_fit_page(data=[data])
1187                    return
1188            else:
1189                data = plottable
1190                self.add_fit_page(data=[data])
1191
1192    def update_fit(self, result=None, msg=""):
1193        """
1194        """
1195        print "update_fit result", result
1196
1197    def _batch_fit_complete(self, result, pars, page_id,
1198                            batch_outputs, batch_inputs, elapsed=None):
1199        """
1200        Display fit result in batch
1201        :param result: list of objects received from fitters
1202        :param pars: list of  fitted parameters names
1203        :param page_id: list of page ids which called fit function
1204        :param elapsed: time spent at the fitting level
1205        """
1206        self._mac_sleep(0.2)
1207        uid = page_id[0]
1208        if uid in self.fit_thread_list.keys():
1209            del self.fit_thread_list[uid]
1210
1211        wx.CallAfter(self._update_fit_button, page_id)
1212        t1 = time.time()
1213        str_time = time.strftime("%a, %d %b %Y %H:%M:%S ", time.localtime(t1))
1214        msg = "Fit completed on %s \n" % str_time
1215        msg += "Duration time: %s s.\n" % str(elapsed)
1216        wx.PostEvent(self.parent, StatusEvent(status=msg, info="info",
1217                                              type="stop"))
1218
1219        if batch_outputs is None:
1220            batch_outputs = {}
1221
1222        # format batch_outputs
1223        batch_outputs["Chi2"] = []
1224        #Don't like these loops
1225        # Need to create dictionary of all fitted parameters
1226        # since the number of parameters can differ between each fit result
1227        for list_res in result:
1228            for res in list_res:
1229                model, data = res.inputs[0]
1230                if model is not None and hasattr(model, "model"):
1231                    model = model.model
1232                #get all fittable parameters of the current model
1233                for param in  model.getParamList():
1234                    if param  not in batch_outputs.keys():
1235                        batch_outputs[param] = []
1236                for param in model.getDispParamList():
1237                    if not model.is_fittable(param) and \
1238                        param in batch_outputs.keys():
1239                        del batch_outputs[param]
1240                # Add fitted parameters and their error
1241                for param in res.param_list:
1242                    if param not in batch_outputs.keys():
1243                        batch_outputs[param] = []
1244                    err_param = "error on %s" % str(param)
1245                    if err_param not in batch_inputs.keys():
1246                        batch_inputs[err_param] = []
1247        msg = ""
1248        for list_res in result:
1249            for res in list_res:
1250                pid = res.fitter_id
1251                model, data = res.inputs[0]
1252                correct_result = False
1253                if model is not None and hasattr(model, "model"):
1254                    model = model.model
1255                if data is not None and hasattr(data, "sas_data"):
1256                    data = data.sas_data
1257
1258                is_data2d = issubclass(data.__class__, Data2D)
1259                #check consistency of arrays
1260                if not is_data2d:
1261                    if len(res.theory) == len(res.index[res.index]) and \
1262                        len(res.index) == len(data.y):
1263                        correct_result = True
1264                else:
1265                    copy_data = deepcopy(data)
1266                    new_theory = copy_data.data
1267                    new_theory[res.index] = res.theory
1268                    new_theory[res.index == False] = numpy.nan
1269                    correct_result = True
1270                #get all fittable parameters of the current model
1271                param_list = model.getParamList()
1272                for param in model.getDispParamList():
1273                    if not model.is_fittable(param) and \
1274                        param in param_list:
1275                        param_list.remove(param)
1276                if not correct_result or res.fitness is None or \
1277                    not numpy.isfinite(res.fitness) or \
1278                    numpy.any(res.pvec == None) or not \
1279                    numpy.all(numpy.isfinite(res.pvec)):
1280                    data_name = str(None)
1281                    if data is not None:
1282                        data_name = str(data.name)
1283                    model_name = str(None)
1284                    if model is not None:
1285                        model_name = str(model.name)
1286                    msg += "Data %s and Model %s did not fit.\n" % (data_name,
1287                                                                    model_name)
1288                    ERROR = numpy.NAN
1289                    cell = BatchCell()
1290                    cell.label = res.fitness
1291                    cell.value = res.fitness
1292                    batch_outputs["Chi2"].append(ERROR)
1293                    for param in param_list:
1294                        # save value of  fixed parameters
1295                        if param not in res.param_list:
1296                            batch_outputs[str(param)].append(ERROR)
1297                        else:
1298                            #save only fitted values
1299                            batch_outputs[param].append(ERROR)
1300                            batch_inputs["error on %s" % str(param)].append(ERROR)
1301                else:
1302                    # TODO: Why sometimes res.pvec comes with numpy.float64?
1303                    # probably from scipy lmfit
1304                    if res.pvec.__class__ == numpy.float64:
1305                        res.pvec = [res.pvec]
1306
1307                    cell = BatchCell()
1308                    cell.label = res.fitness
1309                    cell.value = res.fitness
1310                    batch_outputs["Chi2"].append(cell)
1311                    # add parameters to batch_results
1312                    for param in param_list:
1313                        # save value of  fixed parameters
1314                        if param not in res.param_list:
1315                            batch_outputs[str(param)].append(model.getParam(param))
1316                        else:
1317                            index = res.param_list.index(param)
1318                            #save only fitted values
1319                            batch_outputs[param].append(res.pvec[index])
1320                            if res.stderr is not None and \
1321                                len(res.stderr) == len(res.param_list):
1322                                item = res.stderr[index]
1323                                batch_inputs["error on %s" % param].append(item)
1324                            else:
1325                                batch_inputs["error on %s" % param].append('-')
1326                            model.setParam(param, res.pvec[index])
1327                #fill the batch result with emtpy value if not in the current
1328                #model
1329                EMPTY = "-"
1330                for key in batch_outputs.keys():
1331                    if key not in param_list and key not in ["Chi2", "Data"]:
1332                        batch_outputs[key].append(EMPTY)
1333
1334                self.page_finder[pid].set_batch_result(batch_inputs=batch_inputs,
1335                                                       batch_outputs=batch_outputs)
1336
1337                cpage = self.fit_panel.get_page_by_id(pid)
1338                cpage._on_fit_complete()
1339                self.page_finder[pid][data.id].set_result(res)
1340                fitproblem = self.page_finder[pid][data.id]
1341                qmin, qmax = fitproblem.get_range()
1342                plot_result = False
1343                if correct_result:
1344                    if not is_data2d:
1345                        self._complete1D(x=data.x[res.index], y=res.theory, page_id=pid,
1346                                         elapsed=None,
1347                                         index=res.index, model=model,
1348                                         weight=None, fid=data.id,
1349                                         toggle_mode_on=False, state=None,
1350                                         data=data, update_chisqr=False,
1351                                         source='fit', plot_result=plot_result)
1352                    else:
1353                        self._complete2D(image=new_theory, data=data,
1354                                         model=model,
1355                                         page_id=pid, elapsed=None,
1356                                         index=res.index,
1357                                         qmin=qmin,
1358                                         qmax=qmax, fid=data.id, weight=None,
1359                                         toggle_mode_on=False, state=None,
1360                                         update_chisqr=False,
1361                                         source='fit', plot_result=plot_result)
1362                self.on_set_batch_result(page_id=pid,
1363                                         fid=data.id,
1364                                         batch_outputs=batch_outputs,
1365                                         batch_inputs=batch_inputs)
1366
1367        wx.PostEvent(self.parent, StatusEvent(status=msg, error="info",
1368                                              type="stop"))
1369        # Remove parameters that are not shown
1370        cpage = self.fit_panel.get_page_by_id(uid)
1371        tbatch_outputs = {}
1372        shownkeystr = cpage.get_copy_params()
1373        for key in batch_outputs.keys():
1374            if key in ["Chi2", "Data"] or shownkeystr.count(key) > 0:
1375                tbatch_outputs[key] = batch_outputs[key]
1376
1377        wx.CallAfter(self.parent.on_set_batch_result, tbatch_outputs,
1378                     batch_inputs, self.sub_menu)
1379
1380    def on_set_batch_result(self, page_id, fid, batch_outputs, batch_inputs):
1381        """
1382        """
1383        pid = page_id
1384        if fid not in self.page_finder[pid]:
1385            return
1386        fitproblem = self.page_finder[pid][fid]
1387        index = self.page_finder[pid].nbr_residuals_computed - 1
1388        residuals = fitproblem.get_residuals()
1389        theory_data = fitproblem.get_theory_data()
1390        data = fitproblem.get_fit_data()
1391        model = fitproblem.get_model()
1392        #fill batch result information
1393        if "Data" not in batch_outputs.keys():
1394            batch_outputs["Data"] = []
1395        from sas.guiframe.data_processor import BatchCell
1396        cell = BatchCell()
1397        cell.label = data.name
1398        cell.value = index
1399
1400        if theory_data != None:
1401            #Suucessful fit
1402            theory_data.id = wx.NewId()
1403            theory_data.name = model.name + "[%s]" % str(data.name)
1404            if issubclass(theory_data.__class__, Data2D):
1405                group_id = wx.NewId()
1406                theory_data.group_id = group_id
1407                if group_id not in theory_data.list_group_id:
1408                    theory_data.list_group_id.append(group_id)
1409
1410            try:
1411                # associate residuals plot
1412                if issubclass(residuals.__class__, Data2D):
1413                    group_id = wx.NewId()
1414                    residuals.group_id = group_id
1415                    if group_id not in residuals.list_group_id:
1416                        residuals.list_group_id.append(group_id)
1417                batch_outputs["Chi2"][index].object = [residuals]
1418            except:
1419                pass
1420
1421        cell.object = [data, theory_data]
1422        batch_outputs["Data"].append(cell)
1423        for key, value in data.meta_data.iteritems():
1424            if key not in batch_inputs.keys():
1425                batch_inputs[key] = []
1426            #if key.lower().strip() != "loader":
1427            batch_inputs[key].append(value)
1428        param = "temperature"
1429        if hasattr(data.sample, param):
1430            if param not in  batch_inputs.keys():
1431                batch_inputs[param] = []
1432            batch_inputs[param].append(data.sample.temperature)
1433
1434    def _fit_completed(self, result, page_id, batch_outputs,
1435                       batch_inputs=None, pars=None, elapsed=None):
1436        """
1437        Display result of the fit on related panel(s).
1438        :param result: list of object generated when fit ends
1439        :param pars: list of names of parameters fitted
1440        :param page_id: list of page ids which called fit function
1441        :param elapsed: time spent at the fitting level
1442        """
1443        t1 = time.time()
1444        str_time = time.strftime("%a, %d %b %Y %H:%M:%S ", time.localtime(t1))
1445        msg = "Fit completed on %s \n" % str_time
1446        msg += "Duration time: %s s.\n" % str(elapsed)
1447        wx.PostEvent(self.parent, StatusEvent(status=msg, info="info",
1448                                                      type="stop"))
1449        wx.PostEvent(self.result_panel, PlotResultEvent(result=result))
1450        wx.CallAfter(self._update_fit_button, page_id)
1451        result = result[0]
1452        self.fit_thread_list = {}
1453        if page_id is None:
1454            page_id = []
1455        ## fit more than 1 model at the same time
1456        self._mac_sleep(0.2)
1457        try:
1458            index = 0
1459            for uid in page_id:
1460                res = result[index]
1461                if res.fitness is None or \
1462                    not numpy.isfinite(res.fitness) or \
1463                    numpy.any(res.pvec == None) or \
1464                    not numpy.all(numpy.isfinite(res.pvec)):
1465                    msg = "Fitting did not converge!!!"
1466                    wx.PostEvent(self.parent,
1467                             StatusEvent(status=msg,
1468                                         info="warning",
1469                                         type="stop"))
1470                    wx.CallAfter(self._update_fit_button, page_id)
1471                else:
1472                    #set the panel when fit result are float not list
1473                    if res.pvec.__class__ == numpy.float64:
1474                        pvec = [res.pvec]
1475                    else:
1476                        pvec = res.pvec
1477                    if res.stderr.__class__ == numpy.float64:
1478                        stderr = [res.stderr]
1479                    else:
1480                        stderr = res.stderr
1481                    cpage = self.fit_panel.get_page_by_id(uid)
1482                    # Make sure we got all results
1483                    #(CallAfter is important to MAC)
1484                    try:
1485                        #if res != None:
1486                        wx.CallAfter(cpage.onsetValues, res.fitness,
1487                                     res.param_list,
1488                                     pvec, stderr)
1489                        index += 1
1490                        wx.CallAfter(cpage._on_fit_complete)
1491                    except KeyboardInterrupt:
1492                        msg = "Singular point: Fitting Stoped."
1493                        wx.PostEvent(self.parent, StatusEvent(status=msg,
1494                                                              info="info",
1495                                                              type="stop"))
1496                    except:
1497                        msg = "Singular point: Fitting Error occurred."
1498                        wx.PostEvent(self.parent, StatusEvent(status=msg,
1499                                                              info="error",
1500                                                              type="stop"))
1501
1502        except:
1503            msg = "Fit completed but Following"
1504            msg += " warning occurred: %s" % sys.exc_value
1505            wx.PostEvent(self.parent, StatusEvent(status=msg, info="warning",
1506                                                  type="stop"))
1507
1508    def _update_fit_button(self, page_id):
1509        """
1510        Update Fit button when fit stopped
1511
1512        : parameter page_id: fitpage where the button is
1513        """
1514        if page_id.__class__.__name__ != 'list':
1515            page_id = [page_id]
1516        for uid in page_id:
1517            page = self.fit_panel.get_page_by_id(uid)
1518            page._on_fit_complete()
1519
1520    def _on_show_panel(self, event):
1521        """
1522        """
1523        pass
1524
1525    def on_reset_batch_flag(self, event):
1526        """
1527        Set batch_reset_flag
1528        """
1529        event.Skip()
1530        if self.menu1 == None:
1531            return
1532        menu_item = self.menu1.FindItemById(self.id_reset_flag)
1533        flag = menu_item.IsChecked()
1534        if not flag:
1535            menu_item.Check(False)
1536            self.batch_reset_flag = True
1537        else:
1538            menu_item.Check(True)
1539            self.batch_reset_flag = False
1540
1541        ## post a message to status bar
1542        msg = "Set Chain Fitting: %s" % str(not self.batch_reset_flag)
1543        wx.PostEvent(self.parent,
1544                     StatusEvent(status=msg))
1545
1546
1547    def _on_slicer_event(self, event):
1548        """
1549        Receive a panel as event and send it to guiframe
1550
1551        :param event: event containing a panel
1552
1553        """
1554        if event.panel is not None:
1555            new_panel = event.panel
1556            self.slicer_panels.append(event.panel)
1557            # Set group ID if available
1558            event_id = self.parent.popup_panel(event.panel)
1559            event.panel.uid = event_id
1560            self.mypanels.append(event.panel)
1561
1562    def _onclearslicer(self, event):
1563        """
1564        Clear the boxslicer when close the panel associate with this slicer
1565        """
1566        name = event.GetEventObject().frame.GetTitle()
1567        for panel in self.slicer_panels:
1568            if panel.window_caption == name:
1569
1570                for item in self.parent.panels:
1571                    if hasattr(self.parent.panels[item], "uid"):
1572                        if self.parent.panels[item].uid == panel.base.uid:
1573                            self.parent.panels[item].onClearSlicer(event)
1574                            #self.parent._mgr.Update()
1575                            break
1576                break
1577
1578    def _on_model_panel(self, evt):
1579        """
1580        react to model selection on any combo box or model menu.plot the model
1581
1582        :param evt: wx.combobox event
1583
1584        """
1585        model = evt.model
1586        uid = evt.uid
1587        qmin = evt.qmin
1588        qmax = evt.qmax
1589        caption = evt.caption
1590        enable_smearer = evt.enable_smearer
1591        if model == None:
1592            return
1593        if uid not in self.page_finder.keys():
1594            return
1595        # save the name containing the data name with the appropriate model
1596        self.page_finder[uid].set_model(model)
1597        self.page_finder[uid].enable_smearing(enable_smearer)
1598        self.page_finder[uid].set_range(qmin=qmin, qmax=qmax)
1599        self.page_finder[uid].set_fit_tab_caption(caption=caption)
1600        if self.sim_page is not None and not self.batch_on:
1601            self.sim_page.draw_page()
1602        if self.batch_page is not None and self.batch_on:
1603            self.batch_page.draw_page()
1604
1605    def _update1D(self, x, output):
1606        """
1607        Update the output of plotting model 1D
1608        """
1609        msg = "Plot updating ... "
1610        wx.PostEvent(self.parent, StatusEvent(status=msg, type="update"))
1611
1612    def _complete1D(self, x, y, page_id, elapsed, index, model,
1613                    weight=None, fid=None,
1614                    toggle_mode_on=False, state=None,
1615                    data=None, update_chisqr=True,
1616                    source='model', plot_result=True):
1617        """
1618        Complete plotting 1D data
1619        """
1620        try:
1621            numpy.nan_to_num(y)
1622
1623            new_plot = Data1D(x=x, y=y)
1624            new_plot.is_data = False
1625            new_plot.dy = numpy.zeros(len(y))
1626            new_plot.symbol = GUIFRAME_ID.CURVE_SYMBOL_NUM
1627            _yaxis, _yunit = data.get_yaxis()
1628            _xaxis, _xunit = data.get_xaxis()
1629            new_plot.title = data.name
1630
1631            new_plot.group_id = data.group_id
1632            if new_plot.group_id == None:
1633                new_plot.group_id = data.group_id
1634            new_plot.id = str(page_id) + " " + data.name
1635            #if new_plot.id in self.color_dict:
1636            #    new_plot.custom_color = self.color_dict[new_plot.id]
1637            #find if this theory was already plotted and replace that plot given
1638            #the same id
1639            theory_data = self.page_finder[page_id].get_theory_data(fid=data.id)
1640
1641            if data.is_data:
1642                data_name = str(data.name)
1643            else:
1644                data_name = str(model.__class__.__name__)
1645
1646            new_plot.name = model.name + " [" + data_name + "]"
1647            new_plot.xaxis(_xaxis, _xunit)
1648            new_plot.yaxis(_yaxis, _yunit)
1649            self.page_finder[page_id].set_theory_data(data=new_plot,
1650                                                      fid=data.id)
1651            self.parent.update_theory(data_id=data.id, theory=new_plot,
1652                                       state=state)
1653            current_pg = self.fit_panel.get_page_by_id(page_id)
1654            title = new_plot.title
1655            batch_on = self.fit_panel.get_page_by_id(page_id).batch_on
1656            if not batch_on:
1657                wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot,
1658                                            title=str(title)))
1659            elif plot_result:
1660                top_data_id = self.fit_panel.get_page_by_id(page_id).data.id
1661                if data.id == top_data_id:
1662                    wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot,
1663                                            title=str(title)))
1664            caption = current_pg.window_caption
1665            self.page_finder[page_id].set_fit_tab_caption(caption=caption)
1666
1667            self.page_finder[page_id].set_theory_data(data=new_plot,
1668                                                      fid=data.id)
1669            if toggle_mode_on:
1670                wx.PostEvent(self.parent,
1671                             NewPlotEvent(group_id=str(page_id) + " Model2D",
1672                                          action="Hide"))
1673            else:
1674                if update_chisqr:
1675                    wx.PostEvent(current_pg,
1676                                 Chi2UpdateEvent(output=self._cal_chisqr(
1677                                                                data=data,
1678                                                                fid=fid,
1679                                                                weight=weight,
1680                                                            page_id=page_id,
1681                                                            index=index)))
1682                else:
1683                    self._plot_residuals(page_id=page_id, data=data, fid=fid,
1684                                         index=index, weight=weight)
1685
1686            msg = "Computation  completed!"
1687            wx.PostEvent(self.parent, StatusEvent(status=msg, type="stop"))
1688        except:
1689            raise
1690
1691    def _update2D(self, output, time=None):
1692        """
1693        Update the output of plotting model
1694        """
1695        wx.PostEvent(self.parent, StatusEvent(status="Plot \
1696        #updating ... ", type="update"))
1697        #self.ready_fit()
1698
1699    def _complete2D(self, image, data, model, page_id, elapsed, index, qmin,
1700                qmax, fid=None, weight=None, toggle_mode_on=False, state=None,
1701                     update_chisqr=True, source='model', plot_result=True):
1702        """
1703        Complete get the result of modelthread and create model 2D
1704        that can be plot.
1705        """
1706        numpy.nan_to_num(image)
1707        new_plot = Data2D(image=image, err_image=data.err_data)
1708        new_plot.name = model.name + '2d'
1709        new_plot.title = "Analytical model 2D "
1710        new_plot.id = str(page_id) + " " + data.name
1711        new_plot.group_id = str(page_id) + " Model2D"
1712        new_plot.detector = data.detector
1713        new_plot.source = data.source
1714        new_plot.is_data = False
1715        new_plot.qx_data = data.qx_data
1716        new_plot.qy_data = data.qy_data
1717        new_plot.q_data = data.q_data
1718        new_plot.mask = data.mask
1719        ## plot boundaries
1720        new_plot.ymin = data.ymin
1721        new_plot.ymax = data.ymax
1722        new_plot.xmin = data.xmin
1723        new_plot.xmax = data.xmax
1724        title = data.title
1725
1726        new_plot.is_data = False
1727        if data.is_data:
1728            data_name = str(data.name)
1729        else:
1730            data_name = str(model.__class__.__name__) + '2d'
1731
1732        if len(title) > 1:
1733            new_plot.title = "Model2D for %s " % model.name + data_name
1734        new_plot.name = model.name + " [" + \
1735                                    data_name + "]"
1736        theory_data = deepcopy(new_plot)
1737
1738        self.page_finder[page_id].set_theory_data(data=theory_data,
1739                                                  fid=data.id)
1740        self.parent.update_theory(data_id=data.id,
1741                                       theory=new_plot,
1742                                       state=state)
1743        current_pg = self.fit_panel.get_page_by_id(page_id)
1744        title = new_plot.title
1745        if not source == 'fit' and plot_result:
1746            wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot,
1747                                               title=title))
1748        if toggle_mode_on:
1749            wx.PostEvent(self.parent,
1750                             NewPlotEvent(group_id=str(page_id) + " Model1D",
1751                                               action="Hide"))
1752        else:
1753            # Chisqr in fitpage
1754            if update_chisqr:
1755                wx.PostEvent(current_pg,
1756                             Chi2UpdateEvent(output=self._cal_chisqr(data=data,
1757                                                                    weight=weight,
1758                                                                    fid=fid,
1759                                                         page_id=page_id,
1760                                                         index=index)))
1761            else:
1762                self._plot_residuals(page_id=page_id, data=data, fid=fid,
1763                                      index=index, weight=weight)
1764        msg = "Computation  completed!"
1765        wx.PostEvent(self.parent, StatusEvent(status=msg, type="stop"))
1766
1767    def _draw_model2D(self, model, page_id, qmin,
1768                      qmax,
1769                      data=None, smearer=None,
1770                      description=None, enable2D=False,
1771                      state=None,
1772                      fid=None,
1773                      weight=None,
1774                      toggle_mode_on=False,
1775                       update_chisqr=True, source='model'):
1776        """
1777        draw model in 2D
1778
1779        :param model: instance of the model to draw
1780        :param description: the description of the model
1781        :param enable2D: when True allows to draw model 2D
1782        :param qmin: the minimum value to  draw model 2D
1783        :param qmax: the maximum value to draw model 2D
1784        :param qstep: the number of division of Qx and Qy of the model to draw
1785
1786        """
1787        if not enable2D:
1788            return None
1789        try:
1790            from model_thread import Calc2D
1791            ## If a thread is already started, stop it
1792            if (self.calc_2D is not None) and self.calc_2D.isrunning():
1793                self.calc_2D.stop()
1794                ## stop just raises a flag to tell the thread to kill
1795                ## itself -- see the fix in Calc1D implemented to fix
1796                ## an actual problem.  Seems the fix should also go here
1797                ## and may be the cause of other noted instabilities
1798                ##
1799                ##    -PDB August 12, 2014
1800                while self.calc_2D.isrunning():
1801                    time.sleep(0.1)
1802            self.calc_2D = Calc2D(model=model,
1803                                    data=data,
1804                                    page_id=page_id,
1805                                    smearer=smearer,
1806                                    qmin=qmin,
1807                                    qmax=qmax,
1808                                    weight=weight,
1809                                    fid=fid,
1810                                    toggle_mode_on=toggle_mode_on,
1811                                    state=state,
1812                                    completefn=self._complete2D,
1813                                    update_chisqr=update_chisqr, source=source)
1814            self.calc_2D.queue()
1815        except:
1816            raise
1817
1818    def _draw_model1D(self, model, page_id, data,
1819                      qmin, qmax, smearer=None,
1820                state=None,
1821                weight=None,
1822                fid=None,
1823                toggle_mode_on=False, update_chisqr=True, source='model',
1824                enable1D=True):
1825        """
1826        Draw model 1D from loaded data1D
1827
1828        :param data: loaded data
1829        :param model: the model to plot
1830
1831        """
1832        if not enable1D:
1833            return
1834        try:
1835            from model_thread import Calc1D
1836            ## If a thread is already started, stop it
1837            if (self.calc_1D is not None) and self.calc_1D.isrunning():
1838                self.calc_1D.stop()
1839                ## stop just raises the flag -- the thread is supposed to
1840                ## then kill itself but cannot.  Paul Kienzle came up with
1841                ## this fix to prevent threads from stepping on each other
1842                ## which was causing a simple custom model to crash Sasview.
1843                ## We still don't know why the fit sometimes lauched a second
1844                ## thread -- something which should also be investigated.
1845                ## The thread approach was implemented in order to be able
1846                ## to lauch a computation in a separate thread from the GUI so
1847                ## that the GUI can still respond to user input including
1848                ## a request to stop the computation.
1849                ## It seems thus that the whole thread approach used here
1850                ## May need rethinking 
1851                ##
1852                ##    -PDB August 12, 2014                 
1853                while self.calc_1D.isrunning():
1854                    time.sleep(0.1)
1855            self.calc_1D = Calc1D(data=data,
1856                                  model=model,
1857                                  page_id=page_id,
1858                                  qmin=qmin,
1859                                  qmax=qmax,
1860                                  smearer=smearer,
1861                                  state=state,
1862                                  weight=weight,
1863                                  fid=fid,
1864                                  toggle_mode_on=toggle_mode_on,
1865                                  completefn=self._complete1D,
1866                                  #updatefn = self._update1D,
1867                                  update_chisqr=update_chisqr,
1868                                  source=source)
1869            self.calc_1D.queue()
1870        except:
1871            msg = " Error occurred when drawing %s Model 1D: " % model.name
1872            msg += " %s" % sys.exc_value
1873            wx.PostEvent(self.parent, StatusEvent(status=msg))
1874
1875    def _cal_chisqr(self, page_id, data, weight, fid=None, index=None):
1876        """
1877        Get handy Chisqr using the output from draw1D and 2D,
1878        instead of calling expansive CalcChisqr in guithread
1879        """
1880        try:
1881            data_copy = deepcopy(data)
1882        except:
1883            return
1884        # default chisqr
1885        chisqr = None
1886        #to compute chisq make sure data has valid data
1887        # return None if data == None
1888        if not check_data_validity(data_copy) or data_copy == None:
1889            return chisqr
1890
1891        # Get data: data I, theory I, and data dI in order
1892        if data_copy.__class__.__name__ == "Data2D":
1893            if index == None:
1894                index = numpy.ones(len(data_copy.data), dtype=bool)
1895            if weight != None:
1896                data_copy.err_data = weight
1897            # get rid of zero error points
1898            index = index & (data_copy.err_data != 0)
1899            index = index & (numpy.isfinite(data_copy.data))
1900            fn = data_copy.data[index]
1901            theory_data = self.page_finder[page_id].get_theory_data(fid=data_copy.id)
1902            if theory_data == None:
1903                return chisqr
1904            gn = theory_data.data[index]
1905            en = data_copy.err_data[index]
1906        else:
1907            # 1 d theory from model_thread is only in the range of index
1908            if index == None:
1909                index = numpy.ones(len(data_copy.y), dtype=bool)
1910            if weight != None:
1911                data_copy.dy = weight
1912            if data_copy.dy == None or data_copy.dy == []:
1913                dy = numpy.ones(len(data_copy.y))
1914            else:
1915                ## Set consistently w/AbstractFitengine:
1916                # But this should be corrected later.
1917                dy = deepcopy(data_copy.dy)
1918                dy[dy == 0] = 1
1919            fn = data_copy.y[index]
1920
1921            theory_data = self.page_finder[page_id].get_theory_data(fid=data_copy.id)
1922            if theory_data == None:
1923                return chisqr
1924            gn = theory_data.y
1925            en = dy[index]
1926
1927        # residual
1928        try:
1929            res = (fn - gn) / en
1930        except ValueError:
1931            print "Unmatch lengths %s, %s, %s" % (len(fn), len(gn), len(en))
1932            return
1933
1934        residuals = res[numpy.isfinite(res)]
1935        # get chisqr only w/finite
1936        chisqr = numpy.average(residuals * residuals)
1937
1938        self._plot_residuals(page_id=page_id, data=data_copy,
1939                             fid=fid,
1940                             weight=weight, index=index)
1941
1942        return chisqr
1943
1944    def _plot_residuals(self, page_id, weight, fid=None,
1945                        data=None, index=None):
1946        """
1947        Plot the residuals
1948
1949        :param data: data
1950        :param index: index array (bool)
1951        : Note: this is different from the residuals in cal_chisqr()
1952        """
1953        data_copy = deepcopy(data)
1954        # Get data: data I, theory I, and data dI in order
1955        if data_copy.__class__.__name__ == "Data2D":
1956            # build residuals
1957            residuals = Data2D()
1958            #residuals.copy_from_datainfo(data)
1959            # Not for trunk the line below, instead use the line above
1960            data_copy.clone_without_data(len(data_copy.data), residuals)
1961            residuals.data = None
1962            fn = data_copy.data
1963            theory_data = self.page_finder[page_id].get_theory_data(fid=data_copy.id)
1964            gn = theory_data.data
1965            if weight == None:
1966                en = data_copy.err_data
1967            else:
1968                en = weight
1969            residuals.data = (fn - gn) / en
1970            residuals.qx_data = data_copy.qx_data
1971            residuals.qy_data = data_copy.qy_data
1972            residuals.q_data = data_copy.q_data
1973            residuals.err_data = numpy.ones(len(residuals.data))
1974            residuals.xmin = min(residuals.qx_data)
1975            residuals.xmax = max(residuals.qx_data)
1976            residuals.ymin = min(residuals.qy_data)
1977            residuals.ymax = max(residuals.qy_data)
1978            residuals.q_data = data_copy.q_data
1979            residuals.mask = data_copy.mask
1980            residuals.scale = 'linear'
1981            # check the lengths
1982            if len(residuals.data) != len(residuals.q_data):
1983                return
1984        else:
1985            # 1 d theory from model_thread is only in the range of index
1986            if data_copy.dy == None or data_copy.dy == []:
1987                dy = numpy.ones(len(data_copy.y))
1988            else:
1989                if weight == None:
1990                    dy = numpy.ones(len(data_copy.y))
1991                ## Set consitently w/AbstractFitengine:
1992                ## But this should be corrected later.
1993                else:
1994                    dy = weight
1995                dy[dy == 0] = 1
1996            fn = data_copy.y[index]
1997            theory_data = self.page_finder[page_id].get_theory_data(fid=data_copy.id)
1998            gn = theory_data.y
1999            en = dy[index]
2000            # build residuals
2001            residuals = Data1D()
2002            try:
2003                residuals.y = (fn - gn) / en
2004            except:
2005                msg = "ResidualPlot Error: different # of data points in theory"
2006                wx.PostEvent(self.parent, StatusEvent(status=msg, info="error"))
2007                residuals.y = (fn - gn[index]) / en
2008            residuals.x = data_copy.x[index]
2009            residuals.dy = numpy.ones(len(residuals.y))
2010            residuals.dx = None
2011            residuals.dxl = None
2012            residuals.dxw = None
2013            residuals.ytransform = 'y'
2014            # For latter scale changes
2015            residuals.xaxis('\\rm{Q} ', 'A^{-1}')
2016            residuals.yaxis('\\rm{Residuals} ', 'normalized')
2017        theory_name = str(theory_data.name.split()[0])
2018        new_plot = residuals
2019        new_plot.name = "Residuals for " + str(theory_name) + "[" + \
2020                        str(data.name) + "]"
2021        ## allow to highlight data when plotted
2022        new_plot.interactive = True
2023        ## when 2 data have the same id override the 1 st plotted
2024        new_plot.id = "res" + str(data_copy.id) + str(theory_name)
2025        ##group_id specify on which panel to plot this data
2026        group_id = self.page_finder[page_id].get_graph_id()
2027        if group_id == None:
2028            group_id = data.group_id
2029        new_plot.group_id = "res" + str(group_id)
2030        #new_plot.is_data = True
2031        ##post data to plot
2032        title = new_plot.name
2033        self.page_finder[page_id].set_residuals(residuals=new_plot,
2034                                                fid=data.id)
2035        self.parent.update_theory(data_id=data.id, theory=new_plot)
2036        batch_on = self.fit_panel.get_page_by_id(page_id).batch_on
2037        if not batch_on:
2038            wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot, title=title))
Note: See TracBrowser for help on using the repository browser.