#
# gtkLeastSquares.py : 	GUI for the module Scientific.Functions.LeastSquares
#		Implementation of the Levenberg-Marquardt algorithm for general
#		non-linear least-squares fits.
#
# Copyright (C) 2001 Adrian E. Feiguin <feiguin@ifir.edu.ar>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#


from string import *
from gtk import *
from Numeric import *
from Scientific.Functions.LeastSquares import *
from Scientific.Functions.FirstDerivatives import *

try:
  import sg
  import sgagtk
  import pysga
except:
  sg = 0

def fit_linear(parameters, values):
	a, b = parameters
	x = values
	return (a + b * x)

def fit_quadratic(parameters, values):
	a, b, c, x0 = parameters
	x = values
	return(a + b * (x - x0) + c * (x - x0)**2)
        return

def fit_gauss(parameters, values):
	y0, x0, a, w = parameters
	x = values
	return(y0 + a * exp(-2*(x-x0)**2/w**2))
	return

def fit_lorentz(parameters, values):
	x0, y0, a, b = parameters
	x = values
	return(y0 + 2*a/pi * w / (4 * (x - x0)**2 + w**2))

def fit_boltzman(parameters, values):
	x0, a1, a2, dx = parameters
	x = values
	return((a1 - a2)/(1 + exp((x - x0)/dx)) + a2)

def fit_logistic(parameters, values):
	x0, a1, a2, p = parameters
	x = values
	return((a1 - a2)/(1 + (x/x0)**p) + a2)

def fit_expdecay(parameters, values):
	x0, y0, a, t = parameters
	x = values
	return(y0 + a * exp(-(x - x0)/t))

def fit_expgrow(parameters, values):
	x0, y0, a, t = parameters
	x = values
	return(y0 + a * exp((x - x0)/t))

def fit_expassoc(parameters, values):
	y0, a1, t1, a2, t2 = parameters
	x = values
	return(y0 + a1 * (1 + exp(-x/t1)) + a2 * (1 + exp(-x/t2)))

def fit_hyperbl(parameters, values):
	p1, p2 = parameters
	x = values
	return(p1 * x/ (p2 + x))

def fit_pulse(parameters, values):
	x0, y0, a, t1, t2 = parameters
	x = values
	return(y0 + a * (1 + exp(-(x - x0)/t1)) * exp(-(x - x0)/t2))

def fit_rational0(parameters, values):
	a, b, c = parameters
	x = values
	return((b + c*x)/(1 + a*x))

def fit_sine(parameters, values):
	x0, a, w = parameters
	x = values
	return(a * sin(pi*(x - x0)/w))

def fit_gaussamp(parameters, values):
	x0, y0, a, w = parameters
	x = values
	return(y0 + a * exp(-(x - x0)**2/(2*w**2)))

def fit_allometric(parameters, values):
	a, b = parameters
	x = values
	return(a * x**b)



