Golang跨平臺GUI框架Fyne的使用教程詳解
Go 官方沒有提供標準的 GUI 框架,在 Go 實現的幾個 GUI 庫中,Fyne 算是最出色的,它有著簡潔的API、支持跨平臺能力,且高度可擴展。也就是說,Fyne 是可以用來開發(fā) App 的。
本文將嘗試介紹下 Fyne,希望對大家快速上手這個 GUI 框架有所幫助。我最近產生了不少想法,其中有些是對 GUI 有要求的,就想著折騰用 Go 實現,而不是用那些已經很流行和成熟的 GUI 框架。
在寫這篇文章時,順手搞了下它的中文版文檔,文檔查看 www.poloxue.com/gofyne,希望對想繼續(xù)深入這個框架的朋友有所幫助。
安裝 fyne
開始前,確保已成功安裝 Go,如果是 MacOS X 系統(tǒng),要確認安裝了 xcode。
如下使用 go get
命令安裝 Fyne。
$ mkdir hellofyne $ cd helloyfyne $ go mod init hellofyne $ go get fyne.io/fyne/v2@latest $ go install fyne.io/fyne/v2/cmd/fyne@latest
如果想立刻查看 fyne 提供的演示案例,通過命令檢查:
$ go run fyne.io/fyne/v2/cmd/fyne_demo@latest
看起來,這里面的案例還是不夠豐富的。
安裝工作到此就完成了。Fyne 對不同系統(tǒng)有不同依賴,如果安裝過程中遇到問題,細節(jié)可查看官方提供的安裝文檔。
創(chuàng)建第一個應用
由于 Go 簡潔的語法和 fyne 的設計,使用 fyne 創(chuàng)建一個 GUI 應用異常簡單。
以下是一個創(chuàng)建基礎窗口的例子:
package main import ( "fyne.io/fyne/v2/app" "fyne.io/fyne/v2/container" "fyne.io/fyne/v2/widget" ) func main() { a := app.New() w := a.NewWindow("Hello Fyne") w.SetContent(widget.NewLabel("Welcome to Fyne!")) w.ShowAndRun() }
這段代碼創(chuàng)建了一個包含標簽的窗口。
通過 app.New()
初始化一個 Fyne 應用實例。然后,創(chuàng)建一個標題為 "Hello Fyne" 的窗口,并設置內容為包含 "Welcome to Fyne!" 文本標簽。最后,通過w.ShowAndRun()
顯示窗口并啟動應用的事件循環(huán)。
fyne 中窗口的默認大小是其包含的內容決定,也可預置大小。
如在創(chuàng)建窗口后,提供 Resize
重新調整大小。
w.Resize(fyne.NewSize(100, 100))
還有,一個 app 下可以有多個窗口的,示例代碼:
btn := widget.NewButton("Create a widnow", func() { w2 := a.NewWindow("Window 02") w2.Resize(fyne.NewSize(200, 200)) w2.Show() }) w.SetContent(btn)
我們創(chuàng)建一個按鈕,它的點擊事件是創(chuàng)建一個新的窗口并顯示。
布局和控件
布局和控件是 GUI 應用程序設計中必不可少的兩類組件。Fyne 提供了多種布局管理器和標準的 UI 控件,支持創(chuàng)建更復雜的界面。
布局管理
Fyne 中的布局的實現位于 container
包中。它提供了多種不同布局方式安排窗口中的元素。最基本的布局方式有 HBox
水平布局和 VBox
垂直布局。
通過 HBox
創(chuàng)建水平布局的代碼如下:
w.SetContent(container.NewHBox(widget.NewButton("Left", func() { fmt.Println("Left button clicked") }), widget.NewButton("Right", func() { fmt.Println("Right button clicked") })))
顯示效果:
通過 VBox
創(chuàng)建垂直布局的例子。
w.SetContent(container.NewVBox(widget.NewButton("Top", func() { fmt.Println("Top button clicked") }), widget.NewButton("Bottom", func() { fmt.Println("Bottom button clicked") })))
顯示效果:
Fyne 除了基礎的水平(HBoxLayout
)和垂直(VBoxLayout
)布局,還提供了GridLayout
、FormLayout
甚至是混合布局 CombinedLayout
等高級布局方式。
如 GridLayout
可以將元素均勻地分布在網格中,而FormLayout
適用于創(chuàng)建表單,自動排列標簽和字段。這些靈活的布局選項支持創(chuàng)建更復雜和功能豐富的GUI界面。
官方文檔中的布局列表查看:Layout List。
更多控件
前面的演示案例中,用到了兩個控件:Label 和 Button,Fyne 還支持其他多種控件,它們都為于 widget
包中。
我嘗試在一份代碼中展示出來,如下是常見控件一覽:
// 標簽 Label label := widget.NewLabel("Label") // 按鈕 Button button := widget.NewButton("Button", func() {}) // 輸入框 Entry entry := widget.NewEntry() entry.SetPlaceHolder("Entry") // 復選框 Check check := widget.NewCheck("Check", func(bool) {}) // 單選框 Check radio := widget.NewRadioGroup([]string{"Option 1", "Option 2"}, func(string) {}) // 選擇框 selectEntry := widget.NewSelectEntry([]string{"Option A", "Option B"} // 進度條 progressBar := widget.NewProgressBar() // 滑塊 slider := widget.NewSlider(0, 100) // 組合框 combo := widget.NewSelect([]string{"Option A", "Option B", "Option C"}, func(string) {}) // 表單項 formItem := widget.NewFormItem("FormItem", widget.NewEntry()) form := widget.NewForm(formItem) // 手風琴 accordion := widget.NewAccordion(widget.NewAccordionItem("Accordion", widget.NewLabel("Content"))) // Tab 選擇 tabs := container.NewAppTabs( container.NewTabItem("Tab 1", widget.NewLabel("Content 1")), container.NewTabItem("Tab 2", widget.NewLabel("Content 2")), ) // 彈出對話框示例按鈕 dialogButton := widget.NewButton("Show Dialog", func() { dialog.ShowInformation("Dialog", "Dialog Content", w) }) // 滾動布局 content := container.NewVScroll(container.NewVBox( label, button, entry, check, radio, selectEntry, progressBar, slider, combo, form, accordion, tabs, dialogButton, )) w.SetContent(content)
演示效果:
Fyne 中的自定義
如果在實際項目中使用 Fyne,基本上是要使用 Fyne 的自定義能力。Fyne 提供了自定義控件、布局和主題等。
自定義控件
fyne 是支持實現自定義控件的,這涉及定義控件的繪制方法和布局邏輯。我們主要是實現兩個接口:fyne.Widget
和 fyne.WidgetRenderer
。
fyne.Widget
的定義如下所示:
type Widget interface { CanvasObject CreateRenderer() WidgetRenderer }
CreateRenderer
方法返回的就是 WiddgetRenderer
,用于定義控件渲染和布局的邏輯。
type WidgetRenderer interface { Destroy() Layout(Size) MinSize() Size Objects() []CanvasObject Refresh() }
這樣拆分的目標是為將了控件的邏輯和 UI 繪制分離開來,在 Widget
中專注于邏輯,而 WidgetRenderer
中專注于渲染布局。
假設實現一個類似 Label 的控件,類型定義:
type CustomLabel struct { widget.BaseWidget Text string }
它繼承了 wiget.BaseWidget
基本控件實現,Text
就是要 Label
顯示的文本。還要給給 CustomLabel
實現 CreateRenderer
方法。
定義 CustomLabel
創(chuàng)建函數:
func NewCustomLabel(text string) *CustomLabel { label := &CustomLabel{Text: text} label.ExtendBaseWidget(label) return label }
customWidgetRenderer
類型定義如下:
type customWidgetRenderer struct { text *canvas.Text // 使用canvas.Text來繪制文本 label *CustomLabel }
實現 CustomLabel
的 CreateRenderer
方法。
func (label *CustomLabel) CreateRenderer() fyne.WidgetRenderer { text := canvas.NewText(label.Text, theme.ForegroundColor()) text.Alignment = fyne.TextAlignCenter return &customLabelRenderer{ text: text, label: label, } }
構建 Renderer
變量,使用 canvas
創(chuàng)建 Text
文本框,為適配主題使用主題配置前景色。還有,設置文本居中顯示。
而 customLabelRenderer
要實現 WidgetRender
接口定義的所有方法。
func (r *customLabelRenderer) MinSize() fyne.Size { return r.text.MinSize() } func (r *customLabelRenderer) Layout(size fyne.Size) { r.text.Resize(size) } func (r *customLabelRenderer) Refresh() { r.text.Text = r.label.Text r.text.Color = theme.ForegroundColor() // 確保文本顏色更新 r.text.Refresh() } func (r *customLabelRenderer) BackgroundColor() color.Color { return theme.BackgroundColor() } func (r *customLabelRenderer) Objects() []fyne.CanvasObject { return []fyne.CanvasObject{r.text} } func (r *customLabelRenderer) Destroy() {}
在 main 函數中,嘗試使用這個控件。
a := app.New() w := a.NewWindow("Custom Label") w.SetContent(NewCustomLabel("Hello")) w.ShowAndRun()
顯示的效果和 Label 控件是類似的。
其他自定義
其他自定義能力,如 Layout、Theme,我就不展開介紹。如果有機會,寫點實際應用案例。如果基于案例介紹,會更有體悟吧。
還有,Fyne 的官方文檔寫的挺易讀的,可直接看它的文檔。
數據綁定
Fyne 從 v2.0.0 開始支持數據綁定。它讓控件和與數據實時連接,數據更改會自動反映在UI上,反之亦然。
控件為了支持數據綁定能力,一般會提供如 NewXXXWithData
的接口。
直接通過一個場景說明,目標是編輯輸入框的內容,可同時立刻顯示到 Label 上。
核心代碼如下所示:
// 創(chuàng)建一個字符串綁定 textBind := binding.NewString() // 創(chuàng)建一個 Entry,將其內容綁定到 textBind entry := widget.NewEntryWithData(textBind) // 創(chuàng)建一個 Label,也將其內容綁定到同一個 textBind label := widget.NewLabelWithData(textBind) // 使用容器放置 Entry 和 Label,以便它們都顯示在窗口中 content := container.NewVBox(entry, label)
如上的代碼,創(chuàng)建一個數據綁定類型的變量 textBind,并將其通過 NewWithData 將其綁定到兩個控件上。
顯示效果,如下所示:
嘗試讓前面自定義的 CustomLabel
支持數據綁定能力。只要創(chuàng)建一個 NewCustomLabelWithData
構造函數。
如下所示:
func NewCustomLabelWithData(data binding.String) *CustomLabel { label := &CustomLabel{} label.ExtendBaseWidget(label) data.AddListener(binding.NewDataListener(func() { text, _ := data.Get() label.Text = text label.Refresh() })) return label }
如上的代碼中,其實就是通過 data
這個數據綁定類型變量監(jiān)聽數據變化,監(jiān)聽到變化后,更新空間內容并立刻刷新控件顯示。
如上所示,我們這個自定義 Label 中的文本是居中顯示的。
數據綁定的核心是監(jiān)聽器模式(Observer pattern)。每個綁定對象內部維護了一個監(jiān)聽器列表,當數據變化時,這些監(jiān)聽器會被通知更新。
在 Fyne 中,通過 data.AddListner()
將 UI 組件與數據綁定對象綁定時,實際上是在數據對象上注冊了一個監(jiān)聽器,這個監(jiān)聽器會在數據變化時更新 UI 組件的狀態(tài)。
結語
Fyne 是簡單、強大和跨平臺的 GUI 工具,使得用 GO 開發(fā)現代 GUI 應用多了一個優(yōu)秀選擇。隨著對 Fyne 的深入,它能夠更加靈活地構建出符合需求的應用。
我喜歡用 Go 的原因,很重要的原因就是它的簡潔性,很容易看到本質的東西,但又無需理解太復雜的編程概念。
以上就是Golang跨平臺GUI框架Fyne的使用教程詳解的詳細內容,更多關于Go Fyne的資料請關注腳本之家其它相關文章!
相關文章
go mod 安裝依賴 unkown revision問題的解決方案
這篇文章主要介紹了go mod 安裝依賴 unkown revision問題的解決方案,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-05-05