source: sasview/sansview/perspectives/fitting/modelpage.py @ 3dcd7b5

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 3dcd7b5 was 3dc83be, checked in by Mathieu Doucet <doucetm@…>, 15 years ago

Fix model change bug. Models can now be selected from the top menu or the model page menu with the same result. The 2D plot, if enabled, will also be correctly updated.

  • Property mode set to 100644
File size: 16.9 KB
Line 
1import sys
2import wx
3import wx.lib
4import numpy
5import copy
6
7from sans.guicomm.events import StatusEvent   
8(ModelEventbox, EVT_MODEL_BOX) = wx.lib.newevent.NewEvent()
9_BOX_WIDTH = 80
10
11def format_number(value, high=False):
12    """
13        Return a float in a standardized, human-readable formatted string
14    """
15    try: 
16        value = float(value)
17    except:
18        print "returning 0"
19        return "0"
20   
21    if high:
22        return "%-6.4g" % value
23    else:
24        return "%-5.3g" % value
25
26   
27class ModelPage(wx.ScrolledWindow):
28    """
29        FitPanel class contains fields allowing to display results when
30        fitting  a model and one data
31        @note: For Fit to be performed the user should check at least one parameter
32        on fit Panel window.
33 
34    """
35    ## Internal name for the AUI manager
36    window_name = "Fit page"
37    ## Title to appear on top of the window
38    window_caption = "Fit Page"
39   
40   
41    def __init__(self, parent,model,name, *args, **kwargs):
42        wx.ScrolledWindow.__init__(self, parent, *args, **kwargs)
43        """
44            Initialization of the Panel
45        """
46        #self.scroll = wx.ScrolledWindow(self)
47        self.manager = None
48        self.parent  = parent
49        self.event_owner=None
50        #panel interface
51        self.vbox  = wx.BoxSizer(wx.VERTICAL)
52        self.sizer3 = wx.GridBagSizer(5,5)
53        self.sizer1 = wx.GridBagSizer(5,5)
54        self.sizer2 = wx.GridBagSizer(5,5)
55        self.sizer4 = wx.GridBagSizer(5,5)
56        self.sizer5 = wx.GridBagSizer(5,5)
57        self.static_line_1 = wx.StaticLine(self, -1)
58        self.modelbox = wx.ComboBox(self, -1)
59        id = wx.NewId()
60        self.vbox.Add(self.sizer3)
61        self.vbox.Add(self.sizer1)
62        self.vbox.Add(self.sizer2)
63        self.vbox.Add(self.static_line_1, 0, wx.EXPAND, 0)
64        self.vbox.Add(self.sizer5)
65        self.vbox.Add(self.sizer4)
66       
67        id = wx.NewId()
68        self.btClose =wx.Button(self,id,'Close')
69        self.btClose.Bind(wx.EVT_BUTTON, self.onClose,id=id)
70        self.btClose.SetToolTipString("Close page.")
71        ix = 1
72        iy = 1 
73        self.sizer4.Add(wx.StaticText(self, -1, 'Min'),(iy, ix),(1,1),\
74                            wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 15)
75        ix += 1
76        self.sizer4.Add(wx.StaticText(self, -1, 'Max'),(iy, ix),(1,1),\
77                            wx.EXPAND|wx.ADJUST_MINSIZE, 0)
78        ix += 1
79        self.sizer4.Add(wx.StaticText(self, -1, 'Npts'),(iy, ix),(1,1),\
80                            wx.EXPAND|wx.ADJUST_MINSIZE, 0)
81        ix = 0
82        iy += 1
83        self.sizer4.Add(wx.StaticText(self, -1, 'x range'),(iy, ix),(1,1),\
84                            wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 15)
85        ## Q range
86        self.qmin= 0.001
87        self.qmax= 0.1
88        self.num_points= 100
89       
90        ix += 1
91        self.xmin    = wx.TextCtrl(self, -1,size=(_BOX_WIDTH,20))
92        self.xmin.SetValue(format_number(self.qmin))
93        self.xmin.SetToolTipString("Minimun value of x in linear scale.")
94        self.xmin.Bind(wx.EVT_KILL_FOCUS, self._onparamEnter)
95        self.xmin.Bind(wx.EVT_TEXT_ENTER, self._onparamEnter)
96        self.sizer4.Add(self.xmin,(iy, ix),(1,1), wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 15)
97       
98       
99        ix += 1
100        self.xmax    = wx.TextCtrl(self, -1,size=(_BOX_WIDTH,20))
101        self.xmax.SetValue(format_number(self.qmax))
102        self.xmax.SetToolTipString("Maximum value of x in linear scale.")
103        self.xmax.Bind(wx.EVT_KILL_FOCUS, self._onparamEnter)
104        self.xmax.Bind(wx.EVT_TEXT_ENTER, self._onparamEnter)
105        self.sizer4.Add(self.xmax,(iy,ix),(1,1), wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 0)
106        ix += 1
107        self.npts    = wx.TextCtrl(self, -1,size=(_BOX_WIDTH,20))
108        self.npts.SetValue(format_number(self.num_points))
109        self.npts.SetToolTipString("Number of point to plot.")
110        self.npts.Bind(wx.EVT_KILL_FOCUS, self._onparamEnter)
111        self.npts.Bind(wx.EVT_TEXT_ENTER, self._onparamEnter)
112       
113        self.sizer4.Add(self.npts,(iy,ix),(1,1), wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 0)
114        ix = 0
115        iy += 1
116        self.sizer4.Add((20,20),(iy, ix),(1,1), wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 15)
117        ix +=3
118        self.sizer4.Add( self.btClose,(iy,ix),(1,1), wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 0)
119        ix = 0
120        iy = 1
121        self.sizer3.Add(wx.StaticText(self,-1,'Model'),(iy,ix),(1,1)\
122                  , wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 15)
123        ix += 1
124        self.sizer3.Add(self.modelbox,(iy,ix),(1,1),  wx.EXPAND|wx.ADJUST_MINSIZE, 0)
125        #ix = 0
126        #iy += 1
127        ix += 1
128        id = wx.NewId()
129        self.model_view =wx.Button(self,id,'View 2D')
130        self.model_view.Bind(wx.EVT_BUTTON, self.onModel2D,id=id)
131        self.model_view.SetToolTipString("View model in 2D")
132        self.sizer3.Add(self.model_view,(iy,ix),(1,1),\
133                   wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 15)
134        # contains link between  model ,all its parameters, and panel organization
135        self.parameters=[]
136        #contains link between a model and selected parameters to fit
137        self.param_toFit=[]
138        # model on which the fit would be performed
139        self.model=model
140        try:
141            #print"init modelpage",model.name
142            self.set_panel(model)
143        except:
144            raise
145        # preview selected model name
146        self.prevmodel_name=name
147        #print "model view prev_model",name
148        self.modelbox.SetValue(self.prevmodel_name)
149        # flag to check if the user has selected a new model in the combox box
150        self.model_hasChanged=False
151        #dictionary of model name and model class
152        self.model_list_box={}
153       
154        #enable model 2D draw
155        self.enable2D= False
156        # Data1D to make a deep comparison between 2 Data1D for checking data
157        #change
158        self.vbox.Layout()
159        self.vbox.Fit(self) 
160       
161        self.SetSizer(self.vbox)
162        self.SetScrollbars(20,20,55,40)
163        self.Centre()
164       
165    def set_range(self, qmin, qmax, npts):
166        """
167            Set the range for the plotted models
168            @param qmin: minimum Q
169            @param qmax: maximum Q
170            @param npts: number of Q bins
171        """
172        # Set the data members
173        self.qmin = qmin
174        self.qmax = qmax
175        self.num_points = npts
176       
177        # Set the controls
178        self.xmin.SetValue(format_number(self.qmin))
179        self.xmax.SetValue(format_number(self.qmax))
180        self.npts.SetValue(format_number(self.num_points))
181       
182    def onClose(self,event):
183        """ close the page associated with this panel"""
184        self.GrandParent.onClose()
185       
186    def set_owner(self,owner):
187        """
188            set owner of fitpage
189            @param owner: the class responsible of plotting
190        """
191        self.event_owner=owner   
192   
193 
194    def set_manager(self, manager):
195        """
196             set panel manager
197             @param manager: instance of plugin fitting
198        """
199        self.manager = manager
200       
201    def onModel2D(self, event):
202        """
203         call manager to plot model in 2D
204        """
205        # If the 2D display is not currently enabled, plot the model in 2D
206        # and set the enable2D flag.
207        if self.enable2D==False:
208            self.enable2D=True
209            self._draw_model()
210           
211    def populate_box(self, dict):
212        """
213            Populate each combox box of each page
214            @param page: the page to populate
215        """
216        id=0
217        self.model_list_box=dict
218        list_name=[]
219        for item in  self.model_list_box.itervalues():
220            name = item.__name__
221            if hasattr(item, "name"):
222                name = item.name
223            list_name.append(name)
224        list_name.sort()   
225        for name in list_name:
226            self.modelbox.Insert(name,int(id))
227            id+=1
228        wx.EVT_COMBOBOX(self.modelbox,-1, self._on_select_model) 
229        return 0
230   
231    def select_model(self, model):
232        """
233            Select a new model
234            @param model: model object
235        """
236        self.model= model
237        self.set_panel(model)
238        self._draw_model()
239       
240        # Select the model from the combo box
241        items = self.modelbox.GetItems()
242        for i in range(len(items)):
243            if items[i]==model.__class__.__name__:
244                self.modelbox.SetSelection(i)
245       
246    def _on_select_model(self,event):
247        """
248            react when a model is selected from page's combo box
249            post an event to its owner to draw an appropriate theory
250        """
251        self.model_view.SetFocus()
252        for item in self.model_list_box.itervalues():
253            name = item.__name__
254            if hasattr(item, "name"):
255                name = item.name
256            if name ==event.GetString():
257                model=item()
258                self.model= model
259                self.set_panel(model)
260                self.name= name
261                #self.manager.draw_model(model, name)
262                self._draw_model()
263           
264           
265    def get_model_box(self): 
266        """ return reference to combox box self.model"""
267        return self.modelbox
268
269   
270    def get_param_list(self):
271        """
272            @return self.param_toFit: list containing  references to TextCtrl
273            checked.Theses TextCtrl will allow reference to parameters to fit.
274            @raise: if return an empty list of parameter fit will nnote work
275            properly so raise ValueError,"missing parameter to fit"
276        """
277        if self.param_toFit !=[]:
278            return self.param_toFit
279        else:
280            raise ValueError,"missing parameter to fit"
281       
282       
283    def set_panel(self,model):
284        """
285            Build the panel from the model content
286            @param model: the model selected in combo box for fitting purpose
287        """
288       
289        self.sizer2.Clear(True)
290        self.sizer1.Clear(True)
291        self.sizer5.Clear(True)
292        self.parameters = []
293        self.param_toFit=[]
294        self.model = model
295        keys = self.model.getParamList()
296        keys.sort()
297        description=None
298        if hasattr(self.model,'description'):
299            description =model.description
300        disp_list=self.model.getDispParamList()
301        ip=0
302        iq=1
303        if len(disp_list)>0:
304            disp = wx.StaticText(self, -1, 'Dispersion')
305            self.sizer5.Add(disp,( iq, ip),(1,1),  wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 15)
306            ip += 1 
307            values = wx.StaticText(self, -1, 'Values')
308            self.sizer5.Add(values,( iq, ip),(1,1), wx.EXPAND|wx.ADJUST_MINSIZE, 0)
309           
310        disp_list.sort()
311        #print "went here",self.model.name,model.description
312        iy = 1
313        ix = 0
314        self.cb0 = wx.StaticText(self, -1,'Model Description:')
315        self.sizer1.Add(self.cb0,(iy, ix),(1,1),\
316                          wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 15)
317        iy += 1
318       
319        self.cb01 = wx.StaticText(self, -1,str(description),style=wx.ALIGN_LEFT)
320        self.cb01.Wrap(400) 
321        #self.cb01 = wx.StaticText(self, -1,str(description),(45, 25),style=wx.ALIGN_LEFT)
322       
323        self.sizer1.Add(self.cb01,(iy, ix),(1,1),\
324                          wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 15)
325        ix = 0
326        iy = 1
327        self.cb1 = wx.StaticText(self, -1,'Parameters')
328        self.sizer2.Add(self.cb1,(iy, ix),(1,1),\
329                          wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 15)
330        ix +=1
331        self.text2_2 = wx.StaticText(self, -1, 'Values')
332        self.sizer2.Add(self.text2_2,(iy, ix),(1,1),\
333                            wx.EXPAND|wx.ADJUST_MINSIZE, 0) 
334        ix +=1
335        self.text2_4 = wx.StaticText(self, -1, 'Units')
336        self.sizer2.Add(self.text2_4,(iy, ix),(1,1),\
337                            wx.EXPAND|wx.ADJUST_MINSIZE, 0) 
338        self.text2_4.Hide()
339        for item in keys:
340            if not item in disp_list:
341                iy += 1
342                ix = 0
343                cb=wx.StaticText(self, -1, item)
344                self.sizer2.Add( cb,( iy, ix),(1,1),  wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 15)
345                ix += 1
346                value= self.model.getParam(item)
347                ctl1 = wx.TextCtrl(self, -1, size=(_BOX_WIDTH,20), style=wx.TE_PROCESS_ENTER)
348                ctl1.SetValue(str (format_number(value)))
349                ctl1.Bind(wx.EVT_KILL_FOCUS, self._onparamEnter)
350                ctl1.Bind(wx.EVT_TEXT_ENTER, self._onparamEnter)
351                self.sizer2.Add(ctl1, (iy,ix),(1,1), wx.EXPAND|wx.ADJUST_MINSIZE, 0)
352                ix +=1
353               
354                # Units
355                try:
356                    units = wx.StaticText(self, -1, self.model.details[item][0], style=wx.ALIGN_LEFT)
357                except:
358                    units = wx.StaticText(self, -1, "", style=wx.ALIGN_LEFT)
359                self.sizer2.Add(units, (iy,ix),(1,1),  wx.EXPAND|wx.ADJUST_MINSIZE, 0)
360            else:
361                ip = 0
362                iq += 1
363                cb = wx.CheckBox(self, -1, item, (10, 10))
364                cb.SetValue(False)
365                self.sizer5.Add( cb,( iq, ip),(1,1),  wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 15)
366                wx.EVT_CHECKBOX(self, cb.GetId(), self._on_select_model)
367               
368                ip += 1
369                value= self.model.getParam(item)
370                ctl1 = wx.TextCtrl(self, -1, size=(_BOX_WIDTH,20), style=wx.TE_PROCESS_ENTER)
371                ctl1.SetValue(str (format_number(value)))
372                ctl1.Bind(wx.EVT_KILL_FOCUS, self._onparamEnter)
373                ctl1.Bind(wx.EVT_TEXT_ENTER,self._onparamEnter)
374                self.sizer5.Add(ctl1, (iq,ip),(1,1), wx.EXPAND|wx.ADJUST_MINSIZE, 0)
375            #save data
376            self.parameters.append([cb,ctl1])
377        iy+=1
378        self.sizer2.Add((20,20),(iy,ix),(1,1), wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 15)
379       
380        #Display units text on panel
381        for item in keys:   
382            if self.model.details[item][0]!='':
383                self.text2_4.Show()
384                break
385            else:
386                self.text2_4.Hide()
387        self.vbox.Layout()
388        self.GrandParent.GetSizer().Layout()
389       
390       
391    def _onparamEnter(self,event):
392        """
393            when enter value on panel redraw model according to changed
394        """
395        self.set_model_parameter()
396       
397    def set_model_parameter(self):
398        if len(self.parameters) !=0 and self.model !=None:
399            # Flag to register when a parameter has changed.
400            is_modified = False
401            for item in self.parameters:
402                try:
403                     name=str(item[0].GetLabelText())
404                     value= float(item[1].GetValue())
405                     # If the value of the parameter has changed,
406                     # update the model and set the is_modified flag
407                     if value != self.model.getParam(name):
408                         self.model.setParam(name,value)
409                         is_modified = True
410                except:
411                     wx.PostEvent(self.parent.GrandParent, StatusEvent(status=\
412                            "Model Drawing  Error:wrong value entered : %s"% sys.exc_value))
413           
414            # Here we should check whether the boundaries have been modified.
415            # If qmin and qmax have been modified, update qmin and qmax and
416            # set the is_modified flag to True
417            if float(self.xmin.GetValue()) != self.qmin:
418                self.qmin = float(self.xmin.GetValue())
419                is_modified = True
420            if float(self.xmax.GetValue()) != self.qmax:
421                self.qmax = float(self.xmax.GetValue())
422                is_modified = True
423           
424            if float(self.npts.GetValue()) !=  self.num_points:
425                self.num_points = float(self.npts.GetValue())
426                is_modified = True
427         
428            if is_modified:
429                self._draw_model()           
430           
431    def _draw_model(self):
432        """
433            Method to draw or refresh a plotted model.
434            The method will use the data member from the model page
435            to build a call to the fitting perspective manager.
436           
437            [Note to coder: This way future changes will be done in only one place.]
438        """
439        self.manager.draw_model(self.model, self.model.name, 
440                                qmin=self.qmin, qmax=self.qmax,
441                                qstep= self.num_points,
442                                enable2D=self.enable2D)
443   
444           
445           
446             
Note: See TracBrowser for help on using the repository browser.