fit_linear_dic = {
	"Doc" : "Linear function",
	"Exp" : "%s + %s*x",
	"Par" : ("a", "b"),
        "Fmt" : [0,1],
	"NumPar" : 2,
	"IVar" : "x",
	"DVar" : "y",
	"Function" : fit_linear
}
fit_quadratic_dic = {
	"Doc" : "Quadratic function",
	"Exp" : "%s + %s*(x-%s) + %s*(x-%s)**2",
	"Par" : ("a", "b", "c", "x0"),
        "Fmt" : [0,1,3,2,3],
	"NumPar" : 4,
	"IVar" : "x",
	"DVar" : "y",
	"Function" : fit_quadratic
}
fit_gauss_dic = {
	"Doc" : "Gaussian distribution function",
	"Exp" : "%s + %s * exp(-2*(x-%s)**2 / %s**2)",
	"Par" : ("x0", "y0", "a", "w"),
        "Fmt" : [1,2,0,3],
	"NumPar" : 4,
	"IVar" : "x",
	"DVar" : "y",
	"Function" : fit_gauss
}
fit_lorentz_dic = {
	"Doc" : "Lorentzian peak function",
	"Exp" : "%s + 2*%s/pi * %s / (4 * (x-%s)**2 + %s**2)",
	"Par" : ("x0", "y0", "a", "w"),
        "Fmt" : [1,2,3,0,3],
	"NumPar" : 4,
	"IVar" : "x",
	"DVar" : "y",
	"Function" : fit_lorentz
}
fit_boltzman_dic = {
	"Doc" : "Boltzman function: sigmoidal curve",
	"Exp" : "(%s - %s) / (1 + exp((x-%s)/%s)) + %s",
	"Par" : ("x0", "a1", "a2", "dx"),
        "Fmt" : [1,2,0,3,2],
	"NumPar" : 4,
	"IVar" : "x",
	"DVar" : "y",
	"Function" : fit_boltzman
}
fit_logistic_dic = {
	"Doc" : "Logistic dose/response function",
	"Exp" : "(%s - %s) / (1 + (x/%s)**%s) + %s",
	"Par" : ("x0", "a1", "a2", "p"),
        "Fmt" : [1,2,0,3,2],
	"NumPar" : 4,
	"IVar" : "x",
	"DVar" : "y",
	"Function" : fit_logistic
}
fit_expdecay_dic = {
	"Doc" : "Exponential decay function",
	"Exp" : "%s + %s * exp(-(x-%s)/%s)",
	"Par" : ("x0", "y0", "a", "t"),
        "Fmt" : [1,2,0,3],
	"NumPar" : 4,
	"IVar" : "x",
	"DVar" : "y",
	"Function" : fit_expdecay
}
fit_expgrow_dic = {
	"Doc" : "Exponential growth function",
	"Exp" : "%s + %s * exp((x-%s)/%s)",
	"Par" : ("x0", "y0", "a", "t"),
        "Fmt" : [1,2,0,3],
	"NumPar" : 4,
	"IVar" : "x",
	"DVar" : "y",
	"Function" : fit_expgrow
}
fit_expassoc_dic = {
	"Doc" : "Exponential associate function",
	"Exp" : "%s + %s * (1 + exp(-x/%s)) + %s * (1 + exp(-x/%s))",
	"Par" : ("y0", "a1", "t1", "a2", "t2"),
        "Fmt" : [0,1,2,3,4],
	"NumPar" : 5,
	"IVar" : "x",
	"DVar" : "y",
	"Function" : fit_expassoc
}
fit_hyperbl_dic = {
	"Doc" : "Hyperbola function",
	"Exp" : "%s * x/ (%s + x)",
	"Par" : ("p1", "p2"),
        "Fmt" : [0,1],
	"NumPar" : 2,
	"IVar" : "x",
	"DVar" : "y",
	"Function" : fit_hyperbl
}
fit_pulse_dic = {
	"Doc" : "Pulse function",
	"Exp" : "%s + %s * (1 + exp(-(x-%s)/%s)) * exp(-(x-%s)/%s)",
	"Par" : ("x0", "y0", "a", "t1", "t2"),
        "Fmt" : [1,2,0,3,0,4],
	"NumPar" : 5,
	"IVar" : "x",
	"DVar" : "y",
	"Function" : fit_pulse
}
fit_rational0_dic = {
	"Doc" : "Rational function , type 0",
	"Exp" : "(%s + %s*x)/(1 + %s*x)",
	"Par" : ("a", "b", "c"),
        "Fmt" : [1,2,0],
	"NumPar" : 3,
	"IVar" : "x",
	"DVar" : "y",
	"Function" : fit_rational0
}
fit_sine_dic = {
	"Doc" : "Sine function",
	"Exp" : "%s * sin(pi*(x-%s)/%s)",
	"Par" : ("a", "x0", "w"),
        "Fmt" : [0,1,2],
	"NumPar" : 3,
	"IVar" : "x",
	"DVar" : "y",
	"Function" : fit_sine
}
fit_gaussamp_dic = {
	"Doc" : "Amplitude version of Gaussian peak function",
	"Exp" : "%s + %s * exp(-(x-%s)**2 / (2*%s**2))",
	"Par" : ("x0", "y0", "a", "w"),
        "Fmt" : [1,2,0,3],
	"NumPar" : 4,
	"IVar" : "x",
	"DVar" : "y",
	"Function" : fit_gaussamp
}
fit_allometric_dic = {
	"Doc" : "Classical Freundlich model",
	"Exp" : "%s * x**%s",
	"Par" : ("a", "b"),
        "Fmt" : [0,1],
	"NumPar" : 4,
	"IVar" : "x",
	"DVar" : "y",
	"Function" : fit_allometric
}


