source: sasview/guitools/fitDialog.py @ 6a8adb0

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

Cleaned up dialogs

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