首页 > 解决方案 > Go and Fyne - 需要对话框响应并启用/禁用提交按钮

问题描述

刚开始研究 Go 和 Fyne。使用从示例中获得的信息编写测试 CRUD 程序后,我需要解决以下问题:

  1. 我需要一个初始对话框或类似的,以便允许选择现有记录或“新”来指示新记录。我现有的对话框不能充分处理这个问题。

  2. 如果数据没有更改,我需要能够启用/禁用提交按钮以允许或禁止更新。

  3. 该程序编译没有错误,但是“go vet”和 VsCode 指示(例如 for line):

     {"Given Names:", arwWidgets[I_NDX_GIVEN]},
 go vet shows:
 ERROR:  "unknown field 'Key' in struct literal of type widget.FormItem",
 however, if I include a key, compiler fails.
  1. 作为 Go 和 Fyne 的新手,可能存在根本性错误。

测试程序如下:

package main
// Dependency: local directory "data".
import (
    "errors"
    "io/ioutil"
    "log"
    "os"
    "strings"

    "fyne.io/fyne"
    "fyne.io/fyne/app"
    "fyne.io/fyne/dialog"
    "fyne.io/fyne/layout"
    "fyne.io/fyne/widget"
)

const BY_SEP byte = byte('|')
const I_ARRAY_LEN int = 10
const I_NDX_GIVEN int = 0
const I_NDX_FAMILY int = 1
const I_NDX_TITLE int = 2
const I_NDX_ADDR1 int = 3
const I_NDX_ADDR2 int = 4
const I_NDX_ADDR3 int = 5
const I_NDX_STATE int = 6
const I_NDX_ZIP int = 7
const I_NDX_TELE1 int = 8
const I_NDX_TELE2 int = 9

var _arsOldData [I_ARRAY_LEN]string
var _arsNewData [I_ARRAY_LEN]string
var _tfDataHasChanged bool
var _sKey string
var _arwEntryWidgets [I_ARRAY_LEN]*widget.Entry
var _wApp fyne.App
var _wWindow fyne.Window
var _wModal widget.PopUp
var _wFormKeyEntry widget.Form

//-------------------------------------------------------------------------------

func main() {
    log.Println("In fytest01")
    _, err := os.Stat("./data/")
    if err != nil {
        log.Println("You need to create directory: 'data'")
        os.Exit(1)
    }
    //fnReadData()
    _wApp := app.New()
    _wWindow := _wApp.NewWindow("Customer Details")
    //_wFormKeyEntry := fnCreateKeyForm()
    wFormMain := fnCreateMainForm()
    //_wModal := widget.NewModalPopUp(_wFormKeyEntry, _wWindow.Canvas())
    _wWindow.Resize(fyne.NewSize(500, 400))
    _wWindow.CenterOnScreen()
    _wWindow.SetContent(widget.NewVBox(
        wFormMain,
        widget.NewGroup("",
            fyne.NewContainerWithLayout(layout.NewGridLayout(2),
                widget.NewButton("Exit", func() {
                    _wApp.Quit()
                }),
                widget.NewButton("Submit", func() {
                    log.Println("Submit button pressed")
                    err := fnUpdateData()
                    if err == nil {
                        dialog.ShowInformation("Information", "Data was updated", _wWindow)
                        wEntryForKey := widget.NewEntry()
                        wEntryForKey.SetPlaceHolder("Enter Key (4) : ")
                        wEntryForKey.OnChanged = func(sKey string) {
                            log.Println("Entered", sKey)
                            if len(sKey) == 4 {
                                _sKey = sKey
                            }
                            if len(_sKey) == 4 {
                                dialog.ShowInformation("Information", "Key "+_sKey+" has been entered - press OK", _wWindow)
                                fnReadData()
                                fnRefreshWidgetData()
                            }
                        }
                        dialog.ShowCustom("Enter Customer Key", "OK", wEntryForKey, _wWindow)
                    } else {
                        dialog.ShowError(err, _wWindow)
                    }
                }),
            ))),
    )
    //fnShowKeyEntryDialog()

    wEntryForKey := widget.NewEntry()
    wEntryForKey.SetPlaceHolder("Enter Key (4) : ")
    wEntryForKey.OnChanged = func(sKey string) {
        log.Println("Entered", sKey)
        if len(sKey) == 4 {
            _sKey = sKey
        }
        if len(_sKey) == 4 {
            dialog.ShowInformation("Information", "Key "+_sKey+" has been entered - press OK", _wWindow)
            fnReadData()
            fnRefreshWidgetData()
        }
    }
    dialog.ShowCustom("Enter Customer Key", "OK", wEntryForKey, _wWindow)
    _wWindow.ShowAndRun()
    //_wModal.Show()
}