functions = {
	"Linear" : fit_linear_dic,
	"Quadratic" : fit_quadratic_dic,
	"Gauss" : fit_gauss_dic,
	"Lorentz" : fit_lorentz_dic,
	"Boltzman" : fit_boltzman_dic,
	"Logistic" : fit_logistic_dic,
	"ExpDecay" : fit_expdecay_dic,
	"ExpGrow" : fit_expgrow_dic,
	"ExpAssoc" : fit_expassoc_dic,
	"Hyperbl" : fit_hyperbl_dic,
	"Pulse" : fit_pulse_dic,
	"Rational0" : fit_rational0_dic,
	"Sine" : fit_sine_dic,
	"GaussAmp" : fit_gaussamp_dic,
	"Allometric" : fit_allometric_dic
}

def str_exp(exp,par,fmt):
  vals=()
  for v in fmt:
    vals=vals+(par[v],)
  return exp%vals

def construct_data(a1, a2):
        try:
          temp1=a1.shape
          temp2=a2.shape
        except:
          return None
        max_index=min(a1.shape[0], a2.shape[0])
        if max_index==0:
          return None
	data = []
	for i in range(0, min(a1.shape[0], a2.shape[0])):
	  	data = data + [(a1[i], a2[i])]
	return data


def fit(params,page_box,next=None,prev=None,cancel=None):
        if params['mode']==1:
          xvalues=sg.col(params["xcolumn"])
          yvalues=sg.col(params["ycolumn"])
          
        else:
          dataset=sg.plot_layer_dataset(params['plot_name'],params['plot_layer'],params['datasets'][params['dataset_choice']])
#          print params['plot_name'],params['plot_layer'],params['datasets'][params['dataset_choice']]
          xvalues=dataset['x']
          yvalues=dataset['y']
