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

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 c95a1a5 was 098f3d2, checked in by Doucet, Mathieu <doucetm@…>, 9 years ago

Remove print preview menu items. Fix 2D plotting from fit perspective.

  • Property mode set to 100644
File size: 83.2 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, self.on_fit_results)
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            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                        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 we are not dealing with a specific fit problem, then
673        # there is no point setting the weights.
674        if fid is None:
675            return
676        if uid in self.page_finder.keys():
677            self.page_finder[uid].set_weight(flag=flag, is2d=is2d)
678
679    def set_fit_range(self, uid, qmin, qmax, fid=None):
680        """
681        Set the fitting range of a given page for all
682        its data by default. If fid is provide then set the range
683        only for the data with fid as id
684        :param uid: id corresponding to a fit page
685        :param fid: id corresponding to a fit problem (data, model)
686        :param qmin: minimum  value of the fit range
687        :param qmax: maximum  value of the fit range
688        """
689        if uid in self.page_finder.keys():
690            self.page_finder[uid].set_range(qmin=qmin, qmax=qmax, fid=fid)
691
692    def schedule_for_fit(self, value=0, uid=None):
693        """
694        Set the fit problem field to 0 or 1 to schedule that problem to fit.
695        Schedule the specified fitproblem or get the fit problem related to
696        the current page and set value.
697        :param value: integer 0 or 1
698        :param uid: the id related to a page contaning fitting information
699        """
700        if uid in self.page_finder.keys():
701            self.page_finder[uid].schedule_tofit(value)
702
703    def get_page_finder(self):
704        """
705        return self.page_finder used also by simfitpage.py
706        """
707        return self.page_finder
708
709    def set_page_finder(self, modelname, names, values):
710        """
711        Used by simfitpage.py to reset a parameter given the string constrainst.
712
713        :param modelname: the name ot the model for with the parameter
714                            has to reset
715        :param value: can be a string in this case.
716        :param names: the paramter name
717        """
718        sim_page_id = self.sim_page.uid
719        for uid, value in self.page_finder.iteritems():
720            if uid != sim_page_id and uid != self.batch_page.uid:
721                list = value.get_model()
722                model = list[0]
723                if model.name == modelname:
724                    value.set_model_param(names, values)
725                    break
726
727    def split_string(self, item):
728        """
729        receive a word containing dot and split it. used to split parameterset
730        name into model name and parameter name example: ::
731
732            paramaterset (item) = M1.A
733            Will return model_name = M1 , parameter name = A
734
735        """
736        if item.find(".") >= 0:
737            param_names = re.split("\.", item)
738            model_name = param_names[0]
739            ##Assume max len is 3; eg., M0.radius.width
740            if len(param_names) == 3:
741                param_name = param_names[1] + "." + param_names[2]
742            else:
743                param_name = param_names[1]
744            return model_name, param_name
745
746    def on_bumps_options(self, event=None):
747        """
748        Open the bumps options panel.
749        """
750        from bumps.gui.fit_dialog import OpenFitOptions
751        OpenFitOptions()
752
753    def on_fit_results(self, event=None):
754        """
755        Make the Fit Results panel visible.
756        """
757        self.result_frame.Show()
758        self.result_frame.Raise()
759
760    def stop_fit(self, uid):
761        """
762        Stop the fit
763        """
764        if uid in self.fit_thread_list.keys():
765            calc_fit = self.fit_thread_list[uid]
766            if calc_fit is not  None and calc_fit.isrunning():
767                calc_fit.stop()
768                msg = "Fit stop!"
769                wx.PostEvent(self.parent, StatusEvent(status=msg, type="stop"))
770            del self.fit_thread_list[uid]
771        #set the fit button label of page when fit stop is trigger from
772        #simultaneous fit pane
773        sim_flag = self.sim_page is not None and uid == self.sim_page.uid
774        batch_flag = self.batch_page is not None and uid == self.batch_page.uid
775        if sim_flag or batch_flag:
776            for uid, value in self.page_finder.iteritems():
777                if value.get_scheduled() == 1:
778                    if uid in self.fit_panel.opened_pages.keys():
779                        panel = self.fit_panel.opened_pages[uid]
780                        panel._on_fit_complete()
781
782    def set_smearer(self, uid, smearer, fid, qmin=None, qmax=None, draw=True,
783                    enable_smearer=False):
784        """
785        Get a smear object and store it to a fit problem of fid as id. If proper
786        flag is enable , will plot the theory with smearing information.
787
788        :param smearer: smear object to allow smearing data of id fid
789        :param enable_smearer: Define whether or not all (data, model) contained
790            in the structure of id uid will be smeared before fitting.
791        :param qmin: the maximum value of the theory plotting range
792        :param qmax: the maximum value of the theory plotting range
793        :param draw: Determine if the theory needs to be plot
794        """
795        if uid not in self.page_finder.keys():
796            return
797        self.page_finder[uid].enable_smearing(flag=enable_smearer)
798        self.page_finder[uid].set_smearer(smearer, fid=fid)
799        if draw:
800            ## draw model 1D with smeared data
801            data = self.page_finder[uid].get_fit_data(fid=fid)
802            if data is None:
803                msg = "set_mearer requires at least data.\n"
804                msg += "Got data = %s .\n" % str(data)
805                return
806                #raise ValueError, msg
807            model = self.page_finder[uid].get_model(fid=fid)
808            if model is None:
809                return
810            enable1D = issubclass(data.__class__, Data1D)
811            enable2D = issubclass(data.__class__, Data2D)
812            ## if user has already selected a model to plot
813            ## redraw the model with data smeared
814            smear = self.page_finder[uid].get_smearer(fid=fid)
815
816            # compute weight for the current data
817            weight = self.page_finder[uid].get_weight(fid=fid)
818
819            self.draw_model(model=model, data=data, page_id=uid, smearer=smear,
820                enable1D=enable1D, enable2D=enable2D,
821                qmin=qmin, qmax=qmax, weight=weight)
822            self._mac_sleep(0.2)
823
824    def _mac_sleep(self, sec=0.2):
825        """
826        Give sleep to MAC
827        """
828        if ON_MAC:
829            time.sleep(sec)
830
831    def draw_model(self, model, page_id, data=None, smearer=None,
832                   enable1D=True, enable2D=False,
833                   state=None,
834                   fid=None,
835                   toggle_mode_on=False,
836                   qmin=None, qmax=None,
837                   update_chisqr=True, weight=None, source='model'):
838        """
839        Draw model.
840
841        :param model: the model to draw
842        :param name: the name of the model to draw
843        :param data: the data on which the model is based to be drawn
844        :param description: model's description
845        :param enable1D: if true enable drawing model 1D
846        :param enable2D: if true enable drawing model 2D
847        :param qmin:  Range's minimum value to draw model
848        :param qmax:  Range's maximum value to draw model
849        :param qstep: number of step to divide the x and y-axis
850        :param update_chisqr: update chisqr [bool]
851
852        """
853        #self.weight = weight
854        if issubclass(data.__class__, Data1D) or not enable2D:
855            ## draw model 1D with no loaded data
856            self._draw_model1D(model=model,
857                               data=data,
858                               page_id=page_id,
859                               enable1D=enable1D,
860                               smearer=smearer,
861                               qmin=qmin,
862                               qmax=qmax,
863                               fid=fid,
864                               weight=weight,
865                               toggle_mode_on=toggle_mode_on,
866                               state=state,
867                               update_chisqr=update_chisqr,
868                               source=source)
869        else:
870            ## draw model 2D with no initial data
871            self._draw_model2D(model=model,
872                                page_id=page_id,
873                                data=data,
874                                enable2D=enable2D,
875                                smearer=smearer,
876                                qmin=qmin,
877                                qmax=qmax,
878                                fid=fid,
879                                weight=weight,
880                                state=state,
881                                toggle_mode_on=toggle_mode_on,
882                                update_chisqr=update_chisqr,
883                                source=source)
884
885    def onFit(self, uid):
886        """
887        Get series of data, model, associates parameters and range and send then
888        to  series of fitters. Fit data and model, display result to
889        corresponding panels.
890        :param uid: id related to the panel currently calling this fit function.
891        """
892        if uid is None: raise RuntimeError("no page to fit") # Should never happen
893
894        sim_page_uid = getattr(self.sim_page, 'uid', None)
895        batch_page_uid = getattr(self.batch_page, 'uid', None)
896
897        if uid == sim_page_uid:
898            fit_type = 'simultaneous'
899        elif uid == batch_page_uid:
900            fit_type = 'combined_batch'
901        else:
902            fit_type = 'single'
903
904        fitter_list = []
905        sim_fitter = None
906        if fit_type == 'simultaneous':
907            # for simultaneous fitting only one fitter is needed
908            sim_fitter = Fit()
909            sim_fitter.fitter_id = self.sim_page.uid
910            fitter_list.append(sim_fitter)
911
912        self.current_pg = None
913        list_page_id = []
914        fit_id = 0
915        for page_id, page_info in self.page_finder.iteritems():
916            # For simulfit (uid give with None), do for-loop
917            # if uid is specified (singlefit), do it only on the page.
918            if page_id in (sim_page_uid, batch_page_uid): continue
919            if fit_type == "single" and page_id != uid: continue
920
921            try:
922                if page_info.get_scheduled() == 1:
923                    page_info.nbr_residuals_computed = 0
924                    page = self.fit_panel.get_page_by_id(page_id)
925                    self.set_fit_weight(uid=page.uid,
926                                     flag=page.get_weight_flag(),
927                                     is2d=page._is_2D())
928                    if not page.param_toFit:
929                        msg = "No fitting parameters for %s" % page.window_caption
930                        wx.PostEvent(page.parent.parent,
931                                     StatusEvent(status=msg, info="error",
932                                                 type="stop"))
933                        return False
934                    if not page._update_paramv_on_fit():
935                        msg = "Fitting range or parameter values are"
936                        msg += " invalid in %s" % \
937                                    page.window_caption
938                        wx.PostEvent(page.parent.parent,
939                                     StatusEvent(status=msg, info="error",
940                                     type="stop"))
941                        return False
942
943                    pars = [str(element[1]) for element in page.param_toFit]
944                    fitproblem_list = page_info.values()
945                    for fitproblem in  fitproblem_list:
946                        if sim_fitter is None:
947                            fitter = Fit()
948                            fitter.fitter_id = page_id
949                            fitter_list.append(fitter)
950                        else:
951                            fitter = sim_fitter
952                        self._add_problem_to_fit(fitproblem=fitproblem,
953                                             pars=pars,
954                                             fitter=fitter,
955                                             fit_id=fit_id)
956                        fit_id += 1
957                    list_page_id.append(page_id)
958                    page_info.clear_model_param()
959            except KeyboardInterrupt:
960                msg = "Fitting terminated"
961                wx.PostEvent(self.parent, StatusEvent(status=msg, info="info",
962                                                      type="stop"))
963                return True
964            except:
965                raise
966                msg = "Fitting error: %s" % str(sys.exc_value)
967                wx.PostEvent(self.parent, StatusEvent(status=msg, info="error",
968                                                      type="stop"))
969                return False
970        ## If a thread is already started, stop it
971        #if self.calc_fit!= None and self.calc_fit.isrunning():
972        #    self.calc_fit.stop()
973        msg = "Fitting is in progress..."
974        wx.PostEvent(self.parent, StatusEvent(status=msg, type="progress"))
975
976        #Handler used to display fit message
977        handler = ConsoleUpdate(parent=self.parent,
978                                manager=self,
979                                improvement_delta=0.1)
980        self._mac_sleep(0.2)
981
982        # batch fit
983        batch_inputs = {}
984        batch_outputs = {}
985        if fit_type == "simultaneous":
986            page = self.sim_page
987        elif fit_type == "combined_batch":
988            page = self.batch_page
989        else:
990            page = self.fit_panel.get_page_by_id(uid)
991        if page.batch_on:
992            calc_fit = FitThread(handler=handler,
993                                 fn=fitter_list,
994                                 pars=pars,
995                                 batch_inputs=batch_inputs,
996                                 batch_outputs=batch_outputs,
997                                 page_id=list_page_id,
998                                 completefn=self._batch_fit_complete,
999                                 reset_flag=self.batch_reset_flag)
1000        else:
1001            ## Perform more than 1 fit at the time
1002            calc_fit = FitThread(handler=handler,
1003                                    fn=fitter_list,
1004                                    batch_inputs=batch_inputs,
1005                                    batch_outputs=batch_outputs,
1006                                    page_id=list_page_id,
1007                                    updatefn=handler.update_fit,
1008                                    completefn=self._fit_completed)
1009        #self.fit_thread_list[current_page_id] = calc_fit
1010        self.fit_thread_list[uid] = calc_fit
1011        calc_fit.queue()
1012        calc_fit.ready(2.5)
1013        msg = "Fitting is in progress..."
1014        wx.PostEvent(self.parent, StatusEvent(status=msg, type="progress"))
1015
1016        return True
1017
1018    def remove_plot(self, uid, fid=None, theory=False):
1019        """
1020        remove model plot when a fit page is closed
1021        :param uid: the id related to the fitpage to close
1022        :param fid: the id of the fitproblem(data, model, range,etc)
1023        """
1024        if uid not in self.page_finder.keys():
1025            return
1026        fitproblemList = self.page_finder[uid].get_fit_problem(fid)
1027        for fitproblem in fitproblemList:
1028            data = fitproblem.get_fit_data()
1029            model = fitproblem.get_model()
1030            plot_id = None
1031            if model is not None:
1032                plot_id = data.id + model.name
1033            if theory:
1034                plot_id = data.id + model.name
1035            group_id = data.group_id
1036            wx.PostEvent(self.parent, NewPlotEvent(id=plot_id,
1037                                                   group_id=group_id,
1038                                                   action='remove'))
1039
1040    def store_data(self, uid, data_list=None, caption=None):
1041        """
1042        Recieve a list of data and store them ans well as a caption of
1043        the fit page where they come from.
1044        :param uid: if related to a fit page
1045        :param data_list: list of data to fit
1046        :param caption: caption of the window related to these data
1047        """
1048        if data_list is None:
1049            data_list = []
1050
1051        self.page_finder[uid].set_fit_data(data=data_list)
1052        if caption is not None:
1053            self.page_finder[uid].set_fit_tab_caption(caption=caption)
1054
1055    def on_add_new_page(self, event=None):
1056        """
1057        ask fit panel to create a new empty page
1058        """
1059        try:
1060            page = self.fit_panel.add_empty_page()
1061            # add data associated to the page created
1062            if page != None:
1063                wx.PostEvent(self.parent, StatusEvent(status="Page Created",
1064                                               info="info"))
1065            else:
1066                msg = "Page was already Created"
1067                wx.PostEvent(self.parent, StatusEvent(status=msg,
1068                                                       info="warning"))
1069        except:
1070            msg = "Creating Fit page: %s" % sys.exc_value
1071            wx.PostEvent(self.parent, StatusEvent(status=msg, info="error"))
1072
1073    def add_fit_page(self, data):
1074        """
1075        given a data, ask to the fitting panel to create a new fitting page,
1076        get this page and store it into the page_finder of this plug-in
1077        :param data: is a list of data
1078        """
1079        page = self.fit_panel.set_data(data)
1080        # page could be None when loading state files
1081        if page == None:
1082            return page
1083        #append Data1D to the panel containing its theory
1084        #if theory already plotted
1085        if page.uid in self.page_finder:
1086            data = page.get_data()
1087            theory_data = self.page_finder[page.uid].get_theory_data(data.id)
1088            if issubclass(data.__class__, Data2D):
1089                data.group_id = wx.NewId()
1090                if theory_data is not None:
1091                    group_id = str(page.uid) + " Model1D"
1092                    wx.PostEvent(self.parent,
1093                             NewPlotEvent(group_id=group_id,
1094                                               action="delete"))
1095                    self.parent.update_data(prev_data=theory_data,
1096                                             new_data=data)
1097            else:
1098                if theory_data is not None:
1099                    group_id = str(page.uid) + " Model2D"
1100                    data.group_id = theory_data.group_id
1101                    wx.PostEvent(self.parent,
1102                             NewPlotEvent(group_id=group_id,
1103                                               action="delete"))
1104                    self.parent.update_data(prev_data=theory_data,
1105                                             new_data=data)
1106        self.store_data(uid=page.uid, data_list=page.get_data_list(),
1107                        caption=page.window_caption)
1108        if self.sim_page is not None and not self.batch_on:
1109            self.sim_page.draw_page()
1110        if self.batch_page is not None and self.batch_on:
1111            self.batch_page.draw_page()
1112
1113        return page
1114
1115    def _onEVT_SLICER_PANEL(self, event):
1116        """
1117        receive and event telling to update a panel with a name starting with
1118        event.panel_name. this method update slicer panel
1119        for a given interactor.
1120
1121        :param event: contains type of slicer , paramaters for updating
1122            the panel and panel_name to find the slicer 's panel concerned.
1123        """
1124        event.panel_name
1125        for item in self.parent.panels:
1126            name = event.panel_name
1127            if self.parent.panels[item].window_caption.startswith(name):
1128                self.parent.panels[item].set_slicer(event.type, event.params)
1129
1130        #self.parent._mgr.Update()
1131
1132    def _closed_fitpage(self, event):
1133        """
1134        request fitpanel to close a given page when its unique data is removed
1135        from the plot. close fitpage only when the a loaded data is removed
1136        """
1137        if event is None or event.data is None:
1138            return
1139        if hasattr(event.data, "is_data"):
1140            if not event.data.is_data or \
1141                event.data.__class__.__name__ == "Data1D":
1142                self.fit_panel.close_page_with_data(event.data)
1143
1144    def _reset_schedule_problem(self, value=0, uid=None):
1145        """
1146        unschedule or schedule all fitproblem to be fit
1147        """
1148        # case that uid is not specified
1149        if uid == None:
1150            for page_id in self.page_finder.keys():
1151                self.page_finder[page_id].schedule_tofit(value)
1152        # when uid is given
1153        else:
1154            if uid in self.page_finder.keys():
1155                self.page_finder[uid].schedule_tofit(value)
1156
1157    def _add_problem_to_fit(self, fitproblem, pars, fitter, fit_id):
1158        """
1159        Create and set fitter with series of data and model
1160        """
1161        data = fitproblem.get_fit_data()
1162        model = fitproblem.get_model()
1163        smearer = fitproblem.get_smearer()
1164        qmin, qmax = fitproblem.get_range()
1165
1166        #Extra list of parameters and their constraints
1167        listOfConstraint = []
1168        param = fitproblem.get_model_param()
1169        if len(param) > 0:
1170            for item in param:
1171                ## check if constraint
1172                if item[0] != None and item[1] != None:
1173                    listOfConstraint.append((item[0], item[1]))
1174        new_model = model
1175        fitter.set_model(new_model, fit_id, pars, data=data,
1176                         constraints=listOfConstraint)
1177        fitter.set_data(data=data, id=fit_id, smearer=smearer, qmin=qmin,
1178                        qmax=qmax)
1179        fitter.select_problem_for_fit(id=fit_id, value=1)
1180
1181    def _onSelect(self, event):
1182        """
1183        when Select data to fit a new page is created .Its reference is
1184        added to self.page_finder
1185        """
1186        panel = self.plot_panel
1187        if panel == None:
1188            raise ValueError, "Fitting:_onSelect: NonType panel"
1189        Plugin.on_perspective(self, event=event)
1190        self.select_data(panel)
1191
1192    def select_data(self, panel):
1193        """
1194        """
1195        for plottable in panel.graph.plottables:
1196            if plottable.__class__.__name__ in ["Data1D", "Theory1D"]:
1197                data_id = panel.graph.selected_plottable
1198                if plottable == panel.plots[data_id]:
1199                    data = plottable
1200                    self.add_fit_page(data=[data])
1201                    return
1202            else:
1203                data = plottable
1204                self.add_fit_page(data=[data])
1205
1206    def update_fit(self, result=None, msg=""):
1207        """
1208        """
1209        print "update_fit result", result
1210
1211    def _batch_fit_complete(self, result, pars, page_id,
1212                            batch_outputs, batch_inputs, elapsed=None):
1213        """
1214        Display fit result in batch
1215        :param result: list of objects received from fitters
1216        :param pars: list of  fitted parameters names
1217        :param page_id: list of page ids which called fit function
1218        :param elapsed: time spent at the fitting level
1219        """
1220        self._mac_sleep(0.2)
1221        uid = page_id[0]
1222        if uid in self.fit_thread_list.keys():
1223            del self.fit_thread_list[uid]
1224
1225        wx.CallAfter(self._update_fit_button, page_id)
1226        t1 = time.time()
1227        str_time = time.strftime("%a, %d %b %Y %H:%M:%S ", time.localtime(t1))
1228        msg = "Fit completed on %s \n" % str_time
1229        msg += "Duration time: %s s.\n" % str(elapsed)
1230        wx.PostEvent(self.parent, StatusEvent(status=msg, info="info",
1231                                              type="stop"))
1232
1233        if batch_outputs is None:
1234            batch_outputs = {}
1235
1236        # format batch_outputs
1237        batch_outputs["Chi2"] = []
1238        #Don't like these loops
1239        # Need to create dictionary of all fitted parameters
1240        # since the number of parameters can differ between each fit result
1241        for list_res in result:
1242            for res in list_res:
1243                model, data = res.inputs[0]
1244                if model is not None and hasattr(model, "model"):
1245                    model = model.model
1246                #get all fittable parameters of the current model
1247                for param in  model.getParamList():
1248                    if param  not in batch_outputs.keys():
1249                        batch_outputs[param] = []
1250                for param in model.getDispParamList():
1251                    if not model.is_fittable(param) and \
1252                        param in batch_outputs.keys():
1253                        del batch_outputs[param]
1254                # Add fitted parameters and their error
1255                for param in res.param_list:
1256                    if param not in batch_outputs.keys():
1257                        batch_outputs[param] = []
1258                    err_param = "error on %s" % str(param)
1259                    if err_param not in batch_inputs.keys():
1260                        batch_inputs[err_param] = []
1261        msg = ""
1262        for list_res in result:
1263            for res in list_res:
1264                pid = res.fitter_id
1265                model, data = res.inputs[0]
1266                correct_result = False
1267                if model is not None and hasattr(model, "model"):
1268                    model = model.model
1269                if data is not None and hasattr(data, "sas_data"):
1270                    data = data.sas_data
1271
1272                is_data2d = issubclass(data.__class__, Data2D)
1273                #check consistency of arrays
1274                if not is_data2d:
1275                    if len(res.theory) == len(res.index[res.index]) and \
1276                        len(res.index) == len(data.y):
1277                        correct_result = True
1278                else:
1279                    copy_data = deepcopy(data)
1280                    new_theory = copy_data.data
1281                    new_theory[res.index] = res.theory
1282                    new_theory[res.index == False] = numpy.nan
1283                    correct_result = True
1284                #get all fittable parameters of the current model
1285                param_list = model.getParamList()
1286                for param in model.getDispParamList():
1287                    if not model.is_fittable(param) and \
1288                        param in param_list:
1289                        param_list.remove(param)
1290                if not correct_result or res.fitness is None or \
1291                    not numpy.isfinite(res.fitness) or \
1292                    numpy.any(res.pvec == None) or not \
1293                    numpy.all(numpy.isfinite(res.pvec)):
1294                    data_name = str(None)
1295                    if data is not None:
1296                        data_name = str(data.name)
1297                    model_name = str(None)
1298                    if model is not None:
1299                        model_name = str(model.name)
1300                    msg += "Data %s and Model %s did not fit.\n" % (data_name,
1301                                                                    model_name)
1302                    ERROR = numpy.NAN
1303                    cell = BatchCell()
1304                    cell.label = res.fitness
1305                    cell.value = res.fitness
1306                    batch_outputs["Chi2"].append(ERROR)
1307                    for param in param_list:
1308                        # save value of  fixed parameters
1309                        if param not in res.param_list:
1310                            batch_outputs[str(param)].append(ERROR)
1311                        else:
1312                            #save only fitted values
1313                            batch_outputs[param].append(ERROR)
1314                            batch_inputs["error on %s" % str(param)].append(ERROR)
1315                else:
1316                    # TODO: Why sometimes res.pvec comes with numpy.float64?
1317                    # probably from scipy lmfit
1318                    if res.pvec.__class__ == numpy.float64:
1319                        res.pvec = [res.pvec]
1320
1321                    cell = BatchCell()
1322                    cell.label = res.fitness
1323                    cell.value = res.fitness
1324                    batch_outputs["Chi2"].append(cell)
1325                    # add parameters to batch_results
1326                    for param in param_list:
1327                        # save value of  fixed parameters
1328                        if param not in res.param_list:
1329                            batch_outputs[str(param)].append(model.getParam(param))
1330                        else:
1331                            index = res.param_list.index(param)
1332                            #save only fitted values
1333                            batch_outputs[param].append(res.pvec[index])
1334                            if res.stderr is not None and \
1335                                len(res.stderr) == len(res.param_list):
1336                                item = res.stderr[index]
1337                                batch_inputs["error on %s" % param].append(item)
1338                            else:
1339                                batch_inputs["error on %s" % param].append('-')
1340                            model.setParam(param, res.pvec[index])
1341                #fill the batch result with emtpy value if not in the current
1342                #model
1343                EMPTY = "-"
1344                for key in batch_outputs.keys():
1345                    if key not in param_list and key not in ["Chi2", "Data"]:
1346                        batch_outputs[key].append(EMPTY)
1347
1348                self.page_finder[pid].set_batch_result(batch_inputs=batch_inputs,
1349                                                       batch_outputs=batch_outputs)
1350
1351                cpage = self.fit_panel.get_page_by_id(pid)
1352                cpage._on_fit_complete()
1353                self.page_finder[pid][data.id].set_result(res)
1354                fitproblem = self.page_finder[pid][data.id]
1355                qmin, qmax = fitproblem.get_range()
1356                plot_result = False
1357                if correct_result:
1358                    if not is_data2d:
1359                        self._complete1D(x=data.x[res.index], y=res.theory, page_id=pid,
1360                                         elapsed=None,
1361                                         index=res.index, model=model,
1362                                         weight=None, fid=data.id,
1363                                         toggle_mode_on=False, state=None,
1364                                         data=data, update_chisqr=False,
1365                                         source='fit', plot_result=plot_result)
1366                    else:
1367                        self._complete2D(image=new_theory, data=data,
1368                                         model=model,
1369                                         page_id=pid, elapsed=None,
1370                                         index=res.index,
1371                                         qmin=qmin,
1372                                         qmax=qmax, fid=data.id, weight=None,
1373                                         toggle_mode_on=False, state=None,
1374                                         update_chisqr=False,
1375                                         source='fit', plot_result=plot_result)
1376                self.on_set_batch_result(page_id=pid,
1377                                         fid=data.id,
1378                                         batch_outputs=batch_outputs,
1379                                         batch_inputs=batch_inputs)
1380
1381        wx.PostEvent(self.parent, StatusEvent(status=msg, error="info",
1382                                              type="stop"))
1383        # Remove parameters that are not shown
1384        cpage = self.fit_panel.get_page_by_id(uid)
1385        tbatch_outputs = {}
1386        shownkeystr = cpage.get_copy_params()
1387        for key in batch_outputs.keys():
1388            if key in ["Chi2", "Data"] or shownkeystr.count(key) > 0:
1389                tbatch_outputs[key] = batch_outputs[key]
1390
1391        wx.CallAfter(self.parent.on_set_batch_result, tbatch_outputs,
1392                     batch_inputs, self.sub_menu)
1393
1394    def on_set_batch_result(self, page_id, fid, batch_outputs, batch_inputs):
1395        """
1396        """
1397        pid = page_id
1398        if fid not in self.page_finder[pid]:
1399            return
1400        fitproblem = self.page_finder[pid][fid]
1401        index = self.page_finder[pid].nbr_residuals_computed - 1
1402        residuals = fitproblem.get_residuals()
1403        theory_data = fitproblem.get_theory_data()
1404        data = fitproblem.get_fit_data()
1405        model = fitproblem.get_model()
1406        #fill batch result information
1407        if "Data" not in batch_outputs.keys():
1408            batch_outputs["Data"] = []
1409        from sas.guiframe.data_processor import BatchCell
1410        cell = BatchCell()
1411        cell.label = data.name
1412        cell.value = index
1413
1414        if theory_data != None:
1415            #Suucessful fit
1416            theory_data.id = wx.NewId()
1417            theory_data.name = model.name + "[%s]" % str(data.name)
1418            if issubclass(theory_data.__class__, Data2D):
1419                group_id = wx.NewId()
1420                theory_data.group_id = group_id
1421                if group_id not in theory_data.list_group_id:
1422                    theory_data.list_group_id.append(group_id)
1423
1424            try:
1425                # associate residuals plot
1426                if issubclass(residuals.__class__, Data2D):
1427                    group_id = wx.NewId()
1428                    residuals.group_id = group_id
1429                    if group_id not in residuals.list_group_id:
1430                        residuals.list_group_id.append(group_id)
1431                batch_outputs["Chi2"][index].object = [residuals]
1432            except:
1433                pass
1434
1435        cell.object = [data, theory_data]
1436        batch_outputs["Data"].append(cell)
1437        for key, value in data.meta_data.iteritems():
1438            if key not in batch_inputs.keys():
1439                batch_inputs[key] = []
1440            #if key.lower().strip() != "loader":
1441            batch_inputs[key].append(value)
1442        param = "temperature"
1443        if hasattr(data.sample, param):
1444            if param not in  batch_inputs.keys():
1445                batch_inputs[param] = []
1446            batch_inputs[param].append(data.sample.temperature)
1447
1448    def _fit_completed(self, result, page_id, batch_outputs,
1449                       batch_inputs=None, pars=None, elapsed=None):
1450        """
1451        Display result of the fit on related panel(s).
1452        :param result: list of object generated when fit ends
1453        :param pars: list of names of parameters fitted
1454        :param page_id: list of page ids which called fit function
1455        :param elapsed: time spent at the fitting level
1456        """
1457        t1 = time.time()
1458        str_time = time.strftime("%a, %d %b %Y %H:%M:%S ", time.localtime(t1))
1459        msg = "Fit completed on %s \n" % str_time
1460        msg += "Duration time: %s s.\n" % str(elapsed)
1461        wx.PostEvent(self.parent, StatusEvent(status=msg, info="info",
1462                                                      type="stop"))
1463        wx.PostEvent(self.result_panel, PlotResultEvent(result=result))
1464        wx.CallAfter(self._update_fit_button, page_id)
1465        result = result[0]
1466        self.fit_thread_list = {}
1467        if page_id is None:
1468            page_id = []
1469        ## fit more than 1 model at the same time
1470        self._mac_sleep(0.2)
1471        try:
1472            index = 0
1473            for uid in page_id:
1474                res = result[index]
1475                if res.fitness is None or \
1476                    not numpy.isfinite(res.fitness) or \
1477                    numpy.any(res.pvec == None) or \
1478                    not numpy.all(numpy.isfinite(res.pvec)):
1479                    msg = "Fitting did not converge!!!"
1480                    wx.PostEvent(self.parent,
1481                             StatusEvent(status=msg,
1482                                         info="warning",
1483                                         type="stop"))
1484                    wx.CallAfter(self._update_fit_button, page_id)
1485                else:
1486                    #set the panel when fit result are float not list
1487                    if res.pvec.__class__ == numpy.float64:
1488                        pvec = [res.pvec]
1489                    else:
1490                        pvec = res.pvec
1491                    if res.stderr.__class__ == numpy.float64:
1492                        stderr = [res.stderr]
1493                    else:
1494                        stderr = res.stderr
1495                    cpage = self.fit_panel.get_page_by_id(uid)
1496                    # Make sure we got all results
1497                    #(CallAfter is important to MAC)
1498                    try:
1499                        #if res != None:
1500                        wx.CallAfter(cpage.onsetValues, res.fitness,
1501                                     res.param_list,
1502                                     pvec, stderr)
1503                        index += 1
1504                        wx.CallAfter(cpage._on_fit_complete)
1505                    except KeyboardInterrupt:
1506                        msg = "Singular point: Fitting Stoped."
1507                        wx.PostEvent(self.parent, StatusEvent(status=msg,
1508                                                              info="info",
1509                                                              type="stop"))
1510                    except:
1511                        msg = "Singular point: Fitting Error occurred."
1512                        wx.PostEvent(self.parent, StatusEvent(status=msg,
1513                                                              info="error",
1514                                                              type="stop"))
1515
1516        except:
1517            msg = ("Fit completed but the following error occurred: %s"
1518                   % sys.exc_value)
1519            #import traceback; msg = "\n".join((traceback.format_exc(), msg))
1520            wx.PostEvent(self.parent, StatusEvent(status=msg, info="warning",
1521                                                  type="stop"))
1522
1523    def _update_fit_button(self, page_id):
1524        """
1525        Update Fit button when fit stopped
1526
1527        : parameter page_id: fitpage where the button is
1528        """
1529        if page_id.__class__.__name__ != 'list':
1530            page_id = [page_id]
1531        for uid in page_id:
1532            page = self.fit_panel.get_page_by_id(uid)
1533            page._on_fit_complete()
1534
1535    def _on_show_panel(self, event):
1536        """
1537        """
1538        pass
1539
1540    def on_reset_batch_flag(self, event):
1541        """
1542        Set batch_reset_flag
1543        """
1544        event.Skip()
1545        if self.menu1 == None:
1546            return
1547        menu_item = self.menu1.FindItemById(self.id_reset_flag)
1548        flag = menu_item.IsChecked()
1549        if not flag:
1550            menu_item.Check(False)
1551            self.batch_reset_flag = True
1552        else:
1553            menu_item.Check(True)
1554            self.batch_reset_flag = False
1555
1556        ## post a message to status bar
1557        msg = "Set Chain Fitting: %s" % str(not self.batch_reset_flag)
1558        wx.PostEvent(self.parent,
1559                     StatusEvent(status=msg))
1560
1561
1562    def _on_slicer_event(self, event):
1563        """
1564        Receive a panel as event and send it to guiframe
1565
1566        :param event: event containing a panel
1567
1568        """
1569        if event.panel is not None:
1570            new_panel = event.panel
1571            self.slicer_panels.append(event.panel)
1572            # Set group ID if available
1573            event_id = self.parent.popup_panel(event.panel)
1574            event.panel.uid = event_id
1575            self.mypanels.append(event.panel)
1576
1577    def _onclearslicer(self, event):
1578        """
1579        Clear the boxslicer when close the panel associate with this slicer
1580        """
1581        name = event.GetEventObject().frame.GetTitle()
1582        for panel in self.slicer_panels:
1583            if panel.window_caption == name:
1584
1585                for item in self.parent.panels:
1586                    if hasattr(self.parent.panels[item], "uid"):
1587                        if self.parent.panels[item].uid == panel.base.uid:
1588                            self.parent.panels[item].onClearSlicer(event)
1589                            #self.parent._mgr.Update()
1590                            break
1591                break
1592
1593    def _on_model_panel(self, evt):
1594        """
1595        react to model selection on any combo box or model menu.plot the model
1596
1597        :param evt: wx.combobox event
1598
1599        """
1600        model = evt.model
1601        uid = evt.uid
1602        qmin = evt.qmin
1603        qmax = evt.qmax
1604        caption = evt.caption
1605        enable_smearer = evt.enable_smearer
1606        if model == None:
1607            return
1608        if uid not in self.page_finder.keys():
1609            return
1610        # save the name containing the data name with the appropriate model
1611        self.page_finder[uid].set_model(model)
1612        self.page_finder[uid].enable_smearing(enable_smearer)
1613        self.page_finder[uid].set_range(qmin=qmin, qmax=qmax)
1614        self.page_finder[uid].set_fit_tab_caption(caption=caption)
1615        if self.sim_page is not None and not self.batch_on:
1616            self.sim_page.draw_page()
1617        if self.batch_page is not None and self.batch_on:
1618            self.batch_page.draw_page()
1619
1620    def _update1D(self, x, output):
1621        """
1622        Update the output of plotting model 1D
1623        """
1624        msg = "Plot updating ... "
1625        wx.PostEvent(self.parent, StatusEvent(status=msg, type="update"))
1626
1627    def _complete1D(self, x, y, page_id, elapsed, index, model,
1628                    weight=None, fid=None,
1629                    toggle_mode_on=False, state=None,
1630                    data=None, update_chisqr=True,
1631                    source='model', plot_result=True):
1632        """
1633        Complete plotting 1D data
1634        """
1635        try:
1636            numpy.nan_to_num(y)
1637
1638            new_plot = Data1D(x=x, y=y)
1639            new_plot.is_data = False
1640            new_plot.dy = numpy.zeros(len(y))
1641            new_plot.symbol = GUIFRAME_ID.CURVE_SYMBOL_NUM
1642            _yaxis, _yunit = data.get_yaxis()
1643            _xaxis, _xunit = data.get_xaxis()
1644            new_plot.title = data.name
1645
1646            new_plot.group_id = data.group_id
1647            if new_plot.group_id == None:
1648                new_plot.group_id = data.group_id
1649            new_plot.id = str(page_id) + " " + data.name
1650            #if new_plot.id in self.color_dict:
1651            #    new_plot.custom_color = self.color_dict[new_plot.id]
1652            #find if this theory was already plotted and replace that plot given
1653            #the same id
1654            theory_data = self.page_finder[page_id].get_theory_data(fid=data.id)
1655
1656            if data.is_data:
1657                data_name = str(data.name)
1658            else:
1659                data_name = str(model.__class__.__name__)
1660
1661            new_plot.name = model.name + " [" + data_name + "]"
1662            new_plot.xaxis(_xaxis, _xunit)
1663            new_plot.yaxis(_yaxis, _yunit)
1664            self.page_finder[page_id].set_theory_data(data=new_plot,
1665                                                      fid=data.id)
1666            self.parent.update_theory(data_id=data.id, theory=new_plot,
1667                                       state=state)
1668            current_pg = self.fit_panel.get_page_by_id(page_id)
1669            title = new_plot.title
1670            batch_on = self.fit_panel.get_page_by_id(page_id).batch_on
1671            if not batch_on:
1672                wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot,
1673                                            title=str(title)))
1674            elif plot_result:
1675                top_data_id = self.fit_panel.get_page_by_id(page_id).data.id
1676                if data.id == top_data_id:
1677                    wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot,
1678                                            title=str(title)))
1679            caption = current_pg.window_caption
1680            self.page_finder[page_id].set_fit_tab_caption(caption=caption)
1681
1682            self.page_finder[page_id].set_theory_data(data=new_plot,
1683                                                      fid=data.id)
1684            if toggle_mode_on:
1685                wx.PostEvent(self.parent,
1686                             NewPlotEvent(group_id=str(page_id) + " Model2D",
1687                                          action="Hide"))
1688            else:
1689                if update_chisqr:
1690                    wx.PostEvent(current_pg,
1691                                 Chi2UpdateEvent(output=self._cal_chisqr(
1692                                                                data=data,
1693                                                                fid=fid,
1694                                                                weight=weight,
1695                                                            page_id=page_id,
1696                                                            index=index)))
1697                else:
1698                    self._plot_residuals(page_id=page_id, data=data, fid=fid,
1699                                         index=index, weight=weight)
1700
1701            msg = "Computation  completed!"
1702            wx.PostEvent(self.parent, StatusEvent(status=msg, type="stop"))
1703        except:
1704            raise
1705
1706    def _update2D(self, output, time=None):
1707        """
1708        Update the output of plotting model
1709        """
1710        wx.PostEvent(self.parent, StatusEvent(status="Plot \
1711        #updating ... ", type="update"))
1712        #self.ready_fit()
1713
1714    def _complete2D(self, image, data, model, page_id, elapsed, index, qmin,
1715                qmax, fid=None, weight=None, toggle_mode_on=False, state=None,
1716                     update_chisqr=True, source='model', plot_result=True):
1717        """
1718        Complete get the result of modelthread and create model 2D
1719        that can be plot.
1720        """
1721        numpy.nan_to_num(image)
1722        new_plot = Data2D(image=image, err_image=data.err_data)
1723        new_plot.name = model.name + '2d'
1724        new_plot.title = "Analytical model 2D "
1725        new_plot.id = str(page_id) + " " + data.name
1726        new_plot.group_id = str(page_id) + " Model2D"
1727        new_plot.detector = data.detector
1728        new_plot.source = data.source
1729        new_plot.is_data = False
1730        new_plot.qx_data = data.qx_data
1731        new_plot.qy_data = data.qy_data
1732        new_plot.q_data = data.q_data
1733        new_plot.mask = data.mask
1734        ## plot boundaries
1735        new_plot.ymin = data.ymin
1736        new_plot.ymax = data.ymax
1737        new_plot.xmin = data.xmin
1738        new_plot.xmax = data.xmax
1739        title = data.title
1740
1741        new_plot.is_data = False
1742        if data.is_data:
1743            data_name = str(data.name)
1744        else:
1745            data_name = str(model.__class__.__name__) + '2d'
1746
1747        if len(title) > 1:
1748            new_plot.title = "Model2D for %s " % model.name + data_name
1749        new_plot.name = model.name + " [" + \
1750                                    data_name + "]"
1751        theory_data = deepcopy(new_plot)
1752
1753        self.page_finder[page_id].set_theory_data(data=theory_data,
1754                                                  fid=data.id)
1755        self.parent.update_theory(data_id=data.id,
1756                                       theory=new_plot,
1757                                       state=state)
1758        current_pg = self.fit_panel.get_page_by_id(page_id)
1759        title = new_plot.title
1760        if not source == 'fit' and plot_result:
1761            wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot,
1762                                               title=title))
1763        if toggle_mode_on:
1764            wx.PostEvent(self.parent,
1765                             NewPlotEvent(group_id=str(page_id) + " Model1D",
1766                                               action="Hide"))
1767        else:
1768            # Chisqr in fitpage
1769            if update_chisqr:
1770                wx.PostEvent(current_pg,
1771                             Chi2UpdateEvent(output=self._cal_chisqr(data=data,
1772                                                                    weight=weight,
1773                                                                    fid=fid,
1774                                                         page_id=page_id,
1775                                                         index=index)))
1776            else:
1777                self._plot_residuals(page_id=page_id, data=data, fid=fid,
1778                                      index=index, weight=weight)
1779        msg = "Computation  completed!"
1780        wx.PostEvent(self.parent, StatusEvent(status=msg, type="stop"))
1781
1782    def _draw_model2D(self, model, page_id, qmin,
1783                      qmax,
1784                      data=None, smearer=None,
1785                      description=None, enable2D=False,
1786                      state=None,
1787                      fid=None,
1788                      weight=None,
1789                      toggle_mode_on=False,
1790                       update_chisqr=True, source='model'):
1791        """
1792        draw model in 2D
1793
1794        :param model: instance of the model to draw
1795        :param description: the description of the model
1796        :param enable2D: when True allows to draw model 2D
1797        :param qmin: the minimum value to  draw model 2D
1798        :param qmax: the maximum value to draw model 2D
1799        :param qstep: the number of division of Qx and Qy of the model to draw
1800
1801        """
1802        if not enable2D:
1803            return None
1804        try:
1805            from model_thread import Calc2D
1806            ## If a thread is already started, stop it
1807            if (self.calc_2D is not None) and self.calc_2D.isrunning():
1808                self.calc_2D.stop()
1809                ## stop just raises a flag to tell the thread to kill
1810                ## itself -- see the fix in Calc1D implemented to fix
1811                ## an actual problem.  Seems the fix should also go here
1812                ## and may be the cause of other noted instabilities
1813                ##
1814                ##    -PDB August 12, 2014
1815                while self.calc_2D.isrunning():
1816                    time.sleep(0.1)
1817            self.calc_2D = Calc2D(model=model,
1818                                    data=data,
1819                                    page_id=page_id,
1820                                    smearer=smearer,
1821                                    qmin=qmin,
1822                                    qmax=qmax,
1823                                    weight=weight,
1824                                    fid=fid,
1825                                    toggle_mode_on=toggle_mode_on,
1826                                    state=state,
1827                                    completefn=self._complete2D,
1828                                    update_chisqr=update_chisqr, source=source)
1829            self.calc_2D.queue()
1830        except:
1831            raise
1832
1833    def _draw_model1D(self, model, page_id, data,
1834                      qmin, qmax, smearer=None,
1835                state=None,
1836                weight=None,
1837                fid=None,
1838                toggle_mode_on=False, update_chisqr=True, source='model',
1839                enable1D=True):
1840        """
1841        Draw model 1D from loaded data1D
1842
1843        :param data: loaded data
1844        :param model: the model to plot
1845
1846        """
1847        if not enable1D:
1848            return
1849        try:
1850            from model_thread import Calc1D
1851            ## If a thread is already started, stop it
1852            if (self.calc_1D is not None) and self.calc_1D.isrunning():
1853                self.calc_1D.stop()
1854                ## stop just raises the flag -- the thread is supposed to
1855                ## then kill itself but cannot.  Paul Kienzle came up with
1856                ## this fix to prevent threads from stepping on each other
1857                ## which was causing a simple custom model to crash Sasview.
1858                ## We still don't know why the fit sometimes lauched a second
1859                ## thread -- something which should also be investigated.
1860                ## The thread approach was implemented in order to be able
1861                ## to lauch a computation in a separate thread from the GUI so
1862                ## that the GUI can still respond to user input including
1863                ## a request to stop the computation.
1864                ## It seems thus that the whole thread approach used here
1865                ## May need rethinking 
1866                ##
1867                ##    -PDB August 12, 2014                 
1868                while self.calc_1D.isrunning():
1869                    time.sleep(0.1)
1870            self.calc_1D = Calc1D(data=data,
1871                                  model=model,
1872                                  page_id=page_id,
1873                                  qmin=qmin,
1874                                  qmax=qmax,
1875                                  smearer=smearer,
1876                                  state=state,
1877                                  weight=weight,
1878                                  fid=fid,
1879                                  toggle_mode_on=toggle_mode_on,
1880                                  completefn=self._complete1D,
1881                                  #updatefn = self._update1D,
1882                                  update_chisqr=update_chisqr,
1883                                  source=source)
1884            self.calc_1D.queue()
1885        except:
1886            msg = " Error occurred when drawing %s Model 1D: " % model.name
1887            msg += " %s" % sys.exc_value
1888            wx.PostEvent(self.parent, StatusEvent(status=msg))
1889
1890    def _cal_chisqr(self, page_id, data, weight, fid=None, index=None):
1891        """
1892        Get handy Chisqr using the output from draw1D and 2D,
1893        instead of calling expansive CalcChisqr in guithread
1894        """
1895        try:
1896            data_copy = deepcopy(data)
1897        except:
1898            return
1899        # default chisqr
1900        chisqr = None
1901        #to compute chisq make sure data has valid data
1902        # return None if data == None
1903        if not check_data_validity(data_copy) or data_copy == None:
1904            return chisqr
1905
1906        # Get data: data I, theory I, and data dI in order
1907        if data_copy.__class__.__name__ == "Data2D":
1908            if index == None:
1909                index = numpy.ones(len(data_copy.data), dtype=bool)
1910            if weight != None:
1911                data_copy.err_data = weight
1912            # get rid of zero error points
1913            index = index & (data_copy.err_data != 0)
1914            index = index & (numpy.isfinite(data_copy.data))
1915            fn = data_copy.data[index]
1916            theory_data = self.page_finder[page_id].get_theory_data(fid=data_copy.id)
1917            if theory_data == None:
1918                return chisqr
1919            gn = theory_data.data[index]
1920            en = data_copy.err_data[index]
1921        else:
1922            # 1 d theory from model_thread is only in the range of index
1923            if index == None:
1924                index = numpy.ones(len(data_copy.y), dtype=bool)
1925            if weight != None:
1926                data_copy.dy = weight
1927            if data_copy.dy == None or data_copy.dy == []:
1928                dy = numpy.ones(len(data_copy.y))
1929            else:
1930                ## Set consistently w/AbstractFitengine:
1931                # But this should be corrected later.
1932                dy = deepcopy(data_copy.dy)
1933                dy[dy == 0] = 1
1934            fn = data_copy.y[index]
1935
1936            theory_data = self.page_finder[page_id].get_theory_data(fid=data_copy.id)
1937            if theory_data == None:
1938                return chisqr
1939            gn = theory_data.y
1940            en = dy[index]
1941
1942        # residual
1943        try:
1944            res = (fn - gn) / en
1945        except ValueError:
1946            print "Unmatch lengths %s, %s, %s" % (len(fn), len(gn), len(en))
1947            return
1948
1949        residuals = res[numpy.isfinite(res)]
1950        # get chisqr only w/finite
1951        chisqr = numpy.average(residuals * residuals)
1952
1953        self._plot_residuals(page_id=page_id, data=data_copy,
1954                             fid=fid,
1955                             weight=weight, index=index)
1956
1957        return chisqr
1958
1959    def _plot_residuals(self, page_id, weight, fid=None,
1960                        data=None, index=None):
1961        """
1962        Plot the residuals
1963
1964        :param data: data
1965        :param index: index array (bool)
1966        : Note: this is different from the residuals in cal_chisqr()
1967        """
1968        data_copy = deepcopy(data)
1969        # Get data: data I, theory I, and data dI in order
1970        if data_copy.__class__.__name__ == "Data2D":
1971            # build residuals
1972            residuals = Data2D()
1973            #residuals.copy_from_datainfo(data)
1974            # Not for trunk the line below, instead use the line above
1975            data_copy.clone_without_data(len(data_copy.data), residuals)
1976            residuals.data = None
1977            fn = data_copy.data
1978            theory_data = self.page_finder[page_id].get_theory_data(fid=data_copy.id)
1979            gn = theory_data.data
1980            if weight == None:
1981                en = data_copy.err_data
1982            else:
1983                en = weight
1984            residuals.data = (fn - gn) / en
1985            residuals.qx_data = data_copy.qx_data
1986            residuals.qy_data = data_copy.qy_data
1987            residuals.q_data = data_copy.q_data
1988            residuals.err_data = numpy.ones(len(residuals.data))
1989            residuals.xmin = min(residuals.qx_data)
1990            residuals.xmax = max(residuals.qx_data)
1991            residuals.ymin = min(residuals.qy_data)
1992            residuals.ymax = max(residuals.qy_data)
1993            residuals.q_data = data_copy.q_data
1994            residuals.mask = data_copy.mask
1995            residuals.scale = 'linear'
1996            # check the lengths
1997            if len(residuals.data) != len(residuals.q_data):
1998                return
1999        else:
2000            # 1 d theory from model_thread is only in the range of index
2001            if data_copy.dy == None or data_copy.dy == []:
2002                dy = numpy.ones(len(data_copy.y))
2003            else:
2004                if weight == None:
2005                    dy = numpy.ones(len(data_copy.y))
2006                ## Set consitently w/AbstractFitengine:
2007                ## But this should be corrected later.
2008                else:
2009                    dy = weight
2010                dy[dy == 0] = 1
2011            fn = data_copy.y[index]
2012            theory_data = self.page_finder[page_id].get_theory_data(fid=data_copy.id)
2013            gn = theory_data.y
2014            en = dy[index]
2015            # build residuals
2016            residuals = Data1D()
2017            try:
2018                residuals.y = (fn - gn) / en
2019            except:
2020                msg = "ResidualPlot Error: different # of data points in theory"
2021                wx.PostEvent(self.parent, StatusEvent(status=msg, info="error"))
2022                residuals.y = (fn - gn[index]) / en
2023            residuals.x = data_copy.x[index]
2024            residuals.dy = numpy.ones(len(residuals.y))
2025            residuals.dx = None
2026            residuals.dxl = None
2027            residuals.dxw = None
2028            residuals.ytransform = 'y'
2029            # For latter scale changes
2030            residuals.xaxis('\\rm{Q} ', 'A^{-1}')
2031            residuals.yaxis('\\rm{Residuals} ', 'normalized')
2032        theory_name = str(theory_data.name.split()[0])
2033        new_plot = residuals
2034        new_plot.name = "Residuals for " + str(theory_name) + "[" + \
2035                        str(data.name) + "]"
2036        ## allow to highlight data when plotted
2037        new_plot.interactive = True
2038        ## when 2 data have the same id override the 1 st plotted
2039        new_plot.id = "res" + str(data_copy.id) + str(theory_name)
2040        ##group_id specify on which panel to plot this data
2041        group_id = self.page_finder[page_id].get_graph_id()
2042        if group_id == None:
2043            group_id = data.group_id
2044        new_plot.group_id = "res" + str(group_id)
2045        #new_plot.is_data = True
2046        ##post data to plot
2047        title = new_plot.name
2048        self.page_finder[page_id].set_residuals(residuals=new_plot,
2049                                                fid=data.id)
2050        self.parent.update_theory(data_id=data.id, theory=new_plot)
2051        batch_on = self.fit_panel.get_page_by_id(page_id).batch_on
2052        if not batch_on:
2053            wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot, title=title))
Note: See TracBrowser for help on using the repository browser.