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

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 65f3930 was 00f7ff1, checked in by Paul Kienzle <pkienzle@…>, 7 years ago

move sim fit state to sascalc pagestate

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