#          print xvalues
          
        data=construct_data(xvalues,yvalues)
        params["data"]=data
        
	main_box = GtkVBox(FALSE, 5)
        page_box.pack_start(main_box)
        if not xvalues or not yvalues or len(xvalues)==0 or len(yvalues)==0:
          main_box.pack_start(GtkLabel("No column data to fit!\n"+
           "Press 'Back' to make a selection."),expand=TRUE,fill=TRUE,padding=5)
          main_box.show_all()
          next.set_sensitive(FALSE)
          return


        
	main_frame = GtkFrame("Select Function")
	main_box.pack_start(main_frame)
	hbox=GtkHBox()
	table = GtkTable(7, 4, FALSE)
	table.set_col_spacings(5)
	table.set_row_spacings(5)
	main_frame.add(hbox)

        swindow = GtkScrolledWindow()
        swindow.set_policy(POLICY_NEVER, POLICY_AUTOMATIC)
	swindow.set_usize(120, 100)
        hbox.pack_start(swindow,padding=5)
        clist = GtkCList(1)
	swindow.add(clist)

	hbox.pack_start(GtkVSeparator())
        hbox.pack_start(table,padding=5)

	text = map(lambda i: str(i), range(20))

	k = functions.keys()
	k.sort()
	for i in k:	
		text[0] = i
		clist.append(text)

	label = GtkLabel("Exp:")
	label.set_alignment(1., .5)
	table.attach(label, 2, 3, 0, 1)
	fentry = GtkText()
        fentry.set_editable(FALSE)
        fentry.set_line_wrap(TRUE)
	sw = GtkScrolledWindow()
        sw.set_policy(POLICY_NEVER, POLICY_AUTOMATIC)
        sw.add(fentry)
        table.attach(sw, 3, 4, 0, 1)

        label = GtkLabel("Number of Param:")
        label.set_alignment(1., .5)
        table.attach(label, 2, 3, 1, 2)
        nspin = GtkSpinButton(GtkAdjustment(0, 0, 8, 1, 8, 0), 0, 0)
        nspin.set_editable(FALSE)
        nspin.set_state(STATE_INSENSITIVE)
        table.attach(nspin, 3, 4, 1, 2)

        label = GtkLabel("Param:")
        label.set_alignment(1., .5)
        table.attach(label, 2, 3, 2, 3)
        pentry = GtkEntry()
        pentry.set_editable(FALSE)
        pentry.set_state(STATE_INSENSITIVE)
        table.attach(pentry, 3, 4, 2, 3)

        label = GtkLabel("Independent Var:")
        label.set_alignment(1., .5)
        table.attach(label, 2, 3, 3, 4)
        iventry = GtkEntry()
        iventry.set_editable(FALSE)
        iventry.set_state(STATE_INSENSITIVE)
        table.attach(iventry, 3, 4, 3, 4)

        label = GtkLabel("Dependent Var:")
        label.set_alignment(1., .5)
        table.attach(label, 2, 3, 4, 5)
        dventry = GtkEntry()
        dventry.set_editable(FALSE)
        dventry.set_state(STATE_INSENSITIVE)
        table.attach(dventry, 3, 4, 4, 5)

        lframe = GtkFrame()
        lframe.set_shadow_type(SHADOW_IN)
        main_box.pack_start(lframe,expand=FALSE,fill=TRUE)
        explabel = GtkLabel("Choose a Fitting Function")
        lframe.add(explabel)



#       CALLBACK FUNCTIONS
        def select_function(_clist, row, col, event, functions=functions, 
           label=explabel, fentry=fentry, pentry=pentry, nspin=nspin, 
           iventry=iventry, dventry=dventry, params=params):
		k = _clist.get_text(row, col)
                f = functions[k]
		label.set_text(f["Doc"])
                params["fit"]=f
		fentry.backward_delete(fentry.get_length())
		fentry.insert_defaults("y = "+str_exp(f["Exp"],f["Par"],f["Fmt"]))
		nspin.set_value(f["NumPar"])
		iventry.set_text(f["IVar"])
		dventry.set_text(f["DVar"])
		s = ""
		for i in f["Par"]:
			s = s + i + ", "
		pentry.set_text(s[:len(s)-2])


 	clist.connect("select_row", select_function)
        clist.select_row(0, 0)	
        myk = clist.get_text(0,0)
        myf = functions[myk]

        params["fit"]=myf
	main_box.show_all()

def fit_dialog(params,page_box,next=None,prev=None,cancel=None):
        f=params["fit"]
        data=params["data"]
  
	table = GtkTable(len(f["Par"])+3, 2, FALSE)
	table.set_col_spacings(5)
	table.set_row_spacings(5)
	page_box.pack_start(table)

        frame=GtkFrame("Enter fit parameter guess values for "+f["Doc"]+":")
        frame.add(GtkLabel("y = "+str_exp(f["Exp"],f["Par"],f["Fmt"])))
	table.attach(frame, 0, 2, 0, 1)
	table.attach(GtkLabel("Variable"), 0, 1, 1, 2)
	table.attach(GtkLabel("Value"), 1, 2, 1, 2)
	table.attach(GtkHSeparator(), 0, 2, 2, 3)
	r = 3
	entries = []
        params["entries"]=entries
	for i in f["Par"]:
                parlabel=GtkLabel(i+":")
                parlabel.set_alignment(1., .5)
		table.attach(parlabel, 0, 1, r, r+1)
		entry = GtkEntry()
		entries.append(entry)
		entry.set_text("0.0")
		table.attach(entry, 1, 2, r, r+1,xpadding=5)
		r = r + 1



	table.show_all()

