Skip to content
Snippets Groups Projects
Commit 2ecb69d0 authored by Andrew E. Torda's avatar Andrew E. Torda
Browse files

Tidying.

parent 18a51fe0
Branches
No related tags found
No related merge requests found
...@@ -24,45 +24,32 @@ import ( ...@@ -24,45 +24,32 @@ import (
"fyne.io/fyne/v2/widget" "fyne.io/fyne/v2/widget"
) )
// A message is sent to the output tab, telling us the current status // We have a channel that sends the status to the output tab.
type status uint8 type status uint8
const ( const (
calculating status = iota calculating status = iota // currently running a simulation
resultsReady resultsReady // You should collect the results
errorCalc errorCalc // You should display an error message
) )
type workstatus struct { type workstatus struct {
fdata, xdata []byte fdata, xdata []byte // the data in a function or X value plot
err error err error
status status status status // Tells us what we should do now
}
// showIniTab is a card to be shown before we have any calculations.
func showIniTab(cntr *fyne.Container) {
cntr.Add(widget.NewCard("No results yet", "go back to the input", nil))
} }
// If we get a message with an error, display it // If we get a message with an error, display it
func showErrTab(cntr *fyne.Container, err error) { func showErrTab(cntr *fyne.Container, err error) {
cntr.Objects = nil
cntr.Add(widget.NewCard("ERROR in MC", err.Error(), nil)) cntr.Add(widget.NewCard("ERROR in MC", err.Error(), nil))
} }
// emptyContainer gets rid of any old contents. Could I just say,
// cntr.Objects = nil ?
func emptyContainer(cntr *fyne.Container) {
for _, o := range cntr.Objects {
cntr.Remove(o)
}
}
// showCalcTab is shown while calculating. Have to check if the refresh() // showCalcTab is shown while calculating. Have to check if the refresh()
// is actually necessary. // is actually necessary.
func showCalcTab(cntr *fyne.Container) { func showCalcTab(cntr *fyne.Container) {
emptyContainer(cntr) cntr.Objects = nil
cntr.Add(widget.NewCard("calculating", "busy", nil)) cntr.Add(widget.NewCard("calculating", "busy", nil))
cntr.Refresh()
} }
// fwrt writes the file. It will be called by the filesave dialog // fwrt writes the file. It will be called by the filesave dialog
...@@ -115,29 +102,27 @@ func png2image(d []byte, fname string) *canvas.Image { ...@@ -115,29 +102,27 @@ func png2image(d []byte, fname string) *canvas.Image {
// showResultsTab show the two plots // showResultsTab show the two plots
func showResultsTab(cntr *fyne.Container, win fyne.Window, fdata, xdata []byte) { func showResultsTab(cntr *fyne.Container, win fyne.Window, fdata, xdata []byte) {
emptyContainer(cntr) cntr.Objects = nil
fImage := png2image(fdata, "function.png") fImage := png2image(fdata, "function.png")
xImage := png2image(xdata, "xdata.png") xImage := png2image(xdata, "xdata.png")
right := container.NewGridWithRows(2, fImage, xImage) right := container.NewGridWithRows(2, fImage, xImage)
left := leftbar(win, fdata, xdata) left := leftbar(win, fdata, xdata)
box := container.NewHBox(left, right) box := container.NewHBox(left, widget.NewSeparator(), right)
cntr.Add(box) cntr.Add(box)
} }
// outputTab is run as a background process. After showing the initial // outputTab is run as a background process. After showing the initial
// screen, it sits and waits on notifications. When it gets one, // screen, it sits and waits on notifications. When it gets one,
// it redraws its tab. // it redraws its tab.
func outputTab(chn chan workstatus, cntr *fyne.Container, form *widget.Form, win fyne.Window) { func outputTab(genParams genParams, cntr *fyne.Container, form *widget.Form) {
showIniTab(cntr) cntr.Add(widget.NewCard("No results yet", "go back to the input", nil))
breaker() for s := range genParams.chn {
for s := range chn {
switch s.status { switch s.status {
case calculating: case calculating:
showCalcTab(cntr) showCalcTab(cntr)
case resultsReady: case resultsReady:
showResultsTab(cntr, win, s.fdata, s.xdata) showResultsTab(cntr, genParams.win, s.fdata, s.xdata)
breaker()
form.Enable() form.Enable()
form.Refresh() form.Refresh()
case errorCalc: case errorCalc:
......
...@@ -21,6 +21,14 @@ import ( ...@@ -21,6 +21,14 @@ import (
"example.com/ackley_mc/mc_work" "example.com/ackley_mc/mc_work"
) )
// startrun is what happens when you click on the form's button to start
// a calculation.
func startrun(genParams genParams, form *widget.Form, mcPrm *mcwork.McPrm) {
form.Disable()
go mcWrap(genParams)
}
// fslicestrng makes a nice string from a slice of float32's // fslicestrng makes a nice string from a slice of float32's
func fslicestrng(f []float32) string { func fslicestrng(f []float32) string {
if len(f) == 0 { if len(f) == 0 {
...@@ -33,6 +41,7 @@ func fslicestrng(f []float32) string { ...@@ -33,6 +41,7 @@ func fslicestrng(f []float32) string {
return s return s
} }
// str2f32 takes a string and returns a slice of float32's // str2f32 takes a string and returns a slice of float32's
func s2f32(s string) ([]float32, error) { func s2f32(s string) ([]float32, error) {
y := strings.Split(s, ",") y := strings.Split(s, ",")
...@@ -62,7 +71,7 @@ func validateXini(s string) error { ...@@ -62,7 +71,7 @@ func validateXini(s string) error {
// This gets put on the list of things to do from the reload function. // This gets put on the list of things to do from the reload function.
func floatItem(xAddr *float64, label string) (*widget.FormItem, func()) { func floatItem(xAddr *float64, label string) (*widget.FormItem, func()) {
bind := binding.BindFloat(xAddr) bind := binding.BindFloat(xAddr)
entry := widget.NewEntryWithData(binding.FloatToStringWithFormat(bind, "%10.2f")) entry := widget.NewEntryWithData(binding.FloatToStringWithFormat(bind, "%.2f"))
fitem := widget.NewFormItem(label, entry) fitem := widget.NewFormItem(label, entry)
reloadfunc := func() { bind.Reload() } reloadfunc := func() { bind.Reload() }
return fitem, reloadfunc return fitem, reloadfunc
...@@ -72,7 +81,8 @@ func floatItem(xAddr *float64, label string) (*widget.FormItem, func()) { ...@@ -72,7 +81,8 @@ func floatItem(xAddr *float64, label string) (*widget.FormItem, func()) {
// It returns the screen and a function to be called to refresh it. // 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 // This is necessary, since there is a button to read parameters from
// a file, which means all the values should be updated. // a file, which means all the values should be updated.
func paramBox(mcPrm *mcwork.McPrm, parent fyne.Window, chn chan workstatus) *widget.Form { func paramBox(genParams genParams) *widget.Form {
mcPrm := genParams.mcPrm
iniTmpItem, iniTmpReload := floatItem(&mcPrm.IniTmp, "initial temperature") iniTmpItem, iniTmpReload := floatItem(&mcPrm.IniTmp, "initial temperature")
fnlTmpItem, fnlTmpReload := floatItem(&mcPrm.FnlTmp, "final temperature") fnlTmpItem, fnlTmpReload := floatItem(&mcPrm.FnlTmp, "final temperature")
xDltaItem, xDltaReload := floatItem(&mcPrm.XDlta, "X delta") xDltaItem, xDltaReload := floatItem(&mcPrm.XDlta, "X delta")
...@@ -103,14 +113,14 @@ func paramBox(mcPrm *mcwork.McPrm, parent fyne.Window, chn chan workstatus) *wid ...@@ -103,14 +113,14 @@ func paramBox(mcPrm *mcwork.McPrm, parent fyne.Window, chn chan workstatus) *wid
_ = nStepBnd.Reload() _ = nStepBnd.Reload()
} }
rdfile := func() { rdwork(mcPrm, parent, reloadPTab) } rdfile := func() { rdwork(mcPrm, genParams.win, reloadPTab) }
rdfileBtn := widget.NewButton("get file ", rdfile) rdfileBtn := widget.NewButton("get file ", rdfile)
rdFileItem := widget.NewFormItem("read from a file", rdfileBtn) rdFileItem := widget.NewFormItem("read from a file", rdfileBtn)
r := widget.NewForm( r := widget.NewForm(
iniTmpItem, fnlTmpItem, xDltaItem, xIniItem, nStepItem, rdFileItem) iniTmpItem, fnlTmpItem, xDltaItem, xIniItem, nStepItem, rdFileItem)
r.SubmitText = "start calculation" r.SubmitText = "start calculation"
r.OnSubmit = func() { startrun(chn, parent, r, mcPrm) } r.OnSubmit = func() { startrun(genParams, r, mcPrm) }
return r return r
} }
...@@ -149,24 +159,18 @@ func rdwork(mcPrm *mcwork.McPrm, parent fyne.Window, reloadPTab func()) { ...@@ -149,24 +159,18 @@ func rdwork(mcPrm *mcwork.McPrm, parent fyne.Window, reloadPTab func()) {
// mcWrap is run in the background so the interface does not block. // mcWrap is run in the background so the interface does not block.
// It runs the Monte Carlo, then when it is finished, sends a message // It runs the Monte Carlo, then when it is finished, sends a message
// down the channel. // down the channel.
func mcWrap(chn chan workstatus, parent fyne.Window, mcPrm *mcwork.McPrm) { func mcWrap(genParams genParams) {
chn := genParams.chn
chn <- workstatus{status: calculating} chn <- workstatus{status: calculating}
if fdata, xdata, err := mcwork.DoRun(mcPrm); err != nil { if fdata, xdata, err := mcwork.DoRun(genParams.mcPrm); err != nil {
dialog.NewError(err, parent).Show() dialog.NewError(err, genParams.win).Show()
chn <- workstatus{status: errorCalc, err: err} chn <- workstatus{status: errorCalc, err: err}
} else { } else {
chn <- workstatus{fdata: fdata, xdata: xdata, status: resultsReady} chn <- workstatus{fdata: fdata, xdata: xdata, status: resultsReady}
} }
} }
// startrun is what happens when you click on the button to start
// a calculation.
func startrun(chn chan workstatus, parent fyne.Window, form *widget.Form, mcPrm *mcwork.McPrm) {
form.Disable()
go mcWrap(chn, parent, mcPrm)
}
// inputTab sets up the input page. // inputTab sets up the input page.
func inputTab(mcPrm *mcwork.McPrm, parent fyne.Window, chn chan workstatus) *widget.Form { func inputTab(genParams genParams) *widget.Form {
return paramBox(mcPrm, parent, chn) return paramBox(genParams)
} }
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
package ui package ui
import ( import (
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/app" "fyne.io/fyne/v2/app"
"fyne.io/fyne/v2/container" "fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/widget" "fyne.io/fyne/v2/widget"
...@@ -14,28 +15,30 @@ import ( ...@@ -14,28 +15,30 @@ import (
"example.com/ackley_mc/mc_work" "example.com/ackley_mc/mc_work"
) )
func UiDoRun(mcPrm *mcwork.McPrm) error { // Convenience struct so we do not have perverse long parameter lists
type genParams struct{
mcPrm *mcwork.McPrm // The MC parameters shared with the main simulation
win fyne.Window // Parent window. Needed for all dialog boxes
chn chan workstatus // To tell the output tab current status
}
// UiDoRun sets up the screen to do a run. It is effectively a big
// wrapper around the DoRun() function.
func UiDoRun(mcPrm *mcwork.McPrm) error {
a := app.NewWithID("Monte Carlo") a := app.NewWithID("Monte Carlo")
w := a.NewWindow("Monte Carlo") win := a.NewWindow("Monte Carlo")
quitbutton := widget.NewButton("click somewhere in here to exit", a.Quit)
chn := make(chan workstatus) chn := make(chan workstatus)
// inputTabCallback := func() *widget.Form { return (inputTab(mcPrm, w, chn))} var genParams = genParams {mcPrm, win, chn}
inputForm := inputTab(mcPrm, w, chn) inputForm := inputTab(genParams)
quitbutton := widget.NewButton("click somewhere in here to confirm", a.Quit)
t1 := container.NewTabItem("input tab", inputForm) t1 := container.NewTabItem("input tab", inputForm)
cntrOut := container.NewWithoutLayout() cntrOut := container.NewWithoutLayout()
t2 := container.NewTabItem("output tab", cntrOut) t2 := container.NewTabItem("output tab", cntrOut)
t3 := container.NewTabItem("quit me", quitbutton) t3 := container.NewTabItem("quit", quitbutton)
appTab := container.NewAppTabs(t1, t2, t3) appTab := container.NewAppTabs(t1, t2, t3)
w.SetContent(appTab) win.SetContent(appTab)
go outputTab(chn, cntrOut, inputForm, w) go outputTab(genParams, cntrOut, inputForm)
w.ShowAndRun() win.ShowAndRun()
close(chn) close(chn)
return nil return nil
} }
// nothing does nothing
func nothing(...interface{}) {}
// and breaker does not do much more
func breaker(...interface{}) {}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment