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

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.2release_4.0.1ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since cf8e119 was aac161f1, checked in by Piotr Rozyczko <piotr.rozyczko@…>, 9 years ago

Ticket 411: added stop button to simultaneous fits.

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