Fyne 折騰手記:開發一個簡易桌面應用

文章首發于個人公眾號:「阿拉平平」

最近試了下用 Fyne 庫開發桌面應用,特此記錄和分享一下。本文演示環境為 Windows,Fyne 版本為 1.2.3。

簡介

Fyne 是一個 Go 語言開發的 UI 工具包。通過 Fyne,我們可以構建桌面和移動設備上運行的應用程序。

安裝

在安裝 Fyne 前,請確保 Go 版本在 1.12 以上。

$ go version go1.12.9 windows/amd64

安裝 Fyne 庫:

$ go get -u fyne.io/fyne

安裝完成后,用官方的例子測試下,代碼如下:

package main

import (
    "fyne.io/fyne/app"
    "fyne.io/fyne/widget"
)

func main() {
    app := app.New()

    w := app.NewWindow("Hello")
    w.SetContent(widget.NewVBox(
        widget.NewLabel("Hello Fyne!"),
        widget.NewButton("Quit", func() {
            app.Quit()
        }),
    ))

    w.ShowAndRun()
}

但是運行失敗了,提示找不到 gcc 的可執行文件:

# command-line-arguments
C:\tools\Go\pkg\tool\windows_amd64\link.exe: running gcc failed: exec: "gcc": executable file not found in %PATH%

要解決這個問題,需要下載安裝 MinGW,并將 bin 目錄添加到環境變量中。再運行就可以看到界面了:


接下來是 Fyne 的實踐部分,我打算用 Fyne 開發一個查詢 IP 歸屬地的桌面應用,讓我們一起動手試試吧。

界面開發

我們先完成界面部分的開發。

窗口

窗口可以根據自己喜好來調整,這里貼下我的代碼:

package main

import (
    "fyne.io/fyne"
    "fyne.io/fyne/app"
    "fyne.io/fyne/theme"
)

func main() {
    a := app.New()
    a.Settings().SetTheme(theme.LightTheme())
    w := a.NewWindow("Demo")
    w.Resize(fyne.NewSize(600, 500))
    w.ShowAndRun()
}

說明:

SetTheme():設置應用的主題,默認為 DarkTheme。
NewWindow():初始化窗口,可修改標題。
Resize():設置窗口尺寸。

窗口調整完成后,就可以開始添加組件了。

組件

Fyne 自帶了一些常用的組件,使用的時候需要導入 widget 包:

import "fyne.io/fyne/widget"

首先添加一個用來顯示數據的函數,這里我命名為 info,代碼如下:

func info() fyne.CanvasObject {
    // 核心功能還未編寫,先用假數據填充
    ip := widget.NewLabel("114.114.114.114")
    position := widget.NewLabel("中國江蘇省南京市")
    isp := widget.NewLabel("南京信風網絡科技有限公司GreatbitDNS服務器")
   
    // 初始化表單,用來顯示數據
    form := widget.NewForm(
        &widget.FormItem{Text: "IP地址:", Widget: ip},
        &widget.FormItem{Text: "所屬地:", Widget: position},
        &widget.FormItem{Text: "供應商:", Widget: isp},
    )
    // 分組
    info := widget.NewGroup("Info", form)
    // 返回一個支持滾動的容器
    return widget.NewScrollContainer(query)
}

接著添加一個用來查詢 IP 的函數,命名為 query,代碼如下:

func query() fyne.CanvasObject {
    // 初始化 ip 輸入框
    ip := widget.NewEntry()
    // 設置輸入框提示信息
    ip.SetPlaceHolder("Please input IP address")
   // 定義表單和觸發事件
    form := &widget.Form{
        OnSubmit: func() {
            fmt.Println("Form submitted")
            fmt.Println("IP Address:", ip.Text)
        },
    }
    // 將 ip 添加到表單中,之后從表單中就可以獲取到 ip
    form.Append("IP", ip)
    // 分組
    query := widget.NewGroup("Query", form)
    // 返回一個支持滾動的容器
    return widget.NewScrollContainer(query)
}

最后在主函數中添加 SetContent 方法,并使用 NewHBox 水平顯示組件:

w.SetContent(widget.NewHBox(
    info(),
    query(),
))

添加完成后,看下效果:


看到這效果,我當場裂開了。看來使用 NewHBox 效果并不理想,還需要調整下界面的布局。

布局

我們可以使用 Fyne 自帶的布局,先導入 layout 包:

import "fyne.io/fyne/layout"

再修改下 SetContent 的內容:

