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

Useful working version.

parent 6f9b667c
Branches
No related tags found
No related merge requests found
# Use the normal go build system for everything, but we want to automate some simple
# commands.
.POSIX:
LINTER=~/go/bin/linux_amd64/golangci-lint
all:
go build ./...
......@@ -8,6 +11,12 @@ test:
go test ./...
go test -tags no_gfx ./...
gofmt:
gofmt -s -w .
lint:
$(LINTER) run
clean:
go clean
rm -rf */*_delme.*
......
// Aug 2021
//go:build !no_gfx
// +build !no_gfx
// Ackley_mc is for playing with Monte Carlo or simulated annealing on the
......
// Aug 2021
//go:build no_gfx
// +build no_gfx
// Ackley_mc is for playing with Monte Carlo or simulated annealing on the
......
......@@ -31,7 +31,6 @@ func getSeed() int64 {
seedLocker.Unlock()
return r
}
func breaker(...interface{}) {}
type withBytes interface {
Bytes() []byte
......
......@@ -26,6 +26,6 @@ var seed int64 // for random numbers, but each thread gets its own value
// usage
func usage() {
u := `[options] input_parameter_file`
u := `[options] [input_parameter_file]`
fmt.Fprintf(flag.CommandLine.Output(), "usage of %s: %s\n", os.Args[0], u)
}
// Aug 2021
//go:build no_gfx
// +build no_gfx
package mcwork
......@@ -88,7 +89,6 @@ func addOutNames(s gentest) string {
return ret + "\n"
}
func Test1(t *testing.T) {
for _, s := range set1 {
instring := addOutNames(s)
......@@ -106,7 +106,6 @@ func Test2(t *testing.T) {
}
}
func TestSetSuffix(t *testing.T) {
var tdata = []struct {
in, suffix, want string
......
......@@ -170,7 +170,7 @@ func plotxWrt(cprm *cprm, ndim int) error {
len_used := len(cprm.plotnstp)
xdata := make([]f64, ndim)
for i := 0; i < ndim; i++ {
xdata[i] = make([]float64, len_used, len_used)
xdata[i] = make([]float64, len_used)
}
var n int
for i := 0; i < len_used; i++ {
......
// feb 2022
// We are building a version without graphics
//go:build no_gfx
// +build no_gfx
// This should mirror the file realmain_gfx.go. It should also remain short.
package mcwork
import (
"fmt"
"io"
"os"
"fmt"
)
// realmain is the real main function. The program wants to read from a file,
......
* get a plot on the screen
* get two plots on the screen
* Give the plotting function a bytes.buffer to scribble into. If we are writing to a file, dump it there. If we are drawing pictures, return it.
See if we can keep this a bit clean and have a build tag that works without the gui, as well as an option that turns off the gui.
\ No newline at end of file
// feb 2022
// We are building a version without graphics
//go:build !no_gfx
// +build !no_gfx
package ui
......
// 17 Feb 2022
// Handle the output tab stuff
// Output tab / results
//go:build !no_gfx
// +build !no_gfx
// There should be three states.
// 1. initial, nothing happened yet
// 2. thinking
// 1. initial, nothing happened yet, but we do not need a signal for this
// 2. calculating, busy
// 3. show results
// 4. an error occurred
package ui
import (
"bytes"
"os"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/dialog"
"fyne.io/fyne/v2/storage"
"fyne.io/fyne/v2/widget"
)
// A message is sent to the output tab, telling us the current status
type status uint8
const (
initial status = iota
calculating
calculating status = iota
resultsReady
errorCalc
)
type workstatus struct {
fdata, xdata []byte
err error
status status
}
// showIniTab is just a card to be shown before we have any calculations
// done.
// 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))
}
// emptyContainer gets rid of any old contents
// If we get a message with an error, display it
func showErrTab(cntr *fyne.Container, err error) {
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)
......@@ -51,31 +65,84 @@ func showCalcTab(cntr *fyne.Container) {
cntr.Refresh()
}
// fwrt writes the file. It will be called by the filesave dialog
func fwrt(io fyne.URIWriteCloser, err error, d []byte, parent fyne.Window) {
if io == nil {
return // it was cancelled
}
if err != nil {
dialog.ShowError(err, parent)
return
}
defer io.Close()
if _, err := io.Write(d); err != nil {
dialog.ShowError(err, parent)
}
}
// innerWrite will be called by the button to save a file
func innerWrite(d []byte, parent fyne.Window) {
fwrt := func(io fyne.URIWriteCloser, err error) { fwrt(io, err, d, parent) }
t := dialog.NewFileSave(fwrt, parent)
t.SetFilter(storage.NewExtensionFileFilter([]string{"png", "PNG"}))
if cwd, err := os.Getwd(); err == nil {
if y, err := storage.ListerForURI(storage.NewFileURI(cwd)); err == nil {
t.SetLocation(y) // on error, just use default location
}
}
t.Show()
}
// leftbar sets up the buttons on the left
func leftbar(win fyne.Window, fdata, xdata []byte) *fyne.Container {
wrtFdata := func() { innerWrite(fdata, win) }
wrtXdata := func() { innerWrite(xdata, win) }
fdataBtn := widget.NewButton("save func\nvalue plot\nto file", wrtFdata)
xdataBtn := widget.NewButton("save\nX coord plot\nto file", wrtXdata)
return container.NewVBox(fdataBtn, xdataBtn)
}
// png2image takes the file data we have and puts it in a fyne image with
// a size we want. fname is used by fyne to recognise the file type.
func png2image(d []byte, fname string) *canvas.Image {
pictureSize := fyne.Size{Width: 500, Height: 250}
image := canvas.NewImageFromReader(bytes.NewReader(d), fname)
image.FillMode = canvas.ImageFillContain
image.SetMinSize(pictureSize)
return image
}
// showResultsTab show the two plots
func showResultsTab(cntr *fyne.Container, fdata, xdata []byte) {
func showResultsTab(cntr *fyne.Container, win fyne.Window, fdata, xdata []byte) {
emptyContainer(cntr)
pictureSize := fyne.Size{Width: 500, Height: 250}
fImage := canvas.NewImageFromReader(bytes.NewReader(fdata), "func.png")
fImage.FillMode = canvas.ImageFillContain
fImage.SetMinSize( pictureSize)
xImage := canvas.NewImageFromReader(bytes.NewReader(xdata), "xdata.png")
xImage.FillMode = canvas.ImageFillContain
xImage.SetMinSize( pictureSize)
content := container.NewGridWithRows(2, fImage, xImage)
cntr.Add(content)
fImage := png2image(fdata, "function.png")
xImage := png2image(xdata, "xdata.png")
right := container.NewGridWithRows(2, fImage, xImage)
left := leftbar(win, fdata, xdata)
box := container.NewHBox(left, right)
cntr.Add(box)
}
// outputTab is run as a background process. After showing the initial
// screen, it sits and waits on notifications. When it gets one,
// it redraws its tab.
func outputTab(chn chan workstatus, cntr *fyne.Container) {
func outputTab(chn chan workstatus, cntr *fyne.Container, form *widget.Form, win fyne.Window) {
showIniTab(cntr)
breaker()
for s := range chn {
switch s.status {
case calculating:
showCalcTab(cntr)
case resultsReady:
showResultsTab(cntr, s.fdata, s.xdata)
showResultsTab(cntr, win, s.fdata, s.xdata)
breaker()
form.Enable()
form.Refresh()
case errorCalc:
showErrTab(cntr, s.err)
form.Enable()
}
}
}
......@@ -9,11 +9,11 @@ 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"
......@@ -58,25 +58,27 @@ func validateXini(s string) error {
return err
}
// paramScreen sets up a screen full of parameters we can adjust.
// floatitem gives us a form item, but also a function to refresh the item.
// This gets put on the list of things to do from the reload function.
func floatItem(xAddr *float64, label string) (*widget.FormItem, func()) {
bind := binding.BindFloat(xAddr)
entry := widget.NewEntryWithData(binding.FloatToStringWithFormat(bind, "%10.2f"))
fitem := widget.NewFormItem(label, entry)
reloadfunc := func() { bind.Reload() }
return fitem, reloadfunc
}
// paramBox 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")
func paramBox(mcPrm *mcwork.McPrm, parent fyne.Window, chn chan workstatus) *widget.Form {
iniTmpItem, iniTmpReload := floatItem(&mcPrm.IniTmp, "initial temperature")
fnlTmpItem, fnlTmpReload := floatItem(&mcPrm.FnlTmp, "final temperature")
xDltaItem, xDltaReload := floatItem(&mcPrm.XDlta, "X delta")
xIniStr := binding.NewString()
xIniStr.Set(fslicestrng(mcPrm.XIni))
_ = xIniStr.Set(fslicestrng(mcPrm.XIni))
xIniEntry := widget.NewEntryWithData(xIniStr)
xIniEntry.OnChanged = func(s string) {
x, err := s2f32(s)
......@@ -87,51 +89,37 @@ func paramScreen(mcPrm *mcwork.McPrm) (*fyne.Container, func()) {
}
}
xIniEntry.Validator = validateXini
xIniLabel := widget.NewLabel("X ini")
xIniItem := widget.NewFormItem("initial X", xIniEntry)
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()
nStepItem := widget.NewFormItem("N steps", nStepEntry)
reloadPTab := func() {
iniTmpReload()
fnlTmpReload()
xDltaReload()
xIniEntry.SetText(fslicestrng(mcPrm.XIni))
nStepBnd.Reload()
}
return r, refreshPScreen
_ = nStepBnd.Reload()
}
rdfile := func() { rdwork(mcPrm, parent, reloadPTab) }
rdfileBtn := widget.NewButton("get file ", rdfile)
rdFileItem := widget.NewFormItem("read from a file", rdfileBtn)
// 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
r := widget.NewForm(
iniTmpItem, fnlTmpItem, xDltaItem, xIniItem, nStepItem, rdFileItem)
r.SubmitText = "start calculation"
r.OnSubmit = func() { startrun(chn, parent, r, mcPrm) }
return r
}
// rdwork is called after the file open dialog gets "OK"
func rdwork(mcPrm *mcwork.McPrm, parent fyne.Window, refreshme func()) error {
var e error
func rdwork(mcPrm *mcwork.McPrm, parent fyne.Window, reloadPTab func()) {
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
......@@ -139,12 +127,11 @@ func rdwork(mcPrm *mcwork.McPrm, parent fyne.Window, refreshme func()) error {
}
defer rd.Close()
if err := mcwork.RdPrm(rd, mcPrm); err != nil {
e = err
dialog.NewError(err, parent).Show()
return
} else {
fmt.Println("I think mcprm is", mcPrm)
refreshme()
dialog.NewInformation("OK", "read file, no errors", parent).Show()
reloadPTab()
return
}
}
......@@ -156,37 +143,30 @@ func rdwork(mcPrm *mcwork.McPrm, parent fyne.Window, refreshme func()) error {
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
}
// 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
// down the channel.
func mcWrap(chn chan workstatus, parent fyne.Window, mcPrm *mcwork.McPrm) {
chn <- workstatus{status: calculating}
if fdata, xdata, err := mcwork.DoRun(mcPrm); err != nil {
dialog.NewError(err, parent).Show()
close(chn)
return
chn <- workstatus{status: errorCalc, err: err}
} 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
// 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.
func inputTab(mcPrm *mcwork.McPrm, parent fyne.Window, chn chan workstatus) *widget.Form {
return paramBox(mcPrm, parent, chn)
}
// 25 Jan 2020
// Given a buffer or two with a plot picture, send it to the screen
// +build !no_gfx
package ui
import (
"bytes"
"fmt"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/dialog"
"fyne.io/fyne/v2/storage"
)
func tryfsave(uriw fyne.URIWriteCloser, err error, data []byte, parent fyne.Window) {
if err != nil {
dialog.ShowError(err, parent)
return
}
if uriw == nil {
return // cancel
}
if _, err := uriw.Write(data); err != nil {
fmt.Println("error writing")
}
if err := uriw.Close(); err != nil {
fmt.Println("error closing")
}
}
func fsave(parent fyne.Window, fdata []byte, suggested string) {
filter := storage.NewExtensionFileFilter([]string{"png", "PNG"})
ts := func(uriw fyne.URIWriteCloser, err error) {
tryfsave(uriw, err, fdata, parent)
}
t := dialog.NewFileSave(ts, parent)
t.SetFileName(suggested)
t.SetFilter(filter)
t.Show()
}
func topmenu(parent fyne.Window, fdata, xdata []byte) *fyne.Menu {
ds := func() { fsave(parent, fdata, "func_val.png") } // function values
xs := func() { fsave(parent, xdata, "x_trj.png") }
c := fyne.NewMenuItem("save func plot", ds)
d := fyne.NewMenuItem("save X trajectory plot", xs)
return fyne.NewMenu("actions", c, d)
}
func fileSaved(f fyne.URIWriteCloser, w fyne.Window) {
defer f.Close()
_, err := f.Write([]byte("Written by Fyne demo\n"))
if err != nil {
dialog.ShowError(err, w)
}
err = f.Close()
if err != nil {
dialog.ShowError(err, w)
}
fmt.Println("Saved to...", f.URI())
}
func nothing(...interface{}) {}
func breaker(...interface{}) {}
func scrnplt(fdata, xdata []byte) *fyne.Container {
fImage := canvas.NewImageFromReader(bytes.NewReader(fdata), "func.png")
fImage.FillMode = canvas.ImageFillContain
xImage := canvas.NewImageFromReader(bytes.NewReader(xdata), "xdata.png")
xImage.FillMode = canvas.ImageFillContain
content := container.NewGridWithRows(2, fImage, xImage)
// w.Resize(fyne.NewSize(500, 600))
// w.SetContent(content)
// w.ShowAndRun()
return content
}
// +build no_gfx
//
// If we do not have graphics
package ui
func Scrnplt(fdata []byte, xdata []byte) {
print ("No graphics")
}
// 25 Jan 2020
// Given a buffer or two with a plot picture, send it to the screen
//go:build !no_gfx
// +build !no_gfx
package ui
import (
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/app"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/widget"
......@@ -14,30 +14,28 @@ import (
"example.com/ackley_mc/mc_work"
)
func initIn () *widget.Card {
return widget.NewCard("input screen", "click on run to start a calc", nil)
}
func UiDoRun(mcPrm *mcwork.McPrm) error {
a := app.NewWithID("Monte Carlo")
w := a.NewWindow("Monte Carlo")
quitbutton := widget.NewButton ("quit .. click somwhere in here", a.Quit)
quitbutton := widget.NewButton("click somewhere in here to exit", a.Quit)
chn := make(chan workstatus)
inputTabCallback := func() (*fyne.Container) {
a, _ := inputTab(mcPrm, w, chn)
return a
}
t1 := container.NewTabItem("input tab", inputTabCallback())
cntrOut := fyne.NewContainer()
// inputTabCallback := func() *widget.Form { return (inputTab(mcPrm, w, chn))}
inputForm := inputTab(mcPrm, w, chn)
t1 := container.NewTabItem("input tab", inputForm)
cntrOut := container.NewWithoutLayout()
t2 := container.NewTabItem("output tab", cntrOut)
t3 := container.NewTabItem("quit me", quitbutton)
appTab := container.NewAppTabs(t1, t2, t3)
w.SetContent(appTab)
breaker()
go outputTab(chn, cntrOut)
go outputTab(chn, cntrOut, inputForm, w)
w.ShowAndRun()
close(chn)
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