def run_fit(params,page_box,next=None,prev=None,cancel=None):
        f=params["fit"]
        data=params["data"]
 
        table = GtkTable(len(f["Par"])+3, 2, FALSE)
	table.set_col_spacings(5)
	table.set_row_spacings(5)
	page_box.pack_start(table)

        frame=GtkFrame(f["Doc"]+" fit results:")
        frame.add(GtkLabel("y = "+str_exp(f["Exp"],f["Par"],f["Fmt"])))
	table.attach(frame, 0, 2, 0, 1)
	table.attach(GtkLabel("Variable"), 0, 1, 1, 2)
	table.attach(GtkLabel("Value"), 1, 2, 1, 2)
	table.attach(GtkHSeparator(), 0, 2, 2, 3)
	r = 3
	entries = []
	for i in f["Par"]:
                parlabel=GtkLabel(i+":")
                parlabel.set_alignment(1., .5)
		table.attach(parlabel, 0, 1, r, r+1)
		entry = GtkEntry()
                entry.set_editable(FALSE)
		entries = entries + [entry]
#		entry.set_text("0.0")
		table.attach(entry, 1, 2, r, r+1,xpadding=5)
		r = r + 1

        parlabel=GtkLabel("Quality of fit (Chi square):")
        parlabel.set_alignment(1., .5)
        
	table.attach(parlabel, 0, 1, r, r + 1,)
	err_entry = GtkEntry()
        err_entry.set_editable(FALSE)
	table.attach(err_entry, 1, 2, r, r + 1,xpadding=5,ypadding=5)
	r = r + 1
	table.attach(GtkHSeparator(), 0, 2, r, r + 1,xpadding=5,ypadding=5)
	r = r + 1
	table.attach(GtkLabel("Press 'Back' to rerun fit with these"\
        " parameters\nor re-enter fit parameters"), 0, 2, r, r + 1)

        oldentries=params["entries"]
        p=()
        for n in range(len(oldentries)):
                s = oldentries[n].get_text()
                p = p+(atof(s),)

	table.show_all()
        try:
          cursor = cursor_new(150 & 0xfe)
          page_box.get_window().set_cursor(cursor)
          while events_pending():
            mainiteration(block=FALSE)
          fit, error = leastSquaresFit(f["Function"], p, data)
          
          params['fitparams']=fit

          for n in range(len(entries)):
                  entries[n].set_text(str(fit[n]))
                  oldentries[n].set_text(str(fit[n]))
          err_entry.set_text(str(error))
        except:
          err_entry.set_text("Unable to fit data with these parameters")
          for i in range(len(entries)):
            entries[i].set_sensitive(FALSE)
            entries[i].set_text(oldentries[i].get_text())
          next.set_sensitive(FALSE)

        cursor = cursor_new(68 & 0xfe)
        page_box.get_window().set_cursor(cursor)
        while events_pending():
          mainiteration(block=FALSE)

def fit_arrays(a1, a2):
        temp1=a1.shape
        temp2=a2.shape
        max_index=min(a1.shape[0], a2.shape[0])
        if max_index==0:
          return
        data=map(None,a1[:max_index],a2[:max_index])
	fit(data)	
	return


def module_called():
  colnum=sg.get_selection()['col0']
  fit_arrays(sg.col(colnum),sg.col(colnum+1))