w.SetContent(fyne.NewContainerWithLayout(layout.NewGridLayoutWithColumns(2), info(), query())

簡單說明下:

NewGridLayoutWithColumns:返回一個 gridLayout 結構體,可以指定列數。如果需要垂直布局,可以替換成 NewGridLayoutWithRows。

NewContainerWithLayout:返回一個 Container 實例,使布局生效。

可以看到,布局已經沒問題了:


布局是沒問題了,但是看到這方塊字,我又裂開了。很明顯,目前 Fyne 窗口無法顯示中文,這個要怎么解決呢?

中文支持

首先,下載一個 TTF 格式的中文字庫,這里我找了個思源字體的字庫。需要注意的是,字庫的格式必須是 TTF 的,否則會報錯。

然后添加一個環境變量 FYNE_FONT,指定下載好的字庫文件:


再看下窗口效果,舒服了:


功能實現

在之前的章節中,我們已經完成了界面的開發,現在可以去實現功能了。

接口

之前的 IP 信息我們是寫死在代碼里的,現在改為調用接口的方式。

關于接口,我使用的是 ALAPI 提供的接口服務,這個開源項目提供了許多實用的數據接口,有興趣的童鞋可以移步到 Github。

請求地址:

http://v1.alapi.cn/api/ip?ip=114.114.114.114&format=json

返回數據如下:

{
    "code": 200,
    "msg": "success",
    "data": {
        "beginip": "114.114.114.114",
        "endip": "114.114.114.114",
        "pos": "中國江蘇省南京市",
        "isp": "南京信風網絡科技有限公司GreatbitDNS服務器",
        "location": {
            "lat": 32.05838,
            "lng": 118.79647
        },
        "rectangle": "",
        "ad_info": {
            "nation": "中國",
            "province": "江蘇省",
            "city": "南京市",
            "district": "",
            "adcode": 320100
        },
        "ip": "114.114.114.114"
    },
    "Author": {
        "name": "Alone88",
        "desc": "由Alone88提供的免費API 服務,官方文檔:www.alapi.cn"
    }
}

接口有了,接下來我們調整下之前的代碼。

代碼調整

定義全局變量與結構體:

var ip = widget.NewLabel("")
var position = widget.NewLabel("")
var isp = widget.NewLabel("")

type IpInfo struct {
    Code int `json:"code"`
    Message string `json:"msg"`
    Data `json:"data"`
}

type Data struct {
    IP string `json:"ip"`
    Position string `json:"pos"`
    Isp string `json:"isp"`
}

說明:

ip,position 和 isp 這里定義成全局變量,是因為之后這部分信息在點擊提交按鈕后會發生變更。

新增 GetIpInfo 函數用來獲取接口數據:

func GetIpInfo(ip string) string {
    if len(ip) == 0 {
        return ""
    }

    url := fmt.Sprintf("http://v1.alapi.cn/api/ip?ip=%s&format=json", ip)

    resp, err :=   http.Get(url)
    if err != nil {
        // handle error
    }

    defer resp.Body.Close()
    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        // handle error
    }

    return string(body)
}

調整 info 函數:

func info(response string) fyne.CanvasObject {
    var i IpInfo
    json.Unmarshal([]byte(response),&i)

    screen := widget.NewForm(
        &widget.FormItem{Text: "IP地址:", Widget: ip},
        &widget.FormItem{Text: "所屬地:", Widget: position},
        &widget.FormItem{Text: "供應商:", Widget: isp},
    )

    ip.SetText(i.IP)
    position.SetText(i.Position)
    isp.SetText(i.Isp)

    info := widget.NewGroup("Info", screen)
    return widget.NewScrollContainer(info)
}

說明:

info() 接受 GetIpInfo 的返回值并將數據反序列化,通過 SetText 方法更新信息。

調整 query 函數:

func query() fyne.CanvasObject {
    ip := widget.NewEntry()
    ip.SetPlaceHolder("Please input IP address")

    form := &widget.Form{
        OnSubmit: func() {
            info(GetIpInfo(ip.Text))
        },
    }

    form.Append("IP", ip)
    query := widget.NewGroup("Query", form)
    return widget.NewScrollContainer(query)
}

說明:

OnSubmit 中調用 info(),即點擊 Submit 后獲取并顯示數據。

調整 main 函數:

w.SetContent(fyne.NewContainerWithLayout(layout.NewGridLayoutWithColumns(2), info(GetIpInfo("")), query()))

輸入 IP 后點擊 Submit,就可以查詢到相關信息了:


demo3.png

編譯打包

下載打包工具:

$ go get fyne.io/fyne/cmd/fyne

在項目目錄中放入圖標,執行以下命令:

$ fyne package -icon icon.png

寫在后面

文中的例子寫的比較簡單,有許多不完善的地方,有興趣的小伙伴可以去 GitHub 上看看,在上面可以找到更多的例子和文檔。

如果有需要 MinGW for Windows x64 和中文字體的小伙伴,可以去微信公眾號后臺回復 fyne 獲取。

References

[1] Fyne: https://github.com/fyne-io/fyne
[2] 思源字體:https://github.com/junmer/source-han-serif-ttf
[3] Github: https://github.com/anhao/ALAPI
[4] 例子: https://github.com/fyne-io/examples/
[5] 文檔:: https://apps.fyne.io/

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容