source: sasview/guitools/fitDialog.py @ b6972a0f

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 b6972a0f was 05da1f89, checked in by Mathieu Doucet <doucetm@…>, 16 years ago

Some mods to improve the look of the fit dialog and fix minor bugs.

  • Property mode set to 100644
File size: 17.8 KB
RevLine 
[6cfe703]1#!/usr/bin/python
2
3# fitDialog.py
4
5import wx
[e40b651]6from PlotPanel import PlotPanel
7from plottables import Theory1D
8import math,pylab,fittings
[831149e]9import transform
10
[05da1f89]11def format_number(value, high=False):
12    """
13        Return a float in a standardized, human-readable formatted string
14    """
15    if high:
16        return "%-6.4g" % value
17    else:
18        return "%-5.3g" % value
19
[831149e]20
[6cfe703]21class LinearFit(wx.Dialog):
[f52bea1]22    def __init__(self, parent, plottable, push_data,transform, id, title):
[05da1f89]23        wx.Dialog.__init__(self, parent, id, title, size=(400, 380))
24
[6cfe703]25        """
[b43a009]26            Dialog window pops- up when select Linear fit on Context menu
27            Displays fitting parameters
[6cfe703]28        """
29        self.parent = parent
[f52bea1]30        self.transform = transform
[05da1f89]31       
[e40b651]32        #dialog panel self call function to plot the fitting function
33        self.push_data = push_data
34        #dialog self plottable
35        self.plottable = plottable
[05da1f89]36       
[ddff053]37        # Receive transformations of x and y
[b43a009]38        self.xLabel,self.yLabel,self.Avalue,self.Bvalue,\
39        self.ErrAvalue,self.ErrBvalue,self.Chivalue= self.transform()
[05da1f89]40       
[e40b651]41        #Dialog interface
[6cfe703]42        vbox  = wx.BoxSizer(wx.VERTICAL)
[106ef4d]43        sizer = wx.GridBagSizer(5,5)
44       
[05da1f89]45        _BOX_WIDTH = 100
[6cfe703]46 
[05da1f89]47        self.tcA      = wx.TextCtrl(self, -1,size=(_BOX_WIDTH,20))
48        self.tcA.SetToolTipString("Fit value for the slope parameter.")
49        self.tcErrA   = wx.TextCtrl(self, -1,size=(_BOX_WIDTH,20))
50        self.tcErrA.SetToolTipString("Error on the slope parameter.")
51        self.tcB      = wx.TextCtrl(self, -1,size=(_BOX_WIDTH,20))
52        self.tcA.SetToolTipString("Fit value for the constant parameter.")
53        self.tcErrB   = wx.TextCtrl(self, -1,size=(_BOX_WIDTH,20))
54        self.tcErrB.SetToolTipString("Error on the constant parameter.")
55        self.tcChi    = wx.TextCtrl(self, -1,size=(_BOX_WIDTH,20))
56        self.tcChi.SetToolTipString("Chi^2 over degrees of freedom.")
57        self.xminFit  = wx.TextCtrl(self,-1,size=(_BOX_WIDTH,20))
58        self.xminFit.SetToolTipString("Enter the minimum value on the axis to be included in the fit.")
59        self.xmaxFit  = wx.TextCtrl(self,-1,size=(_BOX_WIDTH,20))
60        self.xmaxFit.SetToolTipString("Enter the maximum value on the axis to be included in the fit.")
61        self.xminTransFit = wx.TextCtrl(self,-1,size=(_BOX_WIDTH,20))
62        self.xmaxTransFit = wx.TextCtrl(self,-1,size=(_BOX_WIDTH,20))
63        self.initXmin = wx.TextCtrl(self,-1,size=(_BOX_WIDTH,20))
64        self.initXmax = wx.TextCtrl(self,-1,size=(_BOX_WIDTH,20))
65
66        # Make the info box not editable
67        #_BACKGROUND_COLOR = '#ffdf85'
68        _BACKGROUND_COLOR = self.GetBackgroundColour()
69        self.xminTransFit.SetEditable(False)
70        self.xminTransFit.SetBackgroundColour(_BACKGROUND_COLOR)
71        self.xmaxTransFit.SetEditable(False)
72        self.xmaxTransFit.SetBackgroundColour(_BACKGROUND_COLOR)
73        self.initXmin.SetEditable(False)
74        self.initXmin.SetBackgroundColour(_BACKGROUND_COLOR)
75        self.initXmax.SetEditable(False)
76        self.initXmax.SetBackgroundColour(_BACKGROUND_COLOR)
77       
78       
79        # Buttons on the bottom
80        self.static_line_1 = wx.StaticLine(self, -1)
81        self.btFit =wx.Button(self,-1,'Fit')
82        self.btFit.Bind(wx.EVT_BUTTON, self._onFit)
83        self.btFit.SetToolTipString("Perform fit.")
84        self.btClose =wx.Button(self, wx.ID_CANCEL,'Close')
85       
86        # Intro
87        explanation  = "Perform fit for y(x) = Ax + B"
88       
89        vbox.Add(sizer)
[8e44d51]90       
[106ef4d]91        ix = 0
[6cfe703]92        iy = 1
[05da1f89]93        sizer.Add(wx.StaticText(self, -1, explanation),(iy, ix),\
94                 (1,1), wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 15)
95        iy += 2
96        sizer.Add(wx.StaticText(self, -1, 'Parameter A'),(iy, ix),\
[106ef4d]97                 (1,1), wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 15)
[6cfe703]98        ix += 1
[106ef4d]99        sizer.Add(self.tcA,(iy, ix),(1,1), wx.EXPAND|wx.ADJUST_MINSIZE, 0)
[6cfe703]100        ix += 1
[05da1f89]101        sizer.Add(wx.StaticText(self, -1, '+/-'),(iy, ix),(1,1), wx.EXPAND|wx.ADJUST_MINSIZE, 0)
[6cfe703]102        ix += 1
[106ef4d]103        sizer.Add(self.tcErrA, (iy, ix),(1,1), wx.EXPAND|wx.ADJUST_MINSIZE, 0)
[6cfe703]104        iy += 1
[106ef4d]105        ix = 0
[05da1f89]106        sizer.Add(wx.StaticText(self, -1, 'Parameter B'),(iy, ix),(1,1),\
[106ef4d]107                   wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 15)
[6cfe703]108        ix += 1
[106ef4d]109        sizer.Add(self.tcB, (iy, ix),(1,1), wx.EXPAND|wx.ADJUST_MINSIZE, 0)
[6cfe703]110        ix += 1
[05da1f89]111        sizer.Add(wx.StaticText(self, -1, '+/-'),(iy, ix),(1,1), wx.EXPAND|wx.ADJUST_MINSIZE, 0)
[6cfe703]112        ix += 1
[106ef4d]113        sizer.Add(self.tcErrB, (iy, ix),(1,1), wx.EXPAND|wx.ADJUST_MINSIZE, 0)
[6cfe703]114        iy += 1
[106ef4d]115        ix = 0
[05da1f89]116        sizer.Add(wx.StaticText(self, -1, 'Chi2/dof'),(iy, ix),(1,1),\
[106ef4d]117                   wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 15)
[6cfe703]118        ix += 1
[106ef4d]119        sizer.Add(self.tcChi, (iy, ix),(1,1), wx.EXPAND|wx.ADJUST_MINSIZE, 0)
[05da1f89]120        iy += 2
[6cfe703]121        ix = 1
[05da1f89]122        sizer.Add(wx.StaticText(self, -1, 'Min'),(iy, ix),(1,1),\
[106ef4d]123                   wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 0)
[6cfe703]124        ix += 2
[05da1f89]125        sizer.Add(wx.StaticText(self, -1, 'Max'),(iy, ix),(1,1), wx.EXPAND|wx.ADJUST_MINSIZE, 0)
[6cfe703]126        iy += 1
[831149e]127        ix = 0
[05da1f89]128        sizer.Add(wx.StaticText(self, -1, 'Plotted Range'),(iy, ix),(1,1),\
[831149e]129                   wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 15)
130        ix +=1
[83fe2cc]131        sizer.Add(self.initXmin, (iy, ix),(1,1),\
[831149e]132                   wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 0)
133        ix += 2
[83fe2cc]134        sizer.Add(self.initXmax, (iy, ix),(1,1), wx.EXPAND|wx.ADJUST_MINSIZE, 0)
[b43a009]135       
[ddff053]136        iy += 1
[831149e]137        ix = 0
[05da1f89]138        sizer.Add(wx.StaticText(self, -1, 'Fit Range of '+self.xLabel),(iy, ix),(1,1),\
[831149e]139                   wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 15)
140        ix += 1
[83fe2cc]141        sizer.Add(self.xminTransFit, (iy, ix),(1,1),\
[106ef4d]142                   wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 0)
[6cfe703]143        ix += 2
[83fe2cc]144        sizer.Add(self.xmaxTransFit, (iy, ix),(1,1), wx.EXPAND|wx.ADJUST_MINSIZE, 0)
[90640f2]145     
[ddff053]146        iy += 1
147        ix = 0
[05da1f89]148        sizer.Add(wx.StaticText(self, -1, 'Fit Range of x'),(iy, ix),(1,1),\
[ddff053]149                   wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 15)
150        ix += 1
[83fe2cc]151        sizer.Add(self.xminFit, (iy, ix),(1,1),\
[ddff053]152                   wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 0)
153        ix += 2
[83fe2cc]154        sizer.Add(self.xmaxFit, (iy, ix),(1,1), wx.EXPAND|wx.ADJUST_MINSIZE, 0)
[6cfe703]155        iy += 1
[106ef4d]156        ix = 1
157       
[05da1f89]158        vbox.Add(self.static_line_1, 0, wx.EXPAND, 0)
159       
160        sizer_button = wx.BoxSizer(wx.HORIZONTAL)
161        sizer_button.Add((20, 20), 1, wx.EXPAND|wx.ADJUST_MINSIZE, 0)
162        sizer_button.Add(self.btFit, 0, wx.LEFT|wx.RIGHT|wx.ADJUST_MINSIZE, 10)
163        sizer_button.Add(self.btClose, 0, wx.LEFT|wx.RIGHT|wx.ADJUST_MINSIZE, 10)       
164        vbox.Add(sizer_button, 0, wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
165       
166       
167       
[106ef4d]168        sizer.Add(self.btFit, (iy, ix),(1,1), wx.LEFT|wx.ADJUST_MINSIZE, 0)
169       
[05da1f89]170       
171        #panel.SetSizer(sizer)
[6cfe703]172        self.SetSizer(vbox)
173        self.Centre()
[8e44d51]174       
[e40b651]175        # Receives the type of model for the fitting
176        from LineModel import LineModel
177        self.model  = LineModel()
[1fdb81d]178         
179        #Display the fittings values
180        self.default_A = self.model.getParam('A') 
181        self.default_B = self.model.getParam('B') 
182        self.cstA = fittings.Parameter(self.model, 'A', self.default_A)
183        self.cstB  = fittings.Parameter(self.model, 'B', self.default_B)
184       
185        # Set default value of parameter in fit dialog
[90640f2]186       
[b43a009]187        if self.Avalue==None:
188            self.tcA.SetLabel(str(self.default_A))
189        else :
190            self.tcA.SetLabel(str(self.Avalue))
191        if self.Bvalue==None:
192            self.tcB.SetLabel(str(self.default_B))
193        else:
194            self.tcB.SetLabel(str(self.Bvalue))
195        if self.ErrAvalue==None:
196            self.tcErrA.SetLabel(str(0.0))
197        else:
198            self.tcErrA.SetLabel(str(self.ErrAvalue))
199        if self.ErrBvalue==None:
200            self.tcErrB.SetLabel(str(0.0))
201        else:
202            self.tcErrB.SetLabel(str(self.ErrBvalue))
203        if self.Chivalue==None:
204            self.tcChi.SetLabel(str(0.0))
205        else:
206            self.tcChi.SetLabel(str(self.Chivalue))
[831149e]207        if self.plottable.x !=[]:
208            self.mini =min(self.plottable.x)
209            self.maxi =max(self.plottable.x)
[83fe2cc]210            #store the values of View in self.x,self.y,self.dx,self.dy
211            self.x,self.y,self.dx,self.dy= self.plottable.returnValuesOfView()
[90640f2]212           
213           
[05da1f89]214            self.xminTransFit.SetLabel(format_number(min(self.x)))
215            self.xmaxTransFit.SetLabel(format_number(max(self.x)))
216           
217            self.initXmin.SetValue(format_number(self.mini))
218            self.initXmax.SetValue(format_number(self.maxi))
[90640f2]219           
[05da1f89]220            self.xminFit.SetLabel(format_number(self.mini))
221            self.xmaxFit.SetLabel(format_number(self.maxi))
[5e14aee]222       
[90640f2]223     
[6cfe703]224    def _onFit(self ,event):
[f52bea1]225        """
226            Performs the fit. Receive an event when clicking on the button Fit.Computes chisqr ,
227            A and B parameters of the best linear fit y=Ax +B
228            Push a plottable to
229        """
230        tempx=[]
231        tempy=[]
[7a03e65]232        tempdy = []
[ddff053]233       
[83fe2cc]234       
235       
[1fdb81d]236       
[106ef4d]237        # Check if View contains a x array .we online fit when x exits
238        # makes transformation for y as a line to fit
[83fe2cc]239        if self.x != []: 
[ddff053]240           
241               
[83fe2cc]242            if(self.checkFitValues(self.xminFit) == True):
[f193585]243                #Check if the field of Fit Dialog contain values and use the x max and min of the user
[83fe2cc]244                xmin,xmax = self._checkVal(self.xminFit.GetValue(),self.xmaxFit.GetValue())
[ddff053]245               
[f193585]246                xminView=self.floatTransform(xmin)
247                xmaxView=self.floatTransform(xmax)
[b43a009]248                if (self.xLabel=="log10(x)"):
[05da1f89]249                    self.xminTransFit.SetValue(format_number(math.log10(xminView)))
250                    self.xmaxTransFit.SetValue(format_number(math.log10(xmaxView)))
[416223d]251                else:
[05da1f89]252                    self.xminTransFit.SetValue(format_number(xminView))
253                    self.xmaxTransFit.SetValue(format_number(xmaxView))
[f193585]254                # Store the transformed values of view x, y,dy in variables  before the fit
[b43a009]255                if  self.yLabel.lower() == "log10(y)":
256                    if (self.xLabel.lower() == "log10(x)"):
[83fe2cc]257                        for i in range(len(self.x)):
258                            if self.x[i]>= math.log10(xmin):
259                                tempy.append(math.log10(self.y[i])) 
260                                tempdy.append(transform.errToLogX(self.y[i],0,self.dy[i],0))
[f193585]261                    else:
[83fe2cc]262                        for i in range(len(self.y)):
263                            tempy.append(math.log10(self.y[i])) 
264                            tempdy.append(transform.errToLogX(self.y[i],0,self.dy[i],0))
[f193585]265                else:
[83fe2cc]266                    tempy = self.y
267                    tempdy = self.dy
[f193585]268               
[b43a009]269                if (self.xLabel.lower() == "log10(x)"):
[83fe2cc]270                    for x_i in self.x:
[34ae302]271                        if x_i >= math.log10(xmin):
[f193585]272                            tempx.append(math.log10(x_i)) 
273                else:
[83fe2cc]274                    tempx = self.x
[b43a009]275             
[f193585]276                #Find the fitting parameters
[ddff053]277               
[b43a009]278                if (self.xLabel.lower() == "log10(x)"):
[34ae302]279                    chisqr, out, cov = fittings.sansfit(self.model, [self.cstA, self.cstB],
280                    tempx, tempy,tempdy,math.log10(xmin),math.log10(xmax))
281                else:
282                    chisqr, out, cov = fittings.sansfit(self.model, 
[f193585]283                                [self.cstA, self.cstB],tempx, tempy,tempdy,xminView,xmaxView)
[05da1f89]284               
285                # Use chi2/dof
286                if len(tempx)>0:
287                    chisqr = chisqr/len(tempx)
288               
[f193585]289                #Check that cov and out are iterable before displaying them
290                if cov ==None:
291                    errA =0.0
292                    errB =0.0
293                else:
294                    errA= math.sqrt(cov[0][0])
295                    errB= math.sqrt(cov[1][1])
296                if out==None:
297                    cstA=0.0
298                    cstB=0.0
299                else:
300                    cstA=out[0]
301                    cstB=out[1]
302                # Reset model with the right values of A and B
303                self.model.setParam('A', float(cstA))
304                self.model.setParam('B', float(cstB))
[3aa7074]305               
[f193585]306                tempx = []
307                tempy = []
308                y_model = 0.0
309                # load tempy with the minimum transformation
310               
[b43a009]311                if self.xLabel == "log10(x)":
[f193585]312                    y_model = self.model.run(math.log10(xmin))
313                    tempx.append(xmin)
314                else:
315                    y_model = self.model.run(xminView)
316                    tempx.append(xminView)
317                   
[b43a009]318                if self.yLabel == "log10(y)":
[f193585]319                    tempy.append(math.pow(10,y_model))
320                    print "tempy",tempy
321                else:
322                    tempy.append(y_model)
323                   
324                # load tempy with the maximum transformation
[b43a009]325                if self.xLabel == "log10(x)":
[f193585]326                    y_model = self.model.run(math.log10(xmax))
327                    tempx.append(xmax)
328                else:
329                    y_model = self.model.run(xmaxView)
330                    tempx.append(xmaxView)
331                   
[b43a009]332                if self.yLabel == "log10(y)":
[f193585]333                    tempy.append(math.pow(10,y_model))
334                else: 
335                    tempy.append(y_model)
[83fe2cc]336                #Set the fit parameter display when  FitDialog is opened again
[2e07e8f]337                self.Avalue=cstB
338                self.Bvalue=cstA
[b43a009]339                self.ErrAvalue=errA
340                self.ErrBvalue=errB
341                self.Chivalue=chisqr
342                self.push_data(tempx,tempy,xminView,xmaxView,xmin,xmax,self._ongetValues())
[f193585]343               
344                # Display the fitting value on the Fit Dialog
[2e07e8f]345                self._onsetValues(cstB, cstA, errA,errB,chisqr)
[b43a009]346               
347               
[7a03e65]348           
[6cfe703]349    def _onsetValues(self,cstA,cstB,errA,errB,Chi):
[f52bea1]350         """
351              Display  the value on fit Dialog
352         """
[05da1f89]353         self.tcA.SetValue(format_number(cstA))
354         self.tcB.SetValue(format_number(cstB))
355         self.tcErrA.SetValue(format_number(errA))
356         self.tcErrB.SetValue(format_number(errB))
357         self.tcChi.SetValue(format_number(Chi))
[b43a009]358       
359    def _ongetValues(self):
360         """
361              Display  the value on fit Dialog
362         """
363         return self.Avalue, self.Bvalue,self.ErrAvalue,self.ErrBvalue,self.Chivalue
[e40b651]364         
365   
[831149e]366    def _checkVal(self,usermin, usermax):
[6cfe703]367        """
[831149e]368                Ensure that fields parameter contains a min and a max value
369                within x min and x max range
[6cfe703]370        """
[831149e]371        if float(usermin) < float(usermax):
372            if float(usermin) >= float(self.mini) and float(usermin) < float(self.maxi):
[05da1f89]373                self.xminFit.SetValue(format_number(float(usermin)))
[831149e]374            else:
[05da1f89]375                self.xminFit.SetValue(format_number(float(self.mini)))
[831149e]376               
377            if float(usermax) > float(self.mini) and float(usermax) <= float(self.maxi):
[05da1f89]378                self.xmaxFit.SetLabel(format_number(float(usermax)))
[831149e]379            else:
[05da1f89]380                self.xmaxFit.SetLabel(format_number(float(self.maxi)))
[831149e]381               
[83fe2cc]382            mini =float(self.xminFit.GetValue())
383            maxi =float(self.xmaxFit.GetValue())
[831149e]384           
385            return mini, maxi
386    def floatTransform(self,x):
387        """
388             transform a float.It is use to determine the x.View min and x.View max for values
389             not in x
390        """
[b43a009]391        if ( self.xLabel=="x" ):
[831149e]392            return transform.toX(x)
393       
[b43a009]394        if ( self.xLabel=="x^(2)" ): 
[831149e]395            return transform.toX2(x)
396       
[b43a009]397        if (self.xLabel=="log10(x)" ):
[831149e]398            if x >0:
[34ae302]399                return x
[831149e]400            else:
401                raise ValueError,"cannot compute log of a negative number"
[f193585]402           
403    def checkFitValues(self,item):
404        """
405            Check the validity of input values
406        """
407        flag = True
408        value = item.GetValue()
409        # Check for possible values entered
[b43a009]410        if (self.xLabel=="log10(x)"):
[f193585]411            if (float(value) > 0):
412                item.SetBackgroundColour(wx.WHITE)
413                item.Refresh()
414            else:
415                flag = False
416                item.SetBackgroundColour("pink")
417                item.Refresh()
418     
419        return flag
[831149e]420       
[83fe2cc]421    def setFitRange(self,xmin,xmax,xminTrans,xmaxTrans):
422        """
423            Set fit parameters
424        """
[05da1f89]425        self.xminFit.SetValue(format_number(xmin))
426        self.xmaxFit.SetValue(format_number(xmax))
427        self.xminTransFit.SetValue(format_number(xminTrans))
428        self.xmaxTransFit.SetValue(format_number(xmaxTrans))
[ddff053]429       
[05da1f89]430 
431class MyApp(wx.App):
432    def OnInit(self):
433        wx.InitAllImageHandlers()
434        plot = Theory1D([],[])
435        dialog = LinearFit(None, plot, self.onFitDisplay,self.returnTrans, -1, 'Linear Fit')
436        if dialog.ShowModal() == wx.ID_OK:
437            pass
438        dialog.Destroy()
439       
440        return 1
441   
442    def onFitDisplay(self, tempx,tempy,xminView,xmaxView,xmin,xmax,func):
443        pass
444       
445    def returnTrans(self):
446        return '','',0,0,0,0,0
[6cfe703]447
[05da1f89]448# end of class MyApp
[6cfe703]449
[05da1f89]450if __name__ == "__main__":
451    app = MyApp(0)
452    app.MainLoop()
Note: See TracBrowser for help on using the repository browser.