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

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalccostrafo411magnetic_scattrelease-4.1.1release-4.1.2release-4.2.2ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since a95ae9a was a95ae9a, checked in by krzywon, 8 years ago

#189: Modified save project to include information from simultaneous fit panel. Next step is to load in the parameters and repopulate the simultaneous and constrained fit panel.

  • Property mode set to 100644
File size: 40.6 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        _TreeLocation = "user/sasgui/perspectives/fitting/fitting_help.html"
529        _PageAnchor = "#simultaneous-fit-mode"
530        _doc_viewer = DocumentationWindow(self, self.ID_DOC, _TreeLocation,
531                                          _PageAnchor,
532                                          "Simultaneous/Constrained Fitting Help")
533
534    def set_manager(self, manager):
535        """
536        set panel manager
537
538        :param manager: instance of plugin fitting
539        """
540        self.manager = manager
541
542    def check_all_model_name(self, event=None):
543        """
544        check all models names
545        """
546        self.model_to_fit = []
547        if self.cb1.GetValue():
548            for item in self.model_list:
549                if item[0].IsEnabled():
550                    item[0].SetValue(True)
551                    self.model_to_fit.append(item)
552
553            # constraint info
554            self._store_model()
555            if not self.batch_on:
556                # display constraint fields
557                if (self.show_constraint.GetValue() and
558                                 len(self.constraints_list) == 0):
559                    self._show_all_constraint()
560                    self._show_constraint()
561        else:
562            for item in self.model_list:
563                item[0].SetValue(False)
564
565            if not self.batch_on:
566                # constraint info
567                self._hide_constraint()
568
569        self._update_easy_setup_cb()
570        self.FitInside()
571
572    def check_model_name(self, event):
573        """
574        Save information related to checkbox and their states
575        """
576        self.model_to_fit = []
577        for item in self.model_list:
578            if item[0].GetValue():
579                self.model_to_fit.append(item)
580            else:
581                if item in self.model_to_fit:
582                    self.model_to_fit.remove(item)
583                    self.cb1.SetValue(False)
584
585        # display constraint fields
586        if len(self.model_to_fit) >= 1:
587            self._store_model()
588            if not self.batch_on and self.show_constraint.GetValue() and\
589                             len(self.constraints_list) == 0:
590                self._show_all_constraint()
591                self._show_constraint()
592
593        elif len(self.model_to_fit) < 1:
594            # constraint info
595            self._hide_constraint()
596
597        self._update_easy_setup_cb()
598        # set the value of the main check button
599        if len(self.model_list) == len(self.model_to_fit):
600            self.cb1.SetValue(True)
601            self.FitInside()
602            return
603        else:
604            self.cb1.SetValue(False)
605            self.FitInside()
606
607    def _update_easy_setup_cb(self):
608        """
609        Update easy setup combobox on selecting a model
610        """
611        if self.model_cbox_left is None or self.model_cbox_right is None:
612            return
613
614        models = [(item[3].name, item[3]) for item in self.model_to_fit]
615        setComboBoxItems(self.model_cbox_left, models)
616        setComboBoxItems(self.model_cbox_right, models)
617        for item in self.constraints_list:
618            setComboBoxItems(item[0], models)
619        if self.model_cbox_left.GetSelection() == wx.NOT_FOUND:
620            self.model_cbox_left.SetSelection(0)
621        self.constraints_sizer.Layout()
622
623    def _store_model(self):
624        """
625         Store selected model
626        """
627        if len(self.model_to_fit) < 1:
628            return
629        for item in self.model_to_fit:
630            model = item[3]
631            page_id = item[2]
632            self.constraint_dict[page_id] = model
633
634    def _display_constraint(self, event):
635        """
636        Show fields to add constraint
637        """
638        if len(self.model_to_fit) < 1:
639            msg = "Select at least 1 model to add constraint "
640            wx.PostEvent(self.parent.parent, StatusEvent(status=msg))
641            # hide button
642            self._hide_constraint()
643            return
644        if self.show_constraint.GetValue():
645            self._show_all_constraint()
646            self._show_constraint()
647            self.FitInside()
648            return
649        else:
650            self._hide_constraint()
651            return
652
653    def _show_all_constraint(self):
654        """
655        Show constraint fields
656        """
657        box_description = wx.StaticBox(self, wx.ID_ANY, "Easy Setup ")
658        box_sizer = wx.StaticBoxSizer(box_description, wx.HORIZONTAL)
659        sizer_constraint = wx.BoxSizer(wx.HORIZONTAL)
660        self.model_cbox_left = wx.ComboBox(self, wx.ID_ANY, style=wx.CB_READONLY)
661        self.model_cbox_left.Clear()
662        self.model_cbox_right = wx.ComboBox(self, wx.ID_ANY, style=wx.CB_READONLY)
663        self.model_cbox_right.Clear()
664        wx.EVT_COMBOBOX(self.model_cbox_left, wx.ID_ANY, self._on_select_modelcb)
665        wx.EVT_COMBOBOX(self.model_cbox_right, wx.ID_ANY, self._on_select_modelcb)
666        egal_txt = wx.StaticText(self, wx.ID_ANY, " = ")
667        self.set_button = wx.Button(self, self.ID_SET_ALL, 'Set All')
668        self.set_button.Bind(wx.EVT_BUTTON, self._on_set_all_equal,
669                             id=self.set_button.GetId())
670        set_tip = "Add constraints for all the adjustable parameters "
671        set_tip += "(checked in FitPages) if exist."
672        self.set_button.SetToolTipString(set_tip)
673        self.set_button.Disable()
674
675        for id, model in self.constraint_dict.iteritems():
676            # check if all parameters have been selected for constraint
677            # then do not allow add constraint on parameters
678            self.model_cbox_left.Append(str(model.name), model)
679        self.model_cbox_left.Select(0)
680        for id, model in self.constraint_dict.iteritems():
681            # check if all parameters have been selected for constraint
682            # then do not allow add constraint on parameters
683            self.model_cbox_right.Append(str(model.name), model)
684        box_sizer.Add(self.model_cbox_left,
685                             flag=wx.RIGHT | wx.EXPAND, border=10)
686        # box_sizer.Add(wx.StaticText(self, wx.ID_ANY, ".parameters"),
687        #                     flag=wx.RIGHT | wx.EXPAND, border=5)
688        box_sizer.Add(egal_txt, flag=wx.RIGHT | wx.EXPAND, border=5)
689        box_sizer.Add(self.model_cbox_right,
690                             flag=wx.RIGHT | wx.EXPAND, border=10)
691        # box_sizer.Add(wx.StaticText(self, wx.ID_ANY, ".parameters"),
692        #                     flag=wx.RIGHT | wx.EXPAND, border=5)
693        box_sizer.Add((20, -1))
694        box_sizer.Add(self.set_button, flag=wx.RIGHT | wx.EXPAND, border=5)
695        sizer_constraint.Add(box_sizer, flag=wx.RIGHT | wx.EXPAND, border=5)
696        self.sizer_all_constraints.Insert(before=0,
697                             item=sizer_constraint,
698                             flag=wx.TOP | wx.BOTTOM | wx.EXPAND, border=5)
699        self.FitInside()
700
701    def _on_select_modelcb(self, event):
702        """
703        On select model left or right combobox
704        """
705        event.Skip()
706        flag = True
707        if self.model_cbox_left.GetValue().strip() == '':
708            flag = False
709        if self.model_cbox_right.GetValue().strip() == '':
710            flag = False
711        if (self.model_cbox_left.GetValue() ==
712                self.model_cbox_right.GetValue()):
713            flag = False
714        self.set_button.Enable(flag)
715
716    def _on_set_all_equal(self, event):
717        """
718        On set button
719        """
720        event.Skip()
721        length = len(self.constraints_list)
722        if length < 1:
723            return
724        param_list = []
725        param_list_b = []
726        selection = self.model_cbox_left.GetCurrentSelection()
727        model_left = self.model_cbox_left.GetValue()
728        model = self.model_cbox_left.GetClientData(selection)
729        selection_b = self.model_cbox_right.GetCurrentSelection()
730        model_right = self.model_cbox_right.GetValue()
731        model_b = self.model_cbox_right.GetClientData(selection_b)
732        for id, dic_model in self.constraint_dict.iteritems():
733            if model == dic_model:
734                param_list = self.page_finder[id].get_param2fit()
735            if model_b == dic_model:
736                param_list_b = self.page_finder[id].get_param2fit()
737            if len(param_list) > 0 and len(param_list_b) > 0:
738                break
739        num_cbox = 0
740        has_param = False
741        for param in param_list:
742            num_cbox += 1
743            if param in param_list_b:
744                item = self.constraints_list[-1]
745                item.model_cbox.SetStringSelection(model_left)
746                self._on_select_model(None)
747                item.param_cbox.Clear()
748                item.param_cbox.Append(str(param), model)
749                item.param_cbox.SetStringSelection(str(param))
750                item.constraint.SetValue(str(model_right + "." + str(param)))
751                has_param = True
752                if num_cbox == (len(param_list) + 1):
753                    break
754                self._show_constraint()
755
756        self.FitInside()
757        if not has_param:
758            msg = " There is no adjustable parameter (checked to fit)"
759            msg += " either one of the models."
760            wx.PostEvent(self.parent.parent, StatusEvent(info="warning",
761                                                         status=msg))
762        else:
763            msg = " The constraints are added."
764            wx.PostEvent(self.parent.parent, StatusEvent(info="info",
765                                                         status=msg))
766
767    def _show_constraint(self):
768        """
769        Show constraint fields
770        """
771        self.btAdd.Show(True)
772        if len(self.constraints_list) != 0:
773            nb_fit_param = 0
774            for id, model in self.constraint_dict.iteritems():
775                nb_fit_param += len(self.page_finder[id].get_param2fit())
776            # Don't add anymore
777            if len(self.constraints_list) == nb_fit_param:
778                msg = "Cannot add another constraint. Maximum of number "
779                msg += "Parameters name reached %s" % str(nb_fit_param)
780                wx.PostEvent(self.parent.parent, StatusEvent(status=msg))
781                self.sizer_constraints.Layout()
782                self.constraints_sizer.Layout()
783                return
784        if len(self.model_to_fit) < 1:
785            msg = "Select at least 1 model to add constraint "
786            wx.PostEvent(self.parent.parent, StatusEvent(status=msg))
787            self.sizer_constraints.Layout()
788            self.constraints_sizer.Layout()
789            return
790
791        sizer_constraint = wx.BoxSizer(wx.HORIZONTAL)
792
793        # Model list
794        model_cbox = wx.ComboBox(self, wx.ID_ANY, style=wx.CB_READONLY)
795        model_cbox.Clear()
796        for id, model in self.constraint_dict.iteritems():
797            # check if all parameters have been selected for constraint
798            # then do not allow add constraint on parameters
799            model_cbox.Append(str(model.name), model)
800        wx.EVT_COMBOBOX(model_cbox, wx.ID_ANY, self._on_select_model)
801
802        # Parameters in model
803        param_cbox = wx.ComboBox(self, wx.ID_ANY, style=wx.CB_READONLY,
804                                 size=(100, -1))
805        param_cbox.Hide()
806        wx.EVT_COMBOBOX(param_cbox, wx.ID_ANY, self._on_select_param)
807
808        egal_txt = wx.StaticText(self, wx.ID_ANY, " = ")
809
810        # Parameter constraint
811        constraint = wx.TextCtrl(self, wx.ID_ANY)
812
813        # Remove button
814        #btRemove = wx.Button(self, self.ID_REMOVE, 'Remove')
815        bt_remove = wx.Button(self, self._ids.next(), 'Remove')
816        bt_remove.Bind(wx.EVT_BUTTON, self.on_remove,
817                      id=bt_remove.GetId())
818        bt_remove.SetToolTipString("Remove constraint.")
819        bt_remove.Hide()
820
821        # Hid the add button, if it exists
822        if hasattr(self, "btAdd"):
823            self.btAdd.Hide()
824
825        sizer_constraint.Add((5, -1))
826        sizer_constraint.Add(model_cbox, flag=wx.RIGHT | wx.EXPAND, border=10)
827        sizer_constraint.Add(param_cbox, flag=wx.RIGHT | wx.EXPAND, border=5)
828        sizer_constraint.Add(egal_txt, flag=wx.RIGHT | wx.EXPAND, border=5)
829        sizer_constraint.Add(constraint, flag=wx.RIGHT | wx.EXPAND, border=10)
830        sizer_constraint.Add(bt_remove, flag=wx.RIGHT | wx.EXPAND, border=10)
831
832        self.sizer_constraints.Insert(before=self.nb_constraint,
833                item=sizer_constraint, flag=wx.TOP | wx.BOTTOM | wx.EXPAND,
834                border=5)
835        c = ConstraintLine(model_cbox, param_cbox, egal_txt,
836                           constraint, bt_remove, sizer_constraint)
837        self.constraints_list.append(c)
838
839        self.nb_constraint += 1
840        self.sizer_constraints.Layout()
841        self.constraints_sizer.Layout()
842        self.Layout()
843
844    def _hide_constraint(self):
845        """
846        hide buttons related constraint
847        """
848        for id in self.page_finder.iterkeys():
849            self.page_finder[id].clear_model_param()
850
851        self.nb_constraint = 0
852        self.constraint_dict = {}
853        if hasattr(self, "btAdd"):
854            self.btAdd.Hide()
855        self._store_model()
856        if self.model_cbox_left is not None:
857            self.model_cbox_left.Clear()
858            self.model_cbox_left = None
859        if self.model_cbox_right is not None:
860            self.model_cbox_right.Clear()
861            self.model_cbox_right = None
862        self.constraints_list = []
863        self.sizer_all_constraints.Clear(True)
864        self.sizer_all_constraints.Layout()
865        self.sizer_constraints.Clear(True)
866        self.sizer_constraints.Layout()
867        self.constraints_sizer.Layout()
868        self.Layout()
869        self.FitInside()
870
871    def _on_select_model(self, event):
872        """
873        fill combox box with list of parameters
874        """
875        if not self.constraints_list:
876            return
877
878        # This way PC/MAC both work, instead of using event.GetClientData().
879        model_cbox = self.constraints_list[-1].model_cbox
880        n = model_cbox.GetCurrentSelection()
881        if n == wx.NOT_FOUND:
882            return
883
884        model = model_cbox.GetClientData(n)
885        param_list = []
886        for id, dic_model in self.constraint_dict.iteritems():
887            if model == dic_model:
888                param_list = self.page_finder[id].get_param2fit()
889                break
890
891        param_cbox = self.constraints_list[-1].param_cbox
892        param_cbox.Clear()
893        # insert only fittable paramaters
894        for param in param_list:
895            param_cbox.Append(str(param), model)
896        param_cbox.Show(True)
897
898        bt_remove = self.constraints_list[-1].btRemove
899        bt_remove.Show(True)
900        self.btAdd.Show(True)
901#        self.Layout()
902        self.FitInside()
903
904    def _on_select_param(self, event):
905        """
906        Store the appropriate constraint in the page_finder
907        """
908        # This way PC/MAC both work, instead of using event.GetClientData().
909        # n = self.param_cbox.GetCurrentSelection()
910        # model = self.param_cbox.GetClientData(n)
911        # param = event.GetString()
912
913        if self.constraints_list:
914            self.constraints_list[-1].egal_txt.Show(True)
915            self.constraints_list[-1].constraint.Show(True)
916
917    def _on_add_constraint(self, event):
918        """
919        Add another line for constraint
920        """
921        if not self.show_constraint.GetValue():
922            msg = " Select Yes to add Constraint "
923            wx.PostEvent(self.parent.parent, StatusEvent(status=msg))
924            return
925        # check that a constraint is added
926        # before allow to add another constraint
927        for item in self.constraints_list:
928            if item.model_cbox.GetString(0) == "":
929                msg = " Select a model Name! "
930                wx.PostEvent(self.parent.parent, StatusEvent(status=msg))
931                return
932            if item.param_cbox.GetString(0) == "":
933                msg = " Select a parameter Name! "
934                wx.PostEvent(self.parent.parent, StatusEvent(status=msg))
935                return
936            if item.constraint.GetValue().lstrip().rstrip() == "":
937                model = item.param_cbox.GetClientData(
938                                        item.param_cbox.GetCurrentSelection())
939                if model is not None:
940                    msg = " Enter a constraint for %s.%s! " % (model.name,
941                                        item.param_cbox.GetString(0))
942                else:
943                    msg = " Enter a constraint"
944                wx.PostEvent(self.parent.parent, StatusEvent(status=msg))
945                return
946        # some model or parameters can be constrained
947        self._show_constraint()
948        self.FitInside()
949
950    def _set_constraint(self):
951        """
952        get values from the constraint textcrtl ,parses them into model name
953        parameter name and parameters values.
954        store them in a list self.params .when when params is not empty
955        set_model uses it to reset the appropriate model
956        and its appropriates parameters
957        """
958        for item in self.constraints_list:
959            select0 = item.model_cbox.GetSelection()
960            if select0 == wx.NOT_FOUND:
961                continue
962            model = item.model_cbox.GetClientData(select0)
963            select1 = item.param_cbox.GetSelection()
964            if select1 == wx.NOT_FOUND:
965                continue
966            param = item.param_cbox.GetString(select1)
967            constraint = item.constraint.GetValue().lstrip().rstrip()
968            if param.lstrip().rstrip() == "":
969                param = None
970                msg = " Constraint will be ignored!. missing parameters"
971                msg += " in combobox to set constraint! "
972                wx.PostEvent(self.parent.parent, StatusEvent(status=msg))
973            for id, value in self.constraint_dict.iteritems():
974                if model == value:
975                    if constraint == "":
976                        msg = " Constraint will be ignored!. missing value"
977                        msg += " in textcrtl to set constraint! "
978                        wx.PostEvent(self.parent.parent,
979                                     StatusEvent(status=msg))
980                        constraint = None
981                    if str(param) in self.page_finder[id].get_param2fit():
982                        msg = " Checking constraint for parameter: %s ", param
983                        wx.PostEvent(self.parent.parent,
984                                     StatusEvent(info="info", status=msg))
985                    else:
986                        model_name = item[0].GetLabel()
987                        fitpage = self.page_finder[id].get_fit_tab_caption()
988                        msg = "All constrainted parameters must be set "
989                        msg += " adjustable: '%s.%s' " % (model_name, param)
990                        msg += "is NOT checked in '%s'. " % fitpage
991                        msg += " Please check it to fit or"
992                        msg += " remove the line of the constraint."
993                        wx.PostEvent(self.parent.parent,
994                                StatusEvent(info="error", status=msg))
995                        return False
996
997                    for fid in self.page_finder[id].iterkeys():
998                        # wrap in param/constraint in str() to remove unicode
999                        self.page_finder[id].set_model_param(str(param),
1000                                str(constraint), fid=fid)
1001                    break
1002        return True
1003
1004    def on_set_focus(self, event=None):
1005        """
1006        The derivative class is on focus if implemented
1007        """
1008        if self.parent is not None:
1009            if self.parent.parent is not None:
1010                wx.PostEvent(self.parent.parent, PanelOnFocusEvent(panel=self))
1011            self.page_finder = self.parent._manager.get_page_finder()
1012
1013
1014def setComboBoxItems(cbox, items):
1015    assert isinstance(cbox, wx.ComboBox)
1016    selected = cbox.GetStringSelection()
1017    cbox.Clear()
1018    for k, (name, value) in enumerate(items):
1019        cbox.Append(name, value)
1020    cbox.SetStringSelection(selected)
1021
1022
1023class SimFitPageState:
1024    """
1025    State of the simultaneous fit page for saving purposes
1026    """
1027
1028    def __init__(self):
1029        # Sim Fit Page Number
1030        self.fit_page_no = None
1031        # Select all data
1032        self.select_all = False
1033        # Data sets sent to fit page
1034        self.model_list = []
1035        # Data sets to be fit
1036        self.model_to_fit = []
1037        # Number of constraints
1038        self.no_constraint = 0
1039        # Dictionary of constraints
1040        self.constraint_dict = {}
1041        # List of constraints
1042        self.constraints_list = []
Note: See TracBrowser for help on using the repository browser.