def finish_func(params):
  if not params['sheet_choice']:
    return
  datax=map(lambda (x,y):x,params['data'])
  y=[]
  for x in datax:
    y.append(params['fit']['Function'](params['fitparams'],x))
  
  if params['col_choice']==1: # New col after...
    colnum=len(sg.column_names(params['sheet_name']))
    sg.add_columns(1,params['sheet_name'])
  else:
    colnum=params['new_col']
  sg.setcol(colnum,y,params['sheet_name'])
  if params['rename_choice']:
    vals=functions.values()
    keys=functions.keys()
    for i in range(len(functions)):
      if params['fit']['Function']==vals[i]['Function']:
        break
    sg.set_column_name(params['sheet_name'],colnum,keys[i])

def plot_finish_func(params):
  if not params['plot_choice']:
    return
  f=params['fit']
  tpl=[]
  for i in f['Fmt']:
    tpl.append(params['fitparams'][i])
 
  exp=str_exp(f["Exp"],params['fitparams'],f["Fmt"])
  vals=functions.values()
  keys=functions.keys()
  for i in range(len(functions)):
      if params['fit']['Function']==vals[i]['Function']:
        break

  sg.plot_exp(params['plot_name'],params['plot_layer'],exp,keys[i])


def col_combo_changed(entry,params,elem):
  text=entry.get_text()
  cnt=0
  for col in params["cols"]:
    if col==text:
      params[elem]=cnt
      break
    cnt=cnt+1

def dataset_combo_changed(entry,params,elem):
  text=entry.get_text()
  cnt=0
  for col in params["datasets"]:
    if col==text:
      params[elem]=cnt
      break
    cnt=cnt+1


def col_func(params,page_box,next=None,prev=None,cancel=None):
  vbox=GtkVBox()
  page_box.pack_start(vbox)
  vbox.set_homogeneous(FALSE)  

  hbox=GtkHBox()
  hbox.pack_start(GtkLabel("Please select columns for fit:"),expand=FALSE,fill=FALSE,padding=5)
  vbox.pack_start(hbox,expand=TRUE,fill=TRUE,padding=5)


# X Values
  menu=GtkCombo()
  params['sheet_name']=sg.active_worksheet_name()
  mysheet=pysga.SGworksheet(params['sheet_name'])
  cols=sg.column_names(params['sheet_name'])
  params['cols']=cols
  menu.set_popdown_strings(cols)
  menu.set_value_in_list(TRUE,FALSE)
  active=mysheet.col0()
  params['col_num']=active
  menu.entry.set_text(cols[active])

  menu.entry.set_editable(FALSE)
  params["xcolumn"]=active
  menu.entry.connect("changed",col_combo_changed,params,"xcolumn")


  hbox=GtkHBox()
  hbox.pack_start(GtkLabel("X values:"),expand=FALSE,fill=FALSE,padding=5)
  hbox.pack_start(menu,expand=FALSE,fill=FALSE,padding=5)
  vbox.pack_start(hbox,expand=TRUE,fill=TRUE,padding=5)

# Y Values  
  menu=GtkCombo()
  params['sheet_name']=sg.active_worksheet_name()
  cols=sg.column_names(params['sheet_name'])
  menu.set_popdown_strings(cols)
  menu.set_value_in_list(TRUE,FALSE)
  yactive=active+1
  if active==len(cols)-1: # Last column selected
    yactive=active-1
    if len(cols)==1:
      yactive=1
  params['col_num']=active
  menu.entry.set_text(cols[yactive])

  menu.entry.set_editable(FALSE)
  params["ycolumn"]=yactive
  menu.entry.connect("changed",col_combo_changed,params,"ycolumn")

  hbox=GtkHBox()
  hbox.pack_start(GtkLabel("Y values:"),expand=FALSE,fill=FALSE,padding=5)
  hbox.pack_start(menu,expand=FALSE,fill=FALSE,padding=5)
  vbox.pack_start(hbox,expand=TRUE,fill=TRUE,padding=5)
  vbox.show_all()
  