//-------------------------------------------------------------------------------

func fnCreateAllEntryWidgets() {
    _arwEntryWidgets[I_NDX_GIVEN] = fnCreateSingleEntryWidget(I_NDX_GIVEN)
    _arwEntryWidgets[I_NDX_FAMILY] = fnCreateSingleEntryWidget(I_NDX_FAMILY)
    _arwEntryWidgets[I_NDX_TITLE] = fnCreateSingleEntryWidget(I_NDX_TITLE)
    _arwEntryWidgets[I_NDX_ADDR1] = fnCreateSingleEntryWidget(I_NDX_ADDR1)
    _arwEntryWidgets[I_NDX_ADDR2] = fnCreateSingleEntryWidget(I_NDX_ADDR2)
    _arwEntryWidgets[I_NDX_ADDR3] = fnCreateSingleEntryWidget(I_NDX_ADDR3)
    _arwEntryWidgets[I_NDX_STATE] = fnCreateSingleEntryWidget(I_NDX_STATE)
    _arwEntryWidgets[I_NDX_ZIP] = fnCreateSingleEntryWidget(I_NDX_ZIP)
    _arwEntryWidgets[I_NDX_TELE1] = fnCreateSingleEntryWidget(I_NDX_TELE1)
    _arwEntryWidgets[I_NDX_TELE2] = fnCreateSingleEntryWidget(I_NDX_TELE2)
}

//-------------------------------------------------------------------------------

func fnCreateSingleEntryWidget(iNdxData int) *widget.Entry {
    wEntry := widget.NewEntry()
    wEntry.SetText(_arsOldData[iNdxData])
    wEntry.OnChanged = func(sText string) {
        _arsNewData[iNdxData] = sText
        fnCheckIfDataHasChanged()
    }
    return wEntry
}

//-------------------------------------------------------------------------------

func fnCreateFormFields(arwWidgets [I_ARRAY_LEN]*widget.Entry) []*widget.FormItem {
    return []*widget.FormItem{
        {"Given Names:", arwWidgets[I_NDX_GIVEN]},
        {"Family Name: ", arwWidgets[I_NDX_FAMILY]},
        {"Title: ", arwWidgets[I_NDX_TITLE]},
        {"Address Ln 1: ", arwWidgets[I_NDX_ADDR1]},
        {"   ''   Ln 2: ", arwWidgets[I_NDX_ADDR2]},
        {"   ''   Ln 3: ", arwWidgets[I_NDX_ADDR3]},
        {"   ''   State ", arwWidgets[I_NDX_STATE]},
        {"   ''   Zip: ", arwWidgets[I_NDX_ZIP]},
        {"Telephone 1: ", arwWidgets[I_NDX_TELE1]},
        {"Telephone 2: ", arwWidgets[I_NDX_TELE2]},
    }
}

//-------------------------------------------------------------------------------

func fnCheckIfDataHasChanged() {
    var tfChanged bool = false
    for iNdxData := 0; !tfChanged && iNdxData < len(_arsOldData); iNdxData++ {
        tfChanged = (_arsNewData[iNdxData] != _arsOldData[iNdxData])
    }
    if tfChanged != _tfDataHasChanged {
        _tfDataHasChanged = tfChanged
        if tfChanged {
            //  COULD NOT CREATE _wBtnSubmitMain AS A GLOBAL VARIABLE.
            //_wBtnSubmitMain.Show()
            //_wBtnSubmitMain.Enable()
        } else {
            //_wBtnSubmitMain.Disable()
            //_wBtnSubmitMain.Hide()
        }
    }
}

//-------------------------------------------------------------------------------

func fnReadData() {
    _tfDataHasChanged = false
    log.Println("fnReadData: Key = " + _sKey)
    var sData string
    if len(_sKey) > 0 {
        arbData, _ := ioutil.ReadFile("./data/" + _sKey)
        if arbData != nil {
            sData = string(arbData)
        }
    }
    log.Println("fnReadData: sData = " + sData)

    /* CLEAR OLD DATA */
    for iNdxData := 0; iNdxData < I_ARRAY_LEN; iNdxData++ {
        _arsOldData[iNdxData] = ""
    }

    /* POPULATE DATA IF ANY */
    var iNdx1 int = 0
    var iNdxData int = 0
    var iLen int = len(sData)
    for iNdx2 := 0; iNdx2 < len(sData); iNdx2++ {
        if sData[iNdx2] == BY_SEP {
            _arsOldData[iNdxData] = sData[iNdx1:iNdx2]
            iNdx1 = iNdx2 + 1
            iNdxData++
        } else if iNdx2 == (iLen - 1) {
            _arsOldData[iNdxData] = sData[iNdx1 : iNdx2+1]
        }
    }
    for iNdx := 0; iNdx < I_ARRAY_LEN; iNdx++ {
        _arsNewData[iNdx] = _arsOldData[iNdx]
    }
}

