我想將 GUI 添加到我用 Go 撰寫的命令列應用程式中,但我遇到了 fyne 和回圈依賴項的問題。
考慮這個簡單的例子來說明我面臨的問題:假設一個按鈕在我的模型類上觸發了一個耗時的方法(比如獲取資料左右),我希望在任務完成時更新視圖。
我首先實作了一個非常幼稚且完全不解耦的解決方案,這顯然會遇到 go 編譯器引發的回圈依賴錯誤。考慮以下代碼:
main.go
package main
import (
"my-gui/gui"
)
func main() {
gui.Init()
}
gui/gui.go
package gui
import (
"my-gui/model"
//[...] fyne imports
)
var counterLabel *widget.Label
func Init() {
myApp := app.New()
myWindow := myApp.NewWindow("Test")
counterLabel = widget.NewLabel("0")
counterButton := widget.NewButton("Increment", func() {
go model.DoTimeConsumingStuff()
})
content := container.NewVBox(counterLabel, counterButton)
myWindow.SetContent(content)
myWindow.ShowAndRun()
}
func UpdateCounterLabel(value int) {
if counterLabel != nil {
counterLabel.SetText(strconv.Itoa(value))
}
}
模型/模型.go
package model
import (
"my-gui/gui" // <-- this dependency is where it obviously hits the fan
//[...]
)
var counter = 0
func DoTimeConsumingStuff() {
time.Sleep(1 * time.Second)
counter
fmt.Println("Counter: " strconv.Itoa(counter))
gui.UpdateCounterLabel(counter)
}
所以我想知道如何正確解耦這個簡單的應用程式以使其正常作業。我想到了什么:
use fyne data binding: That should work for simple stuff such as the label text in the example above. But what if I have to update more in a very custom way according to a model's state. Say I'd have to update a button's enabled state based on a model's condition. How can this be bound to data? Is that possible at all?
use interfaces as in the standard MVC design pattern: I tried this as well but couldn't really get my head around it. I created a separate module that would provide an interface which could then be imported by the model class. I would then register a view that (implicitly) implements that interface with the model. But I couldn't get it to work. I assume that my understanding of go interfaces isn't really sufficient at this point.
short polling the model: that's just meh and certainly not what the developers of Go and/or fyne intended :-)
Can anyone please point me to an idiomatic solution for this problem? I'm probably missing something very, very basic here...
uj5u.com熱心網友回復:
回傳值
您可以回傳該值。
func DoTimeConsumingStuff() int {
time.Sleep(1 * time.Second)
counter
return counter
}
然后在按鈕單擊時生成一個匿名 goroutine,以便不阻塞 UI。
counterButton := widget.NewButton("Increment", func() {
go func() {
counter := model.DoTimeConsumingStuff(counterChan)
UpdateCounterLabel(counter)
}()
})
打回來
您可以將該UpdateCounterLabel函式傳遞給您的模型函式,即回呼。
func DoTimeConsumingStuff(callback func(int)) {
time.Sleep(1 * time.Second)
counter
callback(counter)
}
counterButton := widget.NewButton("Increment", func() {
go model.DoTimeConsumingStuff(UpdateCounterLabel)
})
渠道
也許您也可以將通道傳遞給您的模型函式。但是使用上述方法,這似乎不是必需的。潛在地,如果您有多個計數器值來。
func DoTimeConsumingStuff(counterChan chan int) {
for i := 0; i < 10; i {
time.Sleep(1 * time.Second)
counter
counterChan <- counter
}
close(counterChan)
}
然后在 GUI 中,您可以從通道接收,再次在 goroutine 中以不阻塞 UI。
counterButton := widget.NewButton("Increment", func() {
go func() {
counterChan := make(chan int)
go model.DoTimeConsumingStuff(counterChan)
for counter := range counterChan {
UpdateCounterLabel(counter)
}
}()
})
當然,您也可以再次使用在每次迭代時呼叫的回呼。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/447730.html
標籤:go model-view-controller circular-dependency fyne
下一篇:IGraphServiceUsersCollectionRequest(GraphServiceClient)的部門非空過濾器