def plot_func(params,page_box,next=None,prev=None,cancel=None):
  vbox=GtkVBox()
  page_box.pack_start(vbox)
  vbox.set_homogeneous(FALSE)  

  hbox=GtkHBox()
  hbox.pack_start(GtkLabel("Please select dataset for fit:"),expand=FALSE,fill=FALSE,padding=5)
  vbox.pack_start(hbox,expand=TRUE,fill=TRUE,padding=5)


# X Values
  menu=GtkCombo()
  try:
    params['plot_name']=sg.active_plot()
    params['plot_layer']=sg.plot_active_layer()
    params['datasets']=sg.plot_layer_dataset_list(params['plot_name'],params['plot_layer'])
    if len(params['datasets'])<=0:
      raise ValueError
  except:
    hbox.pack_start(GtkLabel("Unable to determine dataset names!"),expand=FALSE,fill=FALSE,padding=5)
    vbox.pack_start(hbox,expand=TRUE,fill=TRUE,padding=5)  
    vbox.show_all()
    return
  
  menu.set_popdown_strings(params['datasets'])
  menu.set_value_in_list(TRUE,FALSE)
  active=0
  params['dataset_choice']=active
  menu.entry.set_text(params['datasets'][active])

  menu.entry.set_editable(FALSE)
  menu.entry.connect("changed",dataset_combo_changed,params,"dataset_choice")


  hbox=GtkHBox()
  hbox.pack_start(GtkLabel("Dataset:"),expand=FALSE,fill=FALSE,padding=5)
  hbox.pack_start(menu,expand=TRUE,fill=TRUE,padding=5)
  vbox.pack_start(hbox,expand=TRUE,fill=TRUE,padding=5)

  vbox.show_all()


def rename_toggle(button,elem,params,widget):
  params[elem]=(params[elem]+1)%2
  if widget:
    if params[elem]==0:
      widget.set_sensitive(FALSE)
    else:
      widget.set_sensitive(TRUE)


def action_page(params,page_box,next=None,prev=None,cancel=None):
# First create new sheet object
  mysheet=pysga.SGworksheet(params['sheet_name'])
  f=params["fit"]

  frame=GtkFrame(f["Doc"]+" fit results:")
  frame.add(GtkLabel("y = "+str_exp(f["Exp"],f["Par"],f["Fmt"])))
  vbox=GtkVBox()  
  page_box.pack_start(vbox)
  vbox.pack_start(frame)
  vbox.set_homogeneous(FALSE)  

  hbox=GtkHBox()
  sheetbutton = GtkCheckButton(label="Put values in sheet") 
  hbox.pack_start(sheetbutton,expand=FALSE,fill=FALSE,padding=0)
  vbox.pack_start(hbox,expand=TRUE,fill=TRUE,padding=5)
  sheetbutton.set_active(TRUE)
  params['sheet_choice']=TRUE


  frame=GtkFrame("Select column:")  
  sheet_vbox=GtkVBox()
  frame.add(sheet_vbox)
  vbox.pack_start(frame,expand=TRUE,fill=TRUE,padding=15)
  sheetbutton.connect("toggled",rename_toggle,'sheet_choice',params,frame)  

