source: sasview/src/sas/sasgui/perspectives/fitting/simfitpage.py @ 69a6897

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 69a6897 was 24d9e84, checked in by Paul Kienzle <pkienzle@…>, 7 years ago

forgot to save sim_fit_page before completing merge

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