#!/usr/bin/python
import wx
import numpy
import sys
#import pySAXS.LS.LSsca as LSsca
from pySAXS.models import *
from pySAXS.tools import isNumeric
'''
19-05-2011 OT Remove slider
04-08-2010 DC textboxes for parameter values and bounds are now active for typing in
04-08-2010 DC calculation of chi2 corrected
03-08-2010 DC typos on the interface corrected
10-11-2009 print the chi carre
15-06-2009 now support new model class, use a sizer, fit with or without bounds, remove bugs with slider,...
'''
[docs]class LSModelDlg(wx.Frame):
def __init__(self, parent,dataset_name,type="model",pos=wx.Point(50,50)):
self.Model=parent.data_dict[dataset_name].model
wx.Frame.__init__(self, parent, -1, self.Model.name+" model for "+dataset_name, size=wx.Size(700,40+len(self.Model.Arg)*50),pos=pos,style=wx.MINIMIZE_BOX | wx.SYSTEM_MENU | wx.CAPTION | wx.CLOSE_BOX | wx.CLIP_CHILDREN)
wx.Frame.SetIcon(self,parent.favicon)
# Add a panel so it looks correct on all platforms
self.parentwindow=parent
self.dataset_name=dataset_name
self.type=type
Blig=10
Bcol=10
self.par=self.Model.getArg()
#self.SetBackgroundColour(self.parentwindow.GetBackgroundColour())
#self.SetBackgroundColour("White")
self.itf=self.Model.getIstofit()
self.qbase=copy(self.Model.q)
self.ParDoc=[]
self.ParText=[]
self.MinText=[]
self.MaxText=[]
self.SlideMax=1000
self.CheckFit=[]
self.slider=[]
self.fitexp=0
# top sizer
self.pan=wx.Panel(self, wx.ID_ANY)
panel = self.pan
vbox_top = wx.BoxSizer(wx.VERTICAL)
#--- description
desc=wx.StaticText(panel,1,"Description : \t"+self.Model.Description,size=wx.Size(400,20))
desc.SetForegroundColour('red')
vbox_top.Add(desc,flag=wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL)
author=wx.StaticText(panel,1,"Author : \t"+self.Model.Author,size=wx.Size(400,20))
author.SetForegroundColour('red')
vbox_top.Add(author,flag=wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL)
#vbox_top.Add(wx.StaticLine(panel),0,wx.EXPAND|wx.TOP|wx.BOTTOM,5)
'''if self.type=="data":
#compute chi_carre for datas
rawdataset_name = self.parentwindow.data_dict[self.dataset_name].rawdata_ref
#print rawdataset_name,len(self.parentwindow.data_dict[rawdataset_name].q),len(self.parentwindow.data_dict[rawdataset_name].i)
iexp=numpy.array(self.parentwindow.data_dict[rawdataset_name].i)
#print len(iexp),len(self.parentwindow.data_dict[rawdataset_name].q)
val=self.Model.chi_carre(self.par,iexp)
self.chicarre=wx.TextCtrl(panel,1,"Chi carre : "+str(val),size=wx.Size(400,20))
self.chicarre.SetForegroundColour('blue')
vbox_top.Add(self.chicarre,flag=wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL|wx.TE_READONLY)
'''
#--- Parameters
sizer=wx.FlexGridSizer(rows=len(self.par)+1,cols=5,hgap=20,vgap=5)
sizer.Add(wx.StaticText(panel,1,"Parameter"),flag=wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
sizer.Add(wx.StaticText(panel,1,"Value"),flag=wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL)
sizer.Add(wx.StaticText(panel,1,"Fit ?"),flag=wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL)
sizer.Add(wx.StaticText(panel,1,"Min"),flag=wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL)
#sizer.Add(wx.StaticText(panel,1,"Value"),flag=wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL)
sizer.Add(wx.StaticText(panel,1,"Max"),wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL)
for i in numpy.arange(len(self.par)):
#--control par doc
self.ParDoc.append(wx.StaticText(panel, 5, self.Model.Doc[i]+" : "))
sizer.Add(self.ParDoc[i],flag=wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
#--control par text
self.ParText.append(wx.TextCtrl(panel,i,"",size=wx.Size(100,20), style = wx.TE_PROCESS_ENTER))
self.ParText[i].SetValue(self.Model.Format[i] % self.par[i])
sizer.Add(self.ParText[i],flag=wx.GROW|wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL)
self.ParText[i].Bind(wx.EVT_TEXT,self.textUpdate)
#--control check fit
self.CheckFit.append(wx.CheckBox(panel,50+i,label="fit"))
self.CheckFit[i].SetValue(self.Model.istofit[i])
sizer.Add(self.CheckFit[i],flag=wx.GROW|wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL)
#--control min bounds
self.MinText.append(wx.TextCtrl(panel,i,"",size=wx.Size(100,20),style = wx.TE_PROCESS_ENTER))
min=0.0*self.par[i]
self.MinText[i].SetValue(self.Model.Format[i] % min)
self.MinText[i].Bind(wx.EVT_TEXT,self.textMinMaxUpdate)
sizer.Add(self.MinText[i],flag=wx.GROW|wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL)
#--control slider
'''self.slider.append(wx.Slider(panel,i,self.SlideMax/2.,0,self.SlideMax,style=wx.SL_HORIZONTAL|wx.SL_AUTOTICKS,\
size=(200,20)))
self.slider[i].SetTickFreq(100,1)
self.Bind(wx.EVT_SCROLL_THUMBRELEASE, self.sliderUpdate)
self.Bind(wx.EVT_SCROLL_THUMBTRACK, self.sliderMove)
sizer.Add(self.slider[i],flag=wx.GROW|wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL)'''
#--control max bounds
max=2.0*self.par[i]
self.MaxText.append(wx.TextCtrl(panel,i,"",size=wx.Size(100,20),style = wx.TE_PROCESS_ENTER))
self.MaxText[i].SetValue(self.Model.Format[i] % max)
self.MaxText[i].Bind(wx.EVT_TEXT,self.textMinMaxUpdate)
sizer.Add(self.MaxText[i],flag=wx.GROW|wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL)
box0=wx.StaticBox(panel,-1,"Model parameters :")
sizerParam=wx.StaticBoxSizer(box0,wx.VERTICAL)
sizerParam.Add(sizer)
vbox_top.Add(sizerParam)
#-------- sliders for qrange
sizerSlide=wx.FlexGridSizer(rows=2,cols=3,hgap=20,vgap=5)
qmin=0
self.qminIndex=qmin
qmax=len(self.qbase)-1
self.qmaxIndex=qmax
#qmin
sizerSlide.Add(wx.StaticText(panel,1,"qmin"),flag=wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
self.qminSlide=wx.Slider(panel, -1, qmin, qmin, qmax, style=wx.SL_HORIZONTAL|wx.SL_AUTOTICKS ,\
size=(400,20))
self.qminSlide.SetTickFreq(200,1)
self.qminSlide.Bind(wx.EVT_SCROLL_THUMBTRACK, self.sliderQminMove)
sizerSlide.Add(self.qminSlide,flag=wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
self.qminTxt=wx.TextCtrl(panel,-1,str(self.qbase[qmin]),size=wx.Size(100,20),style = wx.TE_READONLY)
sizerSlide.Add(self.qminTxt,flag=wx.GROW|wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL)
#qmax
sizerSlide.Add(wx.StaticText(panel,1,"qmax"),flag=wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
self.qmaxSlide=wx.Slider(panel, -1, qmax, qmin, qmax, style=wx.SL_HORIZONTAL|wx.SL_AUTOTICKS ,\
size=(400,20))
self.qmaxSlide.SetTickFreq(200,1)
self.qmaxSlide.Bind(wx.EVT_SCROLL_THUMBTRACK, self.sliderQmaxMove)
sizerSlide.Add(self.qmaxSlide,flag=wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
self.qmaxTxt=wx.TextCtrl(panel,-1,str(self.qbase[qmax]),size=wx.Size(100,20),style = wx.TE_READONLY)
sizerSlide.Add(self.qmaxTxt,flag=wx.GROW|wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL)
#qrange
boxQ=wx.StaticBox(panel,-1,"Q range :")
sizerQ=wx.StaticBoxSizer(boxQ,wx.VERTICAL)
sizerQ.Add(sizerSlide)
vbox_top.Add(sizerQ)
#-------- update fit check box
#vbox_top.Add(wx.StaticLine(panel),0,wx.EXPAND|wx.TOP|wx.BOTTOM,5)
self.updateFit=wx.CheckBox(panel,300,label="update model when value are changed")
self.updateFit.SetValue(True)
sizer.Add(self.updateFit,flag=wx.GROW|wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL)
vbox_top.Add(wx.StaticLine(panel),0,wx.EXPAND|wx.TOP|wx.BOTTOM,5)
#-- buttons
sizerButton=wx.FlexGridSizer(rows=1,cols=5,hgap=20,vgap=5)
if self.type=="data":
self.Fitbutton=wx.Button(panel,100,"Fit",size=wx.Size(100,30))
self.Fitbutton.Bind(wx.EVT_BUTTON, self.OnClickFitButton)
sizerButton.Add(self.Fitbutton,flag=wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL)
self.FitbuttonBounds=wx.Button(panel,100,"Fit with bounds",size=wx.Size(100,30))
self.FitbuttonBounds.Bind(wx.EVT_BUTTON, self.OnClickFitButtonBounds)
sizerButton.Add(self.FitbuttonBounds,flag=wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL)
#choice with iq iq2 iq4...
choicelist=['normal','iq','iq2','iq3','iq4']
self.listText=wx.StaticText(panel,-1,"Y type :",size=wx.Size(100,20))
sizerButton.Add(self.listText,flag=wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL)
self.ytype=wx.Choice(panel,-1,size=wx.Size(100,20),choices=['normal','iq','iq2','iq3','iq4'])
self.ytype.Bind(wx.EVT_CHOICE,self.OnClickList)
self.ytype.SetSelection(0)
sizerButton.Add(self.ytype,flag=wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL)
self.QuitButton=wx.Button(panel,wx.ID_OK,"CLOSE",size=wx.Size(100,30))
wx.EVT_BUTTON(self, wx.ID_OK, self.OnExitClick)
self.Bind(wx.EVT_CLOSE, self.OnExitClick)
sizerButton.Add(self.QuitButton,flag=wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL)
vbox_top.Add(sizerButton)
panel.SetSizer(vbox_top)
vbox_top.Fit(self)
self.parentwindow.OnModelUpdate(self)
[docs] def sliderQminMove(self,event):
val=self.qminSlide.GetValue()
self.qminTxt.SetValue(str(self.qbase[val]))
self.qminIndex=val
if self.updateFit.GetValue():
self.ModelUpdate()
self.parentwindow.OnModelUpdate(self)
[docs] def sliderQmaxMove(self,event):
val=self.qmaxSlide.GetValue()
self.qmaxTxt.SetValue(str(self.qbase[val]))
self.qmaxIndex=val
if self.updateFit.GetValue():
self.ModelUpdate()
self.parentwindow.OnModelUpdate(self)
'''def sliderUpdate(self,event):
#no more used if slider is removed
b=self.slider[event.GetId()].GetValue()
c=((eval(self.MaxText[event.GetId()].GetValue())-eval(self.MinText[event.GetId()].GetValue()))/self.SlideMax)*b + eval(self.MinText[event.GetId()].GetValue())
self.ParText[event.GetId()].SetValue(self.Model.Format[event.GetId()] % c)
self.ModelUpdate()
self.parentwindow.OnModelUpdate(self)
def sliderMove(self,event):
#no more used if slider is removed
#print "on slider move"
n=event.GetId()
b=self.slider[event.GetId()].GetValue()
#print b
minbounds=float(eval(self.MinText[n].GetValue()))
maxbounds=float(eval(self.MaxText[n].GetValue()))
parValue=float(eval(self.ParText[n].GetValue()))
range=(maxbounds-minbounds)/self.SlideMax
newvalue=range*b + minbounds
#print newvalue
#self.ParText[event.GetId()].SetValue(self.Model.Format[event.GetId()] % c)
self.ParText[event.GetId()].SetValue(self.Model.Format[event.GetId()] % newvalue)
'''
[docs] def textMinMaxUpdate(self,event):
#self.parentwindow.printTXT( "min max update")
#b=self.slider[event.GetId()].GetValue()
n=event.GetId()
max=self.MaxText[event.GetId()].GetValue()
min=self.MinText[event.GetId()].GetValue()
if (not(isNumeric.isNumeric(max)))or(not(isNumeric.isNumeric(min))):
return #not a number : do nothing
if float(max)<=float(min):
#do nothing
return
val=self.ParText[event.GetId()].GetValue()
if (not(isNumeric.isNumeric(val))):
#parameter val is not a number : set as min
min=float(min)
self.ParText[event.GetId()].SetValue(self.Model.Format[event.GetId()] % min)
else:
if float(val)<float(min):
# val is < than min, set as min
self.ParText[event.GetId()].SetValue(self.Model.Format[event.GetId()] % float(min))
if float(val)>float(max):
# val is > than max, set as max
self.ParText[event.GetId()].SetValue(self.Model.Format[event.GetId()] % float(max))
#c=((eval(self.MaxText[event.GetId()].GetValue())-eval(self.MinText[event.GetId()].GetValue()))/self.SlideMax)*b + eval(self.MinText[event.GetId()].GetValue())
#self.ParText[event.GetId()].SetValue(self.Model.Format[event.GetId()] % c)
if self.updateFit.GetValue():
self.ModelUpdate()
self.parentwindow.OnModelUpdate(self)
[docs] def textUpdate(self,event):
'''
parameter value is updated
'''
n=event.GetId() #no of text box updated
#print "text updated",n
#test if value of text box is coherent with bounds
minbounds=eval(self.MinText[n].GetValue())
maxbounds=eval(self.MaxText[n].GetValue())
if not(isNumeric.isNumeric(self.ParText[n].GetValue())):
#do nothing
return
parValue=eval(self.ParText[n].GetValue())
if parValue<minbounds:
self.MinText[event.GetId()].SetValue(str(parValue))
minbounds=parValue
if parValue>maxbounds:
self.MaxText[event.GetId()].SetValue(str(parValue))
maxbounds=parValue
#update slider
#b=self.slider[event.GetId()].GetValue()
#new value of slider in percent
#range=maxbounds-minbounds
#newvalue=((parValue-minbounds)/range)*self.SlideMax
#self.slider[event.GetId()].SetValue(newvalue)
if self.updateFit.GetValue():
self.ModelUpdate()
self.parentwindow.OnModelUpdate(self)
[docs] def ModelUpdate(self):
'''
when a parameter is updated
'''
if not(self.parentwindow.data_dict.has_key(self.dataset_name)):
self.parentwindow.printTXT(self.dataset_name+" dataset removed ? ")
return
self.bounds=[]
for i in numpy.arange(len(self.Model.Arg)):
self.par[i] = float(eval(self.ParText[i].GetValue()))
self.itf[i]=self.CheckFit[i].GetValue()
bmin=self.MinText[i].GetValue()
bmax=self.MaxText[i].GetValue()
self.bounds.append((bmin,bmax))
self.Model.setIstofit(self.itf)
self.Model.setArg(self.par)
if (self.qminIndex!=0) or (self.qmaxIndex!=len(self.qbase)-1):
self.Model.q=self.qbase[self.qminIndex:self.qmaxIndex]
#print self.Model.q
#print self.Model.q
#compute chicarre
'''if self.type=="data":
#compute chi_carre for datas
#print 'compute chi_carre'
rawdataset_name = self.parentwindow.data_dict[self.dataset_name].rawdata_ref
iexp=self.parentwindow.data_dict[rawdataset_name].i[:]
val=self.Model.chi_carre(self.par,iexp)
self.chicarre.Clear()
self.chicarre.AppendText('Chi carre : '+str(val))'''
[docs] def UpdateAfterFit(self,result):
'''
update all after a fit
'''
val=numpy.array(result).copy()
#print "UPDATE AFTER FIT",val
for i in range(len(val)):
#print i,val[i]
self.ParText[i].SetValue(str(val[i]))
[docs] def DlgUpdate(self,event):
###More to do for data validation...
for i in numpy.arange(len(self.Model.Arg)):
self.ParText[i].SetValue(self.Model.Format[i] % self.par[i])
self.CheckFit[i].SetValue(self.itf[i])
[docs] def OnExitClick(self,event):
self.ModelUpdate()
self.Destroy()
[docs] def OnClickList(self,event):
choice=self.ytype.GetSelection()
#print choice
self.fitexp=choice