# This is generic
  cols=sg.column_names(params['sheet_name'])  
  active=mysheet.col0()+2
  while active<len(cols):
    try:
      c=len(sg.col(active))
    except:
      break
    if c<=0:
      break
    active=active+1
    
  if active>=len(cols):
    active=len(cols)-1
    params['col_choice']=1
  else:
    params['col_choice']=0
  params['new_col']=active
  

  button1 = GtkRadioButton(None, "Existing column:") 
  menu=GtkCombo()
  menu.set_popdown_strings(cols)
  menu.set_value_in_list(TRUE,FALSE)
  menu.entry.set_text(cols[active])
  hbox=GtkHBox()
  hbox.pack_start(button1,expand=FALSE,fill=FALSE,padding=0)
  hbox.pack_start(menu,expand=FALSE,fill=FALSE,padding=5)
  sheet_vbox.pack_start(hbox,expand=TRUE,fill=TRUE,padding=5)
  
  button2 = GtkRadioButton(button1, "New column after column '"+cols[-1]+"'")
  hbox=GtkHBox()  
  hbox.pack_start(button2,expand=FALSE,fill=FALSE,padding=0)
  sheet_vbox.pack_start(hbox,expand=TRUE,fill=TRUE,padding=5)

  menu.entry.set_editable(FALSE)
  menu.set_sensitive((params['col_choice']+1)%2)
  menu.entry.connect("changed",col_combo_changed,params,"new_col")
  button2.connect("toggled",rename_toggle,"col_choice",params,menu)  
  if params['col_choice']==1:
    button2.set_active(params['col_choice'])
    params['col_choice']=1

  sheet_vbox.pack_start(GtkHSeparator(),expand=TRUE,fill=TRUE,padding=5) 
  hbox=GtkHBox()
  button3 = GtkCheckButton(label="Rename column using fit name") 
  hbox.pack_start(button3,expand=FALSE,fill=FALSE,padding=0)
  sheet_vbox.pack_start(hbox,expand=TRUE,fill=TRUE,padding=5)
  button3.set_active(TRUE)
  params['rename_choice']=TRUE
  button3.connect("toggled",rename_toggle,'rename_choice',params,None)

  vbox.show_all()

def plot_action_page(params,page_box,next=None,prev=None,cancel=None):
  f=params["fit"]

  frame=GtkFrame(f["Doc"]+" fit results:")
  frame.add(GtkLabel("y = "+str_exp(f["Exp"],f["Par"],f["Fmt"])))
  vbox=GtkVBox()  
  page_box.pack_start(vbox)
  vbox.pack_start(frame)
  vbox.set_homogeneous(FALSE)  

  hbox=GtkHBox()
  sheetbutton = GtkCheckButton(label="Plot result") 
  hbox.pack_start(sheetbutton,expand=FALSE,fill=FALSE,padding=0)
  vbox.pack_start(hbox,expand=TRUE,fill=TRUE,padding=5)
  sheetbutton.set_active(TRUE)
  params['plot_choice']=TRUE


  sheetbutton.connect("toggled",rename_toggle,'plot_choice',params,frame)  

  vbox.show_all()


def sheet_fit_init():
  params={
#These are compulsory
  "funcs":[col_func,fit,fit_dialog,run_fit,action_page],
  "finish":finish_func,
#This is optional
  "labels":["Select columns","Select fit","Enter fit parameters","Fit results",
  "Insert values"],
  "title": "Curve fitting",
  "mode":1
  }
  wizard=sgagtk.SGwizard(params)

#Module registration
if (sg):
  sg.register_plugin("Curve fitting","Worksheet:Analysis:",sheet_fit_init)

def plot_fit_init():
  params={
#These are compulsory
  "funcs":[plot_func,fit,fit_dialog,run_fit,plot_action_page],
  "finish":plot_finish_func,
#This is optional
  "labels":["Select columns","Select fit","Enter fit parameters","Fit results",
  "Insert values"],
  "title": "Curve fitting",
  "mode":2
  }
  wizard=sgagtk.SGwizard(params)

#Module registration
if (sg):
  sg.new_plugin_group("Plot:Analysis:")
  sg.register_plugin("Curve fitting","Plot:Analysis:",plot_fit_init)


# Test for linear fit:
#
# from gtkLeastSquares import *
# data = [ (0., 0.), (1., 1.1), (2., 1.98), (3., 3.05) ]
# fit(data)
#
# Test for fit_arrays:
# from gtkLeastSquares import *
# a = array([0., 1., 2., 3.]) 
# b = array([0., 1.1, 1.98, 3.05]) 
# fit_arrays(a, b)
