Skip to main content
Sign in
Snippets Groups Projects
Select Git revision
  • 6f9b667cbe28f81995418a865facea3085a8ac59
  • master default protected
  • devel
  • adaptive_step_size
4 results

param_tab.go

Blame
  • Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    param_tab.go 5.24 KiB
    // Feb 2022
    // Set up a tab for getting Monte Carlo Parameters
    // This should return an object that can be embedded in a tabbed page.
    //go:build !no_gfx
    // +build !no_gfx
    
    package ui
    
    import (
    	"fmt"
    	"fyne.io/fyne/v2"
    	"fyne.io/fyne/v2/container"
    	"fyne.io/fyne/v2/data/binding"
    	"fyne.io/fyne/v2/dialog"
    	"fyne.io/fyne/v2/storage"
    	"fyne.io/fyne/v2/widget"
    	"os"
    	"strconv"
    	"strings"
    
    	"example.com/ackley_mc/mc_work"
    )
    
    // fslicestrng makes a nice string from a slice of float32's
    func fslicestrng(f []float32) string {
    	if len(f) == 0 {
    		return ""
    	}
    	s := fmt.Sprintf("%g", f[0])
    	for i := 1; i < len(f); i++ {
    		s += fmt.Sprintf(", %g", f[i])
    	}
    	return s
    }
    
    // str2f32 takes a string and returns a slice of float32's
    func s2f32(s string) ([]float32, error) {
    	y := strings.Split(s, ",")
    	if len(y) < 1 {
    		return nil, fmt.Errorf("no fields found")
    	}
    	x := make([]float32, len(y))
    	for i, s := range y {
    		if r, err := strconv.ParseFloat(strings.TrimSpace(s), 32); err != nil {
    			return nil, fmt.Errorf("tried to parse %v %v", s, err)
    		} else {
    			x[i] = float32(r)
    		}
    	}
    	return x, nil
    }
    
    // validateXini checks if we can convert a string to a slice of float32's.
    // Unfortunately, fyne calls this function every time the cursor is moved
    // in the field. I think this is a little bug on their part.
    func validateXini(s string) error {
    	_, err := s2f32(s)
    	return err
    }
    
    // paramScreen sets up a screen full of parameters we can adjust.
    // It returns the screen and a function to be called to refresh it.
    // This is necessary, since there is a button to read parameters from
    // a file, which means all the values should be updated.
    func paramScreen(mcPrm *mcwork.McPrm) (*fyne.Container, func()) {
    	iniTmp := binding.BindFloat(&mcPrm.IniTmp)
    	iniTmpEntry := widget.NewEntryWithData(binding.FloatToString(iniTmp))
    	iniTmpLabel := widget.NewLabel("initial temp")
    
    	fnlTmp := binding.BindFloat(&mcPrm.FnlTmp)
    	fnlTmpEntry := widget.NewEntryWithData(binding.FloatToString(fnlTmp))
    	fnlTmpLabel := widget.NewLabel("final temp")
    
    	xDlta := binding.BindFloat(&mcPrm.XDlta)
    	xDltaEntry := widget.NewEntryWithData(binding.FloatToString(xDlta))
    	xDltaLabel := widget.NewLabel("X delta")
    
    	xIniStr := binding.NewString()
    	xIniStr.Set(fslicestrng(mcPrm.XIni))
    	xIniEntry := widget.NewEntryWithData(xIniStr)
    	xIniEntry.OnChanged = func(s string) {
    		x, err := s2f32(s)
    		if err != nil {
    			xIniEntry.SetValidationError(err)
    		} else {
    			mcPrm.XIni = append(mcPrm.XIni[:0], x...)
    		}
    	}
    	xIniEntry.Validator = validateXini
    	xIniLabel := widget.NewLabel("X ini")
    
    	nStepBnd := binding.BindInt(&mcPrm.NStep)
    	nStepEntry := widget.NewEntryWithData(binding.IntToString(nStepBnd))
    	nStepLabel := widget.NewLabel("num steps")
    	
    	r := container.NewGridWithColumns(2,
    		iniTmpLabel, iniTmpEntry,
    		fnlTmpLabel, fnlTmpEntry,
    		xDltaLabel, xDltaEntry,
    		xIniLabel, xIniEntry,
    		nStepEntry, nStepLabel)
    	refreshPScreen := func() {
    		iniTmp.Reload()
    		fnlTmp.Reload()
    		xDlta.Reload()
    		xIniEntry.SetText(fslicestrng(mcPrm.XIni))
    		nStepBnd.Reload()
    	}
    	return r, refreshPScreen
    }
    
    
    // checkOk does a last pass over all fields and calls any validators
    // it can find. A run can only be started if everything seems to be OK.
    func checkOk(c *fyne.Container, parent fyne.Window) error {
    	for _, obj := range c.Objects {
    		if ent, ok := obj.(*widget.Entry); ok {
    			if err := ent.Validate(); err != nil {
    				dialog.NewError(err, parent).Show()
    				return err
    			}
    		}
    	}
    	return nil
    }
    
    // rdwork is called after the file open dialog gets "OK"
    func rdwork(mcPrm *mcwork.McPrm, parent fyne.Window, refreshme func()) error {
    	var e error
    	getmcprm := func(rd fyne.URIReadCloser, err error) {
    		if err != nil {
    			fmt.Println("error given to getmcprm")
    			dialog.NewError(err, parent).Show()
    			e = err
    			return
    		}
    		if rd == nil { // cancelled
    			return
    		}
    		defer rd.Close()
    		if err := mcwork.RdPrm(rd, mcPrm); err != nil {
    			e = err
    			return
    		} else {
    			fmt.Println("I think mcprm is", mcPrm)
    			refreshme()
    			dialog.NewInformation("OK", "read file, no errors", parent).Show()
    			return
    		}
    	}
    
    	t := dialog.NewFileOpen(getmcprm, parent)
    
    	if cwd, err := os.Getwd(); err == nil {
    		if y, err := storage.ListerForURI(storage.NewFileURI(cwd)); err == nil {
    			t.SetLocation(y) // If there was an error, we just use default location
    		}
    	}
    
    	t.Show() // set an error and see if anybody notices
    	return e
    }
    
    // inputTab sets up the input page. At the top, we have a button to read from
    // a file.
    func inputTab(mcPrm *mcwork.McPrm, parent fyne.Window, chn chan workstatus) (*fyne.Container, error) {
    	paramBinding, refreshPscreen := paramScreen(mcPrm)
    	rdfile := func() {
    		if err := rdwork(mcPrm, parent, refreshPscreen); err != nil {
    			dialog.NewError(err, parent).Show()
    		}
    	}
    	startrun := func() {
    		if err := checkOk(paramBinding, parent); err != nil {
    			dialog.NewError(err, parent).Show()
    			return
    		}
    		chn <- workstatus{status: calculating}
    		if fdata, xdata, err := mcwork.DoRun(mcPrm); err != nil {
    			dialog.NewError(err, parent).Show()
    			close(chn)
    			return
    		} else {
    			chn <- workstatus{fdata: fdata, xdata: xdata, status: resultsReady}
    		}
    	}
    	rdfileBtn := widget.NewButton("read file", rdfile)
    	runBtn := widget.NewButton("start run", startrun)
    	buttons := container.NewVBox(rdfileBtn, runBtn)
    	c := container.NewHBox(buttons, paramBinding)
    	return c, nil
    }