//-------------------------------------------------------------------------------

func fnUpdateData() error {
    if !_tfDataHasChanged {
        return errors.New("Data has not changed")
    }

    if len(_sKey) < 1 {
        _sKey = "9999"
        //dialog.ShowInformation("Information", "Default key of 9999 was used", _wWindow)  // CAUSES ERROR
    }

    var sNewData string = ""
    for iNdxData, sVal := range _arsNewData {
        if strings.Index(sVal, "|") >= 0 {
            sVal = strings.ReplaceAll(sVal, "|", ":")
        }
        if iNdxData != I_ARRAY_LEN-1 {
            sNewData += sVal + string(BY_SEP)
        } else {
            sNewData += sVal
        }
    }
    log.Println("New Data = " + sNewData)
    var err error = ioutil.WriteFile("./data/"+_sKey, []byte(sNewData), 0644)
    if err == nil {
        for iNdxData := 0; iNdxData < len(_arsNewData); iNdxData++ {
            _arsOldData[iNdxData] = _arsNewData[iNdxData]
        }
        fnCheckIfDataHasChanged()
    }
    return err
}

//-------------------------------------------------------------------------------

func fnCreateKeyForm() *widget.Form {
    wEntryKey := widget.NewEntry()
    return &widget.Form{
        Items: []*widget.FormItem{
            {"Key:", wEntryKey}},
        OnSubmit: func() {
            _sKey = wEntryKey.Text
            log.Println("Key = " + _sKey)
            fnReadData()
            log.Println("Data has been read")
            fnRefreshWidgetData()
            _wFormKeyEntry.Hide()
            //_wModal.Hide()
        },
    }
}

//-------------------------------------------------------------------------------

//func fnCreateMainWindow() {
//  log.Println("Creating Main Window   Form")
//  wFormMain := fnCreateMainForm()
//  log.Println("Creating Main Window   Window")
//  _wWindow.SetContent( //widget.NewVBox(
//      wFormMain,
//  )
//}

//-------------------------------------------------------------------------------

func fnCreateMainForm() *widget.Form {
    log.Println("Create form1 widgets")
    fnCreateAllEntryWidgets()
    log.Println("Create form1 FormFields")
    arwFormFields := fnCreateFormFields(_arwEntryWidgets)
    log.Println("Creating Form1")
    return &widget.Form{
        Items: arwFormFields,
    }
}

//-------------------------------------------------------------------------------

func fnRefreshWidgetData() {
    for iNdx := 0; iNdx < I_ARRAY_LEN; iNdx++ {
        _arwEntryWidgets[iNdx].SetText(_arsNewData[iNdx])
    }
}

标签: godialogfyne

解决方案


  1. 您可以在开始窗口中添加一个“新建”按钮,填充默认值然后打开主窗口。那有意义吗?

  2. 您可以通过创建提交按钮并将其直接传递给SetContent. 您需要将它保存在一个变量中,以便您可以在某个时候调用Disable()它。

submitButton := widget.NewButton("Submit", func() {
    log.Println("Submit button pressed")
...
})
_wWindow.SetContent(widget.NewVBox(
    wFormMain,
    widget.NewGroup("",
        fyne.NewContainerWithLayout(layout.NewGridLayout(2),
            widget.NewButton("Exit", func() {
                _wApp.Quit()
            }),
            submitButton,
        ))),
)

...
if dataChanged {
    submitButton.Enable()
} else {
    submitButton.Disable()
}
  1. 我认为该go vet消息是说您应该命名结构文字中的所有字段(尽管我使用 Go 1.13 收到了不同的消息)。Go 允许您按顺序或按名称添加值 -go vet告诉您按名称更安全(例如,如果字段被移动或插入到FormItem结构中,那么您的代码将被破坏)。

IE:

    return []*widget.FormItem{
        { Text: "Given Names:", Widget: arwWidgets[I_NDX_GIVEN]},
...
  1. 它还可以简化将所有全局变量放入结构的事情,然后您可以在结构上使用“方法”。(有几个原因可以避免使用您可能已经知道的全局变量。我很欣赏这可能是从一个简单的测试开始的,但它可能是重构的好时机。)

推荐阅读