source: sasview/src/sas/sasgui/perspectives/fitting/simfitpage.py @ 0a3c740

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalcmagnetic_scattrelease-4.2.2ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since 0a3c740 was 0a3c740, checked in by Paul Kienzle <pkienzle@…>, 7 years ago

fix merge errors

  • Property mode set to 100644
File size: 42.6 KB
Line 
1"""
2    Simultaneous or Batch fit page
3"""
4# Note that this is used for both Simultaneous/Constrained fit AND for
5# combined batch fit.  This is done through setting of the batch_on parameter.
6# There are the a half dozen or so places where an if statement is used as in
7# if not batch_on:
8#     xxxx
9# else:
10#     xxxx
11# This is just wrong but dont have time to fix this go. Proper approach would be
12# to strip all parts of the code that depend on batch_on and create the top
13# level class from which a contrained/simultaneous fit page and a combined
14# batch page inherit.
15#
16#            04/09/2017   --PDB
17
18import sys
19from collections import namedtuple
20
21import wx
22import wx.lib.newevent
23from wx.lib.scrolledpanel import ScrolledPanel
24
25from sas.sascalc.fit.pagestate import SimFitPageState
26from sas.sasgui.guiframe.events import StatusEvent, PanelOnFocusEvent
27from sas.sasgui.guiframe.panel_base import PanelBase
28from sas.sasgui.guiframe.utils import IdList
29from sas.sasgui.guiframe.documentation_window import DocumentationWindow
30
31# Control panel width
32if sys.platform.count("darwin") == 0:
33    PANEL_WID = 420
34    FONT_VARIANT = 0
35else:
36    PANEL_WID = 490
37    FONT_VARIANT = 1
38
39
40# Each constraint requires five widgets and sizer.  Package them in
41# a named tuple for easy access.
42ConstraintLine = namedtuple('ConstraintLine',
43        'model_cbox param_cbox egal_txt constraint btRemove sizer')
44
45
46def get_fittableParam(model):
47    """
48    return list of fittable parameters from a model
49
50    :param model: the model used
51
52    """
53    fittable_param = []
54    for item in model.getParamList():
55        if not item  in model.getDispParamList():
56            if not item in model.non_fittable:
57                fittable_param.append(item)
58
59    for item in model.fixed:
60        fittable_param.append(item)
61
62    return fittable_param
63
64class SimultaneousFitPage(ScrolledPanel, PanelBase):
65    """
66    Simultaneous fitting panel
67    All that needs to be defined are the
68    two data members window_name and window_caption
69    """
70    # Internal name for the AUI manager
71    window_name = "Simultaneous Fit Page"
72    # Title to appear on top of the window
73    window_caption = "Simultaneous Fit Page"
74    ID_DOC = wx.NewId()
75    ID_SET_ALL = wx.NewId()
76    ID_FIT = wx.NewId()
77    ID_ADD = wx.NewId()
78    _id_pool = IdList()
79
80    def __init__(self, parent, page_finder={}, id=wx.ID_ANY, batch_on=False,
81                 *args, **kwargs):
82        ScrolledPanel.__init__(self, parent, id=id,
83                               style=wx.FULL_REPAINT_ON_RESIZE,
84                               *args, **kwargs)
85        PanelBase.__init__(self, parent)
86        """
87        Simultaneous page display
88        """
89        self._ids = iter(self._id_pool)
90        self.SetupScrolling()
91        # Font size
92        self.SetWindowVariant(variant=FONT_VARIANT)
93        self.uid = wx.NewId()
94        self.parent = parent
95        self.batch_on = batch_on
96        # store page_finder
97        self.page_finder = page_finder
98        # list containing info to set constraint
99        # look like self.constraint_dict[page_id]= page
100        self.constraint_dict = {}
101        # item list
102        # self.constraints_list=[combobox1, combobox2,=,textcrtl, button ]
103        self.constraints_list = []
104        # list of current model
105        self.model_list = []
106        # selected model to fit
107        self.model_to_fit = []
108        # Control the fit state
109        self.fit_started = False
110        # number of constraint
111        self.nb_constraint = 0
112        self.state = SimFitPageState()
113        self.model_cbox_left = None
114        self.model_cbox_right = None
115        # draw page
116        self.define_page_structure()
117        self.draw_page()
118        self._set_save_flag(False)
119
120    def define_page_structure(self):
121        """
122        Create empty sizers, their hierarchy and set the sizer for the panel
123        """
124        self.vbox = wx.BoxSizer(wx.VERTICAL)
125        self.data_selection_sizer = wx.BoxSizer(wx.VERTICAL)
126        self.constraints_sizer = wx.BoxSizer(wx.VERTICAL)
127        self.run_fit_sizer = wx.BoxSizer(wx.VERTICAL)
128
129        self.data_selection_sizer.SetMinSize((PANEL_WID, -1))
130        self.constraints_sizer.SetMinSize((PANEL_WID, -1))
131        self.run_fit_sizer.SetMinSize((PANEL_WID, -1))
132        self.vbox.Add(self.data_selection_sizer)
133        self.vbox.Add(self.constraints_sizer)
134        self.vbox.Add(self.run_fit_sizer)
135        self.SetSizer(self.vbox)
136        self.Centre()
137
138    def set_state(self):
139        """
140        Define a set of state parameters for saving simultaneous fits.
141        """
142        self._set_constraint()
143        self.state.fit_page_no = self.uid
144        self.state.select_all = self.cb1.GetValue()
145        self.state.model_list = self.model_list
146        self.state.model_to_fit = self.model_to_fit
147        self.state.no_constraint = self.nb_constraint
148        self.state.constraint_dict = self.constraint_dict
149        self.state.constraints_list = self.constraints_list
150        return self.get_state()
151
152    def get_state(self):
153        """
154        Return the state of the current page
155        :return: self.state
156        """
157        return self.state
158
159    def load_from_save_state(self, sim_state):
160        """
161        Load in a simultaneous/constrained fit from a save state
162        :param fit: Fitpanel object
163        :return: None
164        """
165        init_map = {}
166        final_map = {}
167        # Process each model and associate old M# with new M#
168        i = 0
169        for model in self.model_list:
170            model_id = self._format_id(model[1].keys()[0])
171            for saved_model in sim_state.model_list:
172                save_id = saved_model.pop('name')
173                saved_model['name'] = save_id
174                save_id = self._format_id(save_id)
175                if save_id == model_id:
176                    inter_id = str(i)*5
177                    init_map[saved_model.pop('fit_page_source')] = inter_id
178                    final_map[inter_id] = model[3].name
179                    check = bool(saved_model.pop('checked'))
180                    self.model_list[i][0].SetValue(check)
181                    break
182            i += 1
183
184        self.check_model_name(None)
185
186        if len(sim_state.constraints_list) > 0:
187            self.hide_constraint.SetValue(False)
188            self.show_constraint.SetValue(True)
189            self._display_constraint(None)
190
191        for index, item in enumerate(sim_state.constraints_list):
192            model_cbox = item.pop('model_cbox')
193            if model_cbox != "":
194                constraint_value = item.pop('constraint')
195                param = item.pop('param_cbox')
196                equality = item.pop('egal_txt')
197                for key, value in init_map.items():
198                    model_cbox = model_cbox.replace(key, value)
199                    constraint_value = constraint_value.replace(key, value)
200                for key, value in final_map.items():
201                    model_cbox = model_cbox.replace(key, value)
202                    constraint_value = constraint_value.replace(key, value)
203
204                self.constraints_list[index][0].SetValue(model_cbox)
205                self._on_select_model(None)
206                self.constraints_list[index][1].SetValue(param)
207                self.constraints_list[index][2].SetLabel(equality)
208                self.constraints_list[index][3].SetValue(constraint_value)
209                self._on_add_constraint(None)
210                self._manager.sim_page = self
211
212    def _format_id(self, original_id):
213        original_id = original_id.rstrip('1234567890.')
214        new_id_list = original_id.split()
215        new_id = ' '.join(new_id_list)
216        return new_id
217
218
219
220    def draw_page(self):
221        """
222        Construct the Simultaneous/Constrained fit page. fills the first
223        region (sizer1) with the list of available fit page pairs of data
224        and models.  Then fills sizer2 with the checkbox for adding
225        constraints, and finally fills sizer3 with the fit button and
226        instructions.
227        """
228
229        # create blank list of constraints
230        self.model_list = []
231        self.model_to_fit = []
232        self.constraints_list = []
233        self.constraint_dict = {}
234        self.nb_constraint = 0
235        self.model_cbox_left = None
236        self.model_cbox_right = None
237
238        if len(self.model_list) > 0:
239            for item in self.model_list:
240                item[0].SetValue(False)
241                self.manager.schedule_for_fit(value=0, uid=item[2])
242
243        #-------------------------------------------------------
244        # setup sizer1 (which fitpages to include)
245        self.data_selection_sizer.Clear(True)
246        box_description = wx.StaticBox(self, wx.ID_ANY, "Fit Combinations")
247        boxsizer1 = wx.StaticBoxSizer(box_description, wx.VERTICAL)
248        sizer_title = wx.BoxSizer(wx.HORIZONTAL)
249        sizer_couples = wx.GridBagSizer(5, 5)
250
251        # The wx GUI has a flag to enable a menu item, but can still be
252        # reached via scripting. There is no guearantee future GUI
253        # implementations force this check, either.
254        # IMHO, this if statement should stay -- JRK 2016-OCT-05
255        if len(self.page_finder) == 0:
256            msg = " No fit combinations are found! \n\n"
257            msg += " Please load data and set up "
258            msg += "at least one fit panels first..."
259            sizer_title.Add(wx.StaticText(self, wx.ID_ANY, msg))
260        else:
261            # store model
262            self._store_model()
263
264            self.cb1 = wx.CheckBox(self, wx.ID_ANY, 'Select all')
265            self.cb1.SetValue(False)
266            wx.EVT_CHECKBOX(self, self.cb1.GetId(), self.check_all_model_name)
267
268            sizer_title.Add((10, 10), 0,
269                wx.TOP | wx.BOTTOM | wx.EXPAND | wx.ADJUST_MINSIZE, border=5)
270            sizer_title.Add(self.cb1, 0,
271                wx.TOP | wx.BOTTOM | wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE,
272                border=5)
273
274            # draw list of model and data names
275            self._fill_sizer_model_list(sizer_couples)
276
277        boxsizer1.Add(sizer_title, flag=wx.TOP | wx.BOTTOM, border=5)
278        boxsizer1.Add(sizer_couples, 1, flag=wx.TOP | wx.BOTTOM, border=5)
279        self.data_selection_sizer.Add(boxsizer1, 1, wx.EXPAND | wx.ALL, 10)
280        # self.sizer1.Layout()
281
282        #--------------------------------------------------------
283        # set up the other 2 sizers: the constraints list and the
284        # buttons (fit, help etc) sizer at the bottom of the page.
285        # Note: the if statement should be removed along with the above
286        # if statement as soon as it can be properly tested.
287        # Nov. 22 2015  --PDB
288        # As above, this page can be accessed through other means than the
289        # base SasView GUI.
290        # Oct. 5, 2016 --JRK
291        if len(self.page_finder) > 0:
292            # draw the sizer containing constraint info
293            if not self.batch_on:
294                self._fill_sizer_constraint()
295            # draw fit button sizer
296            self._fill_sizer_fit()
297
298    def _fill_sizer_model_list(self, sizer):
299        """
300        Receive a dictionary containing information to display model name
301        """
302        ix = 0
303        iy = 0
304        sizer.Clear(True)
305
306        new_name = wx.StaticText(self, wx.ID_ANY, '  Model Title ',
307                                 style=wx.ALIGN_CENTER)
308        new_name.SetBackgroundColour('orange')
309        new_name.SetForegroundColour(wx.WHITE)
310        sizer.Add(new_name, (iy, ix), (1, 1),
311                  wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
312        ix += 2
313        model_type = wx.StaticText(self, wx.ID_ANY, '  Model ')
314        model_type.SetBackgroundColour('grey')
315        model_type.SetForegroundColour(wx.WHITE)
316        sizer.Add(model_type, (iy, ix), (1, 1),
317                  wx.EXPAND | wx.ADJUST_MINSIZE, 0)
318        ix += 1
319        data_used = wx.StaticText(self, wx.ID_ANY, '  Data ')
320        data_used.SetBackgroundColour('grey')
321        data_used.SetForegroundColour(wx.WHITE)
322        sizer.Add(data_used, (iy, ix), (1, 1),
323                  wx.EXPAND | wx.ADJUST_MINSIZE, 0)
324        ix += 1
325        tab_used = wx.StaticText(self, wx.ID_ANY, '  FitPage ')
326        tab_used.SetBackgroundColour('grey')
327        tab_used.SetForegroundColour(wx.WHITE)
328        sizer.Add(tab_used, (iy, ix), (1, 1),
329                  wx.EXPAND | wx.ADJUST_MINSIZE, 0)
330        for id, value in self.page_finder.iteritems():
331            if id not in self.parent.opened_pages:
332                continue
333
334            if self.batch_on != self.parent.get_page_by_id(id).batch_on:
335                continue
336
337            data_list = []
338            model_list = []
339            # get data name and model objetta
340            for fitproblem in value.get_fit_problem():
341
342                data = fitproblem.get_fit_data()
343                if not data.is_data:
344                    continue
345                name = '-'
346                if data is not None and data.is_data:
347                    name = str(data.name)
348                data_list.append(name)
349
350                model = fitproblem.get_model()
351                if model is None:
352                    continue
353                model_list.append(model)
354
355            if len(model_list) == 0:
356                continue
357            # Draw sizer
358            ix = 0
359            iy += 1
360            model = model_list[0]
361            name = '_'
362            if model is not None:
363                name = str(model.name)
364            cb = wx.CheckBox(self, wx.ID_ANY, name)
365            cb.SetValue(False)
366            cb.Enable(model is not None and data.is_data)
367            sizer.Add(cb, (iy, ix), (1, 1),
368                      wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
369            wx.EVT_CHECKBOX(self, cb.GetId(), self.check_model_name)
370            ix += 2
371            model_type = wx.StaticText(self, wx.ID_ANY,
372                                       model.__class__.__name__)
373            sizer.Add(model_type, (iy, ix), (1, 1),
374                      wx.EXPAND | wx.ADJUST_MINSIZE, 0)
375            if self.batch_on:
376                data_used = wx.ComboBox(self, wx.ID_ANY, style=wx.CB_READONLY)
377                data_used.AppendItems(data_list)
378                data_used.SetSelection(0)
379            else:
380                data_used = wx.StaticText(self, wx.ID_ANY, data_list[0])
381
382            ix += 1
383            sizer.Add(data_used, (iy, ix), (1, 1),
384                      wx.EXPAND | wx.ADJUST_MINSIZE, 0)
385            ix += 1
386            caption = value.get_fit_tab_caption()
387            tab_caption_used = wx.StaticText(self, wx.ID_ANY, str(caption))
388            sizer.Add(tab_caption_used, (iy, ix), (1, 1),
389                      wx.EXPAND | wx.ADJUST_MINSIZE, 0)
390
391            self.model_list.append([cb, value, id, model])
392
393        iy += 1
394        sizer.Add((20, 20), (iy, ix), (1, 1),
395                  wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
396
397    def _fill_sizer_constraint(self):
398        """
399        Fill sizer containing constraint info
400        """
401        msg = "Select at least 1 model to add constraint "
402        wx.PostEvent(self.parent.parent, StatusEvent(status=msg))
403
404        self.constraints_sizer.Clear(True)
405        if self.batch_on:
406            if self.constraints_sizer.IsShown():
407                self.constraints_sizer.Show(False)
408            return
409        box_description = wx.StaticBox(self, wx.ID_ANY, "Fit Constraints")
410        box_sizer1 = wx.StaticBoxSizer(box_description, wx.VERTICAL)
411        sizer_title = wx.BoxSizer(wx.HORIZONTAL)
412        self.sizer_all_constraints = wx.BoxSizer(wx.HORIZONTAL)
413        self.sizer_constraints = wx.BoxSizer(wx.VERTICAL)
414        sizer_button = wx.BoxSizer(wx.HORIZONTAL)
415
416        self.hide_constraint = wx.RadioButton(self, wx.ID_ANY, 'No', (10, 10),
417                                              style=wx.RB_GROUP)
418        self.show_constraint = wx.RadioButton(self, wx.ID_ANY, 'Yes', (10, 30))
419        self.Bind(wx.EVT_RADIOBUTTON, self._display_constraint,
420                  id=self.hide_constraint.GetId())
421        self.Bind(wx.EVT_RADIOBUTTON, self._display_constraint,
422                  id=self.show_constraint.GetId())
423        if self.batch_on:
424            self.hide_constraint.Enable(False)
425            self.show_constraint.Enable(False)
426        self.hide_constraint.SetValue(True)
427        self.show_constraint.SetValue(False)
428
429        sizer_title.Add(wx.StaticText(self, wx.ID_ANY, " Model"))
430        sizer_title.Add((10, 10))
431        sizer_title.Add(wx.StaticText(self, wx.ID_ANY, " Parameter"))
432        sizer_title.Add((10, 10))
433        sizer_title.Add(wx.StaticText(self, wx.ID_ANY, " Add Constraint?"))
434        sizer_title.Add((10, 10))
435        sizer_title.Add(self.show_constraint)
436        sizer_title.Add(self.hide_constraint)
437        sizer_title.Add((10, 10))
438
439        self.btAdd = wx.Button(self, self.ID_ADD, 'Add')
440        self.btAdd.Bind(wx.EVT_BUTTON, self._on_add_constraint,
441                        id=self.btAdd.GetId())
442        self.btAdd.SetToolTipString("Add another constraint?")
443        self.btAdd.Hide()
444
445        text_hint = wx.StaticText(self, wx.ID_ANY,
446                                  "Example: [M0][parameter] = M1.parameter")
447        sizer_button.Add(text_hint, 0,
448                         wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 10)
449        sizer_button.Add(self.btAdd, 0,
450                         wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 10)
451
452        box_sizer1.Add(sizer_title, flag=wx.TOP | wx.BOTTOM, border=10)
453        box_sizer1.Add(self.sizer_all_constraints, flag=wx.TOP | wx.BOTTOM,
454                       border=10)
455        box_sizer1.Add(self.sizer_constraints, flag=wx.TOP | wx.BOTTOM,
456                       border=10)
457        box_sizer1.Add(sizer_button, flag=wx.TOP | wx.BOTTOM, border=10)
458
459        self.constraints_sizer.Add(box_sizer1, 0, wx.EXPAND | wx.ALL, 10)
460
461    def _fill_sizer_fit(self):
462        """
463        Draw fit button
464        """
465        self.run_fit_sizer.Clear(True)
466        box_description = wx.StaticBox(self, wx.ID_ANY, "Fit ")
467        boxsizer1 = wx.StaticBoxSizer(box_description, wx.VERTICAL)
468        sizer_button = wx.BoxSizer(wx.HORIZONTAL)
469
470        # Fit button
471        self.btFit = wx.Button(self, self.ID_FIT, 'Fit', size=wx.DefaultSize)
472        self.btFit.Bind(wx.EVT_BUTTON, self.on_fit, id=self.btFit.GetId())
473        self.btFit.SetToolTipString("Perform fit.")
474
475        # General Help button
476        self.btHelp = wx.Button(self, wx.ID_HELP, 'HELP')
477        if self.batch_on:
478            self.btHelp.SetToolTipString("Combined Batch Fitting help.")
479        else:
480            self.btHelp.SetToolTipString("Simultaneous/Constrained Fitting help.")
481        self.btHelp.Bind(wx.EVT_BUTTON, self._on_help)
482
483        # hint text on button line
484        if self.batch_on:
485            text = " Fit in Parallel all Data sets\n"
486            text += "and model selected."
487        else:
488            text = " At least one set of model and data\n"
489            text += " must be selected for fitting."
490        text_hint = wx.StaticText(self, wx.ID_ANY, text)
491
492        sizer_button.Add(text_hint)
493        sizer_button.Add(self.btFit, 0, wx.LEFT | wx.ADJUST_MINSIZE, 10)
494        sizer_button.Add(self.btHelp, 0, wx.LEFT | wx.ADJUST_MINSIZE, 10)
495
496        boxsizer1.Add(sizer_button, flag=wx.TOP | wx.BOTTOM, border=10)
497        self.run_fit_sizer.Add(boxsizer1, 0, wx.EXPAND | wx.ALL, 10)
498
499    def on_remove(self, event):
500        """
501        Remove constraint fields
502        """
503        if len(self.constraints_list) == 1:
504            self.hide_constraint.SetValue(True)
505            self._hide_constraint()
506            return
507        if len(self.constraints_list) == 0:
508            return
509        wx.CallAfter(self._remove_after, event.GetId())
510        # self._onAdd_constraint(None)
511
512    def _remove_after(self, id):
513        for item in self.constraints_list:
514            if id == item.btRemove.GetId():
515                self.sizer_constraints.Hide(item.sizer)
516                item.sizer.Clear(True)
517                self.sizer_constraints.Remove(item.sizer)
518                self.constraints_list.remove(item)
519                self.nb_constraint -= 1
520                self.constraints_sizer.Layout()
521                self.FitInside()
522                break
523
524    def on_fit(self, event):
525        """
526        signal for fitting
527
528        """
529        if self.fit_started:
530            self._stop_fit()
531            self.fit_started = False
532            return
533
534        flag = False
535        # check if the current page a simultaneous fit page or a batch page
536        if self == self._manager.sim_page:
537            flag = (self._manager.sim_page.uid == self.uid)
538
539        # making sure all parameters content a constraint
540        if not self.batch_on and self.show_constraint.GetValue():
541            if not self._set_constraint():
542                return
543        # model was actually selected from this page to be fit
544        if len(self.model_to_fit) >= 1:
545            self.manager._reset_schedule_problem(value=0)
546            for item in self.model_list:
547                if item[0].GetValue():
548                    self.manager.schedule_for_fit(value=1, uid=item[2])
549            try:
550                self.fit_started = True
551                wx.CallAfter(self.set_fitbutton)
552                if not self.manager.onFit(uid=self.uid):
553                    return
554            except:
555                msg = "Select at least one parameter to fit in the FitPages."
556                wx.PostEvent(self.parent.parent, StatusEvent(status=msg))
557        else:
558            msg = "Select at least one model check box to fit "
559            wx.PostEvent(self.parent.parent, StatusEvent(status=msg))
560        self.set_state()
561
562    def _on_fit_complete(self):
563        """
564        Set the completion flag and display the updated fit button label.
565        """
566        self.fit_started = False
567        self.set_fitbutton()
568
569    def _stop_fit(self, event=None):
570        """
571        Attempt to stop the fitting thread
572
573        :param event: Event handler when stop fit is clicked
574        """
575        if event is not None:
576            event.Skip()
577        self.manager.stop_fit(self.uid)
578        self.manager._reset_schedule_problem(value=0)
579        self._on_fit_complete()
580
581    def set_fitbutton(self):
582        """
583        Set fit button label depending on the fit_started
584        """
585        label = "Stop" if self.fit_started else "Fit"
586        color = "red" if self.fit_started else "black"
587
588        self.btFit.SetLabel(label)
589        self.btFit.SetForegroundColour(color)
590        self.btFit.Enable(True)
591
592    def _on_help(self, event):
593        """
594        Bring up the simultaneous Fitting Documentation whenever the HELP
595        button is clicked.
596
597        Calls DocumentationWindow with the path of the location within the
598        documentation tree (after /doc/ ....".  Note that when using old
599        versions of Wx (before 2.9) and thus not the release version of
600        installers, the help comes up at the top level of the file as
601        web browser does not pass anything past the # to the browser when it is
602        running "file:///...."
603
604    :param event: Triggers on clicking the help button
605    """
606        _TreeLocation = "user/sasgui/perspectives/fitting/fitting_help.html"
607        if not self.batch_on:
608            _PageAnchor = "#simultaneous-fit-mode"
609            _doc_viewer = DocumentationWindow(self, self.ID_DOC, _TreeLocation,
610                                          _PageAnchor,
611                                          "Simultaneous/Constrained Fitting Help")
612        else:
613            _PageAnchor = "#combined-batch-fit-mode"
614            _doc_viewer = DocumentationWindow(self, self.ID_DOC, _TreeLocation,
615                                          _PageAnchor,
616                                          "Combined Batch Fit Help")
617
618    def set_manager(self, manager):
619        """
620        set panel manager
621
622        :param manager: instance of plugin fitting
623        """
624        self.manager = manager
625
626    def check_all_model_name(self, event=None):
627        """
628        check all models names
629        """
630        self.model_to_fit = []
631        if self.cb1.GetValue():
632            for item in self.model_list:
633                if item[0].IsEnabled():
634                    item[0].SetValue(True)
635                    self.model_to_fit.append(item)
636
637            # constraint info
638            self._store_model()
639            if not self.batch_on:
640                # display constraint fields
641                if (self.show_constraint.GetValue() and
642                                 len(self.constraints_list) == 0):
643                    self._show_all_constraint()
644                    self._show_constraint()
645        else:
646            for item in self.model_list:
647                item[0].SetValue(False)
648
649            if not self.batch_on:
650                # constraint info
651                self._hide_constraint()
652
653        self._update_easy_setup_cb()
654        self.FitInside()
655
656    def check_model_name(self, event):
657        """
658        Save information related to checkbox and their states
659        """
660        self.model_to_fit = []
661        for item in self.model_list:
662            if item[0].GetValue():
663                self.model_to_fit.append(item)
664            else:
665                if item in self.model_to_fit:
666                    self.model_to_fit.remove(item)
667                    self.cb1.SetValue(False)
668
669        # display constraint fields
670        if len(self.model_to_fit) >= 1:
671            self._store_model()
672            if not self.batch_on and self.show_constraint.GetValue() and\
673                             len(self.constraints_list) == 0:
674                self._show_all_constraint()
675                self._show_constraint()
676
677        elif len(self.model_to_fit) < 1:
678            # constraint info
679            self._hide_constraint()
680
681        self._update_easy_setup_cb()
682        # set the value of the main check button
683        if len(self.model_list) == len(self.model_to_fit):
684            self.cb1.SetValue(True)
685            self.FitInside()
686            return
687        else:
688            self.cb1.SetValue(False)
689            self.FitInside()
690
691    def _update_easy_setup_cb(self):
692        """
693        Update easy setup combobox on selecting a model
694        """
695        if self.model_cbox_left is None or self.model_cbox_right is None:
696            return
697
698        models = [(item[3].name, item[3]) for item in self.model_to_fit]
699        setComboBoxItems(self.model_cbox_left, models)
700        setComboBoxItems(self.model_cbox_right, models)
701        for item in self.constraints_list:
702            setComboBoxItems(item[0], models)
703        if self.model_cbox_left.GetSelection() == wx.NOT_FOUND:
704            self.model_cbox_left.SetSelection(0)
705        self.constraints_sizer.Layout()
706
707    def _store_model(self):
708        """
709         Store selected model
710        """
711        if len(self.model_to_fit) < 1:
712            return
713        for item in self.model_to_fit:
714            model = item[3]
715            page_id = item[2]
716            self.constraint_dict[page_id] = model
717
718    def _display_constraint(self, event):
719        """
720        Show fields to add constraint
721        """
722        if len(self.model_to_fit) < 1:
723            msg = "Select at least 1 model to add constraint "
724            wx.PostEvent(self.parent.parent, StatusEvent(status=msg))
725            # hide button
726            self._hide_constraint()
727            return
728        if self.show_constraint.GetValue():
729            self._show_all_constraint()
730            self._show_constraint()
731            self.FitInside()
732            return
733        else:
734            self._hide_constraint()
735            return
736
737    def _show_all_constraint(self):
738        """
739        Show constraint fields
740        """
741        box_description = wx.StaticBox(self, wx.ID_ANY, "Easy Setup ")
742        box_sizer = wx.StaticBoxSizer(box_description, wx.HORIZONTAL)
743        sizer_constraint = wx.BoxSizer(wx.HORIZONTAL)
744        self.model_cbox_left = wx.ComboBox(self, wx.ID_ANY, style=wx.CB_READONLY)
745        self.model_cbox_left.Clear()
746        self.model_cbox_right = wx.ComboBox(self, wx.ID_ANY, style=wx.CB_READONLY)
747        self.model_cbox_right.Clear()
748        wx.EVT_COMBOBOX(self.model_cbox_left, wx.ID_ANY, self._on_select_modelcb)
749        wx.EVT_COMBOBOX(self.model_cbox_right, wx.ID_ANY, self._on_select_modelcb)
750        egal_txt = wx.StaticText(self, wx.ID_ANY, " = ")
751        self.set_button = wx.Button(self, self.ID_SET_ALL, 'Set All')
752        self.set_button.Bind(wx.EVT_BUTTON, self._on_set_all_equal,
753                             id=self.set_button.GetId())
754        set_tip = "Add constraints for all the adjustable parameters "
755        set_tip += "(checked in FitPages) if exist."
756        self.set_button.SetToolTipString(set_tip)
757        self.set_button.Disable()
758
759        for id, model in self.constraint_dict.iteritems():
760            # check if all parameters have been selected for constraint
761            # then do not allow add constraint on parameters
762            self.model_cbox_left.Append(str(model.name), model)
763        self.model_cbox_left.Select(0)
764        for id, model in self.constraint_dict.iteritems():
765            # check if all parameters have been selected for constraint
766            # then do not allow add constraint on parameters
767            self.model_cbox_right.Append(str(model.name), model)
768        box_sizer.Add(self.model_cbox_left,
769                             flag=wx.RIGHT | wx.EXPAND, border=10)
770        # box_sizer.Add(wx.StaticText(self, wx.ID_ANY, ".parameters"),
771        #                     flag=wx.RIGHT | wx.EXPAND, border=5)
772        box_sizer.Add(egal_txt, flag=wx.RIGHT | wx.EXPAND, border=5)
773        box_sizer.Add(self.model_cbox_right,
774                             flag=wx.RIGHT | wx.EXPAND, border=10)
775        # box_sizer.Add(wx.StaticText(self, wx.ID_ANY, ".parameters"),
776        #                     flag=wx.RIGHT | wx.EXPAND, border=5)
777        box_sizer.Add((20, -1))
778        box_sizer.Add(self.set_button, flag=wx.RIGHT | wx.EXPAND, border=5)
779        sizer_constraint.Add(box_sizer, flag=wx.RIGHT | wx.EXPAND, border=5)
780        self.sizer_all_constraints.Insert(before=0,
781                             item=sizer_constraint,
782                             flag=wx.TOP | wx.BOTTOM | wx.EXPAND, border=5)
783        self.FitInside()
784
785    def _on_select_modelcb(self, event):
786        """
787        On select model left or right combobox
788        """
789        event.Skip()
790        flag = True
791        if self.model_cbox_left.GetValue().strip() == '':
792            flag = False
793        if self.model_cbox_right.GetValue().strip() == '':
794            flag = False
795        if (self.model_cbox_left.GetValue() ==
796                self.model_cbox_right.GetValue()):
797            flag = False
798        self.set_button.Enable(flag)
799
800    def _on_set_all_equal(self, event):
801        """
802        On set button
803        """
804        event.Skip()
805        length = len(self.constraints_list)
806        if length < 1:
807            return
808        param_list = []
809        param_list_b = []
810        selection = self.model_cbox_left.GetCurrentSelection()
811        model_left = self.model_cbox_left.GetValue()
812        model = self.model_cbox_left.GetClientData(selection)
813        selection_b = self.model_cbox_right.GetCurrentSelection()
814        model_right = self.model_cbox_right.GetValue()
815        model_b = self.model_cbox_right.GetClientData(selection_b)
816        for id, dic_model in self.constraint_dict.iteritems():
817            if model == dic_model:
818                param_list = self.page_finder[id].get_param2fit()
819            if model_b == dic_model:
820                param_list_b = self.page_finder[id].get_param2fit()
821            if len(param_list) > 0 and len(param_list_b) > 0:
822                break
823        num_cbox = 0
824        has_param = False
825        for param in param_list:
826            num_cbox += 1
827            if param in param_list_b:
828                item = self.constraints_list[-1]
829                item.model_cbox.SetStringSelection(model_left)
830                self._on_select_model(None)
831                item.param_cbox.Clear()
832                item.param_cbox.Append(str(param), model)
833                item.param_cbox.SetStringSelection(str(param))
834                item.constraint.SetValue(str(model_right + "." + str(param)))
835                has_param = True
836                if num_cbox == (len(param_list) + 1):
837                    break
838                self._show_constraint()
839
840        self.FitInside()
841        if not has_param:
842            msg = " There is no adjustable parameter (checked to fit)"
843            msg += " either one of the models."
844            wx.PostEvent(self.parent.parent, StatusEvent(info="warning",
845                                                         status=msg))
846        else:
847            msg = " The constraints are added."
848            wx.PostEvent(self.parent.parent, StatusEvent(info="info",
849                                                         status=msg))
850
851    def _show_constraint(self):
852        """
853        Show constraint fields
854        :param dict: dictionary mapping constraint values
855        """
856        self.btAdd.Show(True)
857        if len(self.constraints_list) != 0:
858            nb_fit_param = 0
859            for id, model in self.constraint_dict.iteritems():
860                nb_fit_param += len(self.page_finder[id].get_param2fit())
861            # Don't add anymore
862            if len(self.constraints_list) == nb_fit_param:
863                msg = "Cannot add another constraint. Maximum of number "
864                msg += "Parameters name reached %s" % str(nb_fit_param)
865                wx.PostEvent(self.parent.parent, StatusEvent(status=msg))
866                self.sizer_constraints.Layout()
867                self.constraints_sizer.Layout()
868                return
869        if len(self.model_to_fit) < 1:
870            msg = "Select at least 1 model to add constraint "
871            wx.PostEvent(self.parent.parent, StatusEvent(status=msg))
872            self.sizer_constraints.Layout()
873            self.constraints_sizer.Layout()
874            return
875
876        sizer_constraint = wx.BoxSizer(wx.HORIZONTAL)
877
878        # Model list
879        model_cbox = wx.ComboBox(self, wx.ID_ANY, style=wx.CB_READONLY)
880        model_cbox.Clear()
881        for id, model in self.constraint_dict.iteritems():
882            # check if all parameters have been selected for constraint
883            # then do not allow add constraint on parameters
884            model_cbox.Append(str(model.name), model)
885        wx.EVT_COMBOBOX(model_cbox, wx.ID_ANY, self._on_select_model)
886
887        # Parameters in model
888        param_cbox = wx.ComboBox(self, wx.ID_ANY, style=wx.CB_READONLY,
889                                 size=(100, -1))
890        param_cbox.Hide()
891        wx.EVT_COMBOBOX(param_cbox, wx.ID_ANY, self._on_select_param)
892
893        egal_txt = wx.StaticText(self, wx.ID_ANY, " = ")
894
895        # Parameter constraint
896        constraint = wx.TextCtrl(self, wx.ID_ANY)
897
898        # Remove button
899        #btRemove = wx.Button(self, self.ID_REMOVE, 'Remove')
900        bt_remove = wx.Button(self, self._ids.next(), 'Remove')
901        bt_remove.Bind(wx.EVT_BUTTON, self.on_remove,
902                      id=bt_remove.GetId())
903        bt_remove.SetToolTipString("Remove constraint.")
904        bt_remove.Hide()
905
906        # Hid the add button, if it exists
907        if hasattr(self, "btAdd"):
908            self.btAdd.Hide()
909
910        sizer_constraint.Add((5, -1))
911        sizer_constraint.Add(model_cbox, flag=wx.RIGHT | wx.EXPAND, border=10)
912        sizer_constraint.Add(param_cbox, flag=wx.RIGHT | wx.EXPAND, border=5)
913        sizer_constraint.Add(egal_txt, flag=wx.RIGHT | wx.EXPAND, border=5)
914        sizer_constraint.Add(constraint, flag=wx.RIGHT | wx.EXPAND, border=10)
915        sizer_constraint.Add(bt_remove, flag=wx.RIGHT | wx.EXPAND, border=10)
916
917        self.sizer_constraints.Insert(before=self.nb_constraint,
918                item=sizer_constraint, flag=wx.TOP | wx.BOTTOM | wx.EXPAND,
919                border=5)
920        c = ConstraintLine(model_cbox, param_cbox, egal_txt,
921                           constraint, bt_remove, sizer_constraint)
922        self.constraints_list.append(c)
923
924        self.nb_constraint += 1
925        self.sizer_constraints.Layout()
926        self.constraints_sizer.Layout()
927        self.Layout()
928
929    def _hide_constraint(self):
930        """
931        hide buttons related constraint
932        """
933        for id in self.page_finder.iterkeys():
934            self.page_finder[id].clear_model_param()
935
936        self.nb_constraint = 0
937        self.constraint_dict = {}
938        if hasattr(self, "btAdd"):
939            self.btAdd.Hide()
940        self._store_model()
941        if self.model_cbox_left is not None:
942            self.model_cbox_left.Clear()
943            self.model_cbox_left = None
944        if self.model_cbox_right is not None:
945            self.model_cbox_right.Clear()
946            self.model_cbox_right = None
947        self.constraints_list = []
948        self.sizer_all_constraints.Clear(True)
949        self.sizer_all_constraints.Layout()
950        self.sizer_constraints.Clear(True)
951        self.sizer_constraints.Layout()
952        self.constraints_sizer.Layout()
953        self.Layout()
954        self.FitInside()
955
956    def _on_select_model(self, event):
957        """
958        fill combo box with list of parameters
959        """
960        if not self.constraints_list:
961            return
962
963        # This way PC/MAC both work, instead of using event.GetClientData().
964        model_cbox = self.constraints_list[-1].model_cbox
965        n = model_cbox.GetCurrentSelection()
966        if n == wx.NOT_FOUND:
967            return
968
969        model = model_cbox.GetClientData(n)
970        param_list = []
971        for id, dic_model in self.constraint_dict.iteritems():
972            if model == dic_model:
973                param_list = self.page_finder[id].get_param2fit()
974                break
975
976        param_cbox = self.constraints_list[-1].param_cbox
977        param_cbox.Clear()
978        # insert only fittable paramaters
979        for param in param_list:
980            param_cbox.Append(str(param), model)
981        param_cbox.Show(True)
982
983        bt_remove = self.constraints_list[-1].btRemove
984        bt_remove.Show(True)
985        self.btAdd.Show(True)
986#        self.Layout()
987        self.FitInside()
988
989    def _on_select_param(self, event):
990        """
991        Store the appropriate constraint in the page_finder
992        """
993        # This way PC/MAC both work, instead of using event.GetClientData().
994        # n = self.param_cbox.GetCurrentSelection()
995        # model = self.param_cbox.GetClientData(n)
996        # param = event.GetString()
997
998        if self.constraints_list:
999            self.constraints_list[-1].egal_txt.Show(True)
1000            self.constraints_list[-1].constraint.Show(True)
1001
1002    def _on_add_constraint(self, event):
1003        """
1004        Add another line for constraint
1005        """
1006        if not self.show_constraint.GetValue():
1007            msg = " Select Yes to add Constraint "
1008            wx.PostEvent(self.parent.parent, StatusEvent(status=msg))
1009            return
1010        # check that a constraint is added
1011        # before allow to add another constraint
1012        for item in self.constraints_list:
1013            if item.model_cbox.GetString(0) == "":
1014                msg = " Select a model Name! "
1015                wx.PostEvent(self.parent.parent, StatusEvent(status=msg))
1016                return
1017            if item.param_cbox.GetString(0) == "":
1018                msg = " Select a parameter Name! "
1019                wx.PostEvent(self.parent.parent, StatusEvent(status=msg))
1020                return
1021            if item.constraint.GetValue().lstrip().rstrip() == "":
1022                model = item.param_cbox.GetClientData(
1023                                        item.param_cbox.GetCurrentSelection())
1024                if model is not None:
1025                    msg = " Enter a constraint for %s.%s! " % (model.name,
1026                                        item.param_cbox.GetString(0))
1027                else:
1028                    msg = " Enter a constraint"
1029                wx.PostEvent(self.parent.parent, StatusEvent(status=msg))
1030                return
1031        # some model or parameters can be constrained
1032        self._show_constraint()
1033        self.FitInside()
1034
1035    def _set_constraint(self):
1036        """
1037        get values from the constraint textcrtl ,parses them into model name
1038        parameter name and parameters values.
1039        store them in a list self.params .when when params is not empty
1040        set_model uses it to reset the appropriate model
1041        and its appropriates parameters
1042        """
1043        for item in self.constraints_list:
1044            select0 = item.model_cbox.GetSelection()
1045            if select0 == wx.NOT_FOUND:
1046                continue
1047            model = item.model_cbox.GetClientData(select0)
1048            select1 = item.param_cbox.GetSelection()
1049            if select1 == wx.NOT_FOUND:
1050                continue
1051            param = item.param_cbox.GetString(select1)
1052            constraint = item.constraint.GetValue().lstrip().rstrip()
1053            if param.lstrip().rstrip() == "":
1054                param = None
1055                msg = " Constraint will be ignored!. missing parameters"
1056                msg += " in combobox to set constraint! "
1057                wx.PostEvent(self.parent.parent, StatusEvent(status=msg))
1058            for id, value in self.constraint_dict.iteritems():
1059                if model == value:
1060                    if constraint == "":
1061                        msg = " Constraint will be ignored!. missing value"
1062                        msg += " in textcrtl to set constraint! "
1063                        wx.PostEvent(self.parent.parent,
1064                                     StatusEvent(status=msg))
1065                        constraint = None
1066                    if str(param) in self.page_finder[id].get_param2fit():
1067                        msg = " Checking constraint for parameter: %s ", param
1068                        wx.PostEvent(self.parent.parent,
1069                                     StatusEvent(info="info", status=msg))
1070                    else:
1071                        model_name = item[0].GetLabel()
1072                        fitpage = self.page_finder[id].get_fit_tab_caption()
1073                        msg = "All constrainted parameters must be set "
1074                        msg += " adjustable: '%s.%s' " % (model_name, param)
1075                        msg += "is NOT checked in '%s'. " % fitpage
1076                        msg += " Please check it to fit or"
1077                        msg += " remove the line of the constraint."
1078                        wx.PostEvent(self.parent.parent,
1079                                StatusEvent(info="error", status=msg))
1080                        return False
1081
1082                    for fid in self.page_finder[id].iterkeys():
1083                        # wrap in param/constraint in str() to remove unicode
1084                        self.page_finder[id].set_model_param(str(param),
1085                                str(constraint), fid=fid)
1086                    break
1087        return True
1088
1089    def on_set_focus(self, event=None):
1090        """
1091        The derivative class is on focus if implemented
1092        """
1093        if self.parent is not None:
1094            if self.parent.parent is not None:
1095                wx.PostEvent(self.parent.parent, PanelOnFocusEvent(panel=self))
1096            self.page_finder = self.parent._manager.get_page_finder()
1097
1098
1099def setComboBoxItems(cbox, items):
1100    assert isinstance(cbox, wx.ComboBox)
1101    selected = cbox.GetStringSelection()
1102    cbox.Clear()
1103    for k, (name, value) in enumerate(items):
1104        cbox.Append(name, value)
1105    cbox.SetStringSelection(selected)
Note: See TracBrowser for help on using the repository browser.