source: sasview/src/sas/sasgui/perspectives/fitting/simfitpage.py @ 7b8e843

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalccostrafo411magnetic_scattrelease-4.2.2ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since 7b8e843 was 7b8e843, checked in by butler, 7 years ago

fix where the HELP button links to in the documentation when in combined
batch fit mode. Adresses #597

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