Go中GUI库fyne

Go中GUI库fyne学习

参考

  • https://pkg.go.dev/fyne.io/fyne/v2#readme-documentation
  • https://github.com/darjun/go-daily-lib
  • https://fyne.io/

Fyne包结构划分

fyne将功能划分到不同的子包中:

  • fyne.io/fyne:提供所有fyne应用程序代码共用的基础定义,包括数据类型和接口;
  • fyne.io/fyne/app:提供创建应用程序的 API;
  • fyne.io/fyne/canvas:提供Fyne使用的绘制 API;
  • fyne.io/fyne/dialog:提供对话框组件;
  • fyne.io/fyne/layout:提供多种界面布局;
  • fyne.io/fyne/widget:提供多种组件,fyne所有的窗体控件和交互元素都在这个子包中。

使用-Demo

快速使用

package main

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

func test() {
	a := app.New()
	w := a.NewWindow("Hello")
	hello := widget.NewLabel("Hello Fyne!")
	w.SetContent(container.NewVBox(
		hello,
		widget.NewButton("Hi!", func() {
			hello.SetText("Welcome:")
		}),
	))

	w.ShowAndRun()
}

func main() {
	test()
}

Go中GUI库fyne_第1张图片

效果,当你点击Hi!点击按钮时上面的文字变为了Welcome:

Canvas

package main

import (
	"image/color"
	"math/rand"

	"fyne.io/fyne"
	"fyne.io/fyne/app"
	"fyne.io/fyne/canvas"
	"fyne.io/fyne/layout"
	"fyne.io/fyne/theme"
)

func test() {
	a := app.New()
	w := a.NewWindow("Canvas")
	rect := canvas.NewRectangle(color.White)

	text := canvas.NewText("Hello Text", color.Black)
	text.Alignment = fyne.TextAlignTrailing
	text.TextStyle = fyne.TextStyle{Italic: true}

	line := canvas.NewLine(color.Black)
	line.StrokeWidth = 4
	
    // 绘制LoGo图
	image := canvas.NewImageFromResource(theme.FyneLogo())
	image.FillMode = canvas.ImageFillOriginal

	// 像素图
	raster := canvas.NewRasterWithPixels(
		func(_, _, w, h int) color.Color {
			return color.RGBA{uint8(rand.Intn(255)),
				uint8(rand.Intn(255)),
				uint8(rand.Intn(255)), 0xff}
		},
	)

	gradient := canvas.NewHorizontalGradient(color.White, color.Transparent)

	container := fyne.NewContainerWithLayout(
		layout.NewGridWrapLayout(fyne.NewSize(150, 150)),
		rect, text, line, image, raster, gradient,
	)

	w.SetContent(container)
	w.ShowAndRun()
}

func main() {
	test()
}

效果图

Go中GUI库fyne_第2张图片

Widget

窗体控件是一个Fyne应用程序的主要组成部分。它们能适配当前的主题,并且处理与用户的交互。

Label

Label(标签)是一个简单的空间,用于字符串的展示。

package main

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

func test() {
	a := app.New()
	w := a.NewWindow("Label")

	l1 := widget.NewLabel("Name")
    // 可以识别转义字符
	l2 := widget.NewLabel("Yu\nhandsome")

	container := fyne.NewContainerWithLayout(layout.NewVBoxLayout(), l1, l2)
	w.SetContent(container)
	w.Resize(fyne.NewSize(150, 150))
	w.ShowAndRun()
}

func main() {
	test()
}

效果

Go中GUI库fyne_第3张图片

Button

package main

import (
	"fmt"

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

func test() {
	a := app.New()
	w := a.NewWindow("Button")

	btn1 := widget.NewButton("text button", func() {
		fmt.Println("btn1 button clicked")
	})
	btn2 := widget.NewButtonWithIcon("text button", theme.HomeIcon(), func() {
		fmt.Println("Home")
	})

	container := fyne.NewContainerWithLayout(layout.NewVBoxLayout(), btn1, btn2)
	w.SetContent(container)
	w.Resize(fyne.NewSize(150, 150))
	w.ShowAndRun()
}

func main() {
	test()
}

在这个案例中我们创建两个按钮,两种共同之处是都需要传入按钮名称和按钮点击触发的执行函数。值得一提的是我们还可以加载外部的图标进行装饰。

效果

Go中GUI库fyne_第4张图片

Box

package main

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

func test() {
	a := app.New()
	w := a.NewWindow("Button")

	content := widget.NewVBox(
		widget.NewLabel("The top row of VBox"),
		widget.NewHBox(
			widget.NewLabel("Label 1"),
			widget.NewLabel("Label 2"),
		),
	)

	content.Append(widget.NewButton("Append", func() {
		content.Append(widget.NewLabel("Append"))
	}))

	w.SetContent(content)
	w.Resize(fyne.NewSize(150, 150))
	w.ShowAndRun()
}

func main() {
	test()
}

盒子控件Box,其实就是一个容器类似于html中的div。不同的是在盒子创建时我们可选择widget.NewHBox()或widget.NewVBox()创建内部不同布局的盒子。在创建好Box盒子后,我们

效果

Go中GUI库fyne_第5张图片

Entry

输入框(Entry)控件用于给用户输入简单的文本内容。我们可以调用widget.NewEntry()即可创建一个输入框控件。同时我们可以通过访问其Text字段来获得输入框中的内容。注册OnChange回调函数。每当内容有修改时,OnChange就会被调用。SetPlaceHolder()用来设置占位字符,设置字段MultilIne让输入框接受多行文本。此外,NewPasswordEntry()可创建一个密码输入框,输入的文本不会以明文显示。

package main

import (
	"fmt"

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

func test() {
	a := app.New()
	w := a.NewWindow("Entry")

	input := widget.NewEntry()
	input.SetPlaceHolder("please enter name")
	input.OnChanged = func(content string) {
		fmt.Println("name", content)
	}

	passInput := widget.NewPasswordEntry()
	passInput.SetPlaceHolder("please enter password")

	line1 := widget.NewVBox(widget.NewLabel("Name"), layout.NewSpacer(), input)
	line2 := widget.NewVBox(widget.NewLabel("Password"), layout.NewSpacer(), passInput)

	loginBtn := widget.NewButton("Login", func() {
		fmt.Printf("name: %s, password: %s\n Login in", input.Text, passInput.Text)
	})

	content := widget.NewVBox(line1, line2, loginBtn)
	w.SetContent(content)
	w.Resize(fyne.NewSize(150, 150))
	w.ShowAndRun()
}

func main() {
	test()
}

效果

Go中GUI库fyne_第6张图片

Checkbox/Radio/Select

这里的话主要是关于三种选择框的使用:

  • CheckBox 是简单的选择框
  • Radio 是单选框,只能选择其中的一个
  • Select是下拉框,当选项非常多时适合使用。
package main

import (
	"fmt"

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

func test() {
	a := app.New()
	w := a.NewWindow("Entry")

	input := widget.NewEntry()
	input.SetPlaceHolder("please enter name")
	input.OnChanged = func(content string) {
		fmt.Println("name", content)
	}

	passInput := widget.NewPasswordEntry()
	passInput.SetPlaceHolder("please enter password")

	line1 := widget.NewVBox(widget.NewLabel("Name"), layout.NewSpacer(), input)
	line2 := widget.NewVBox(widget.NewLabel("Password"), layout.NewSpacer(), passInput)

	loginBtn := widget.NewButton("Login", func() {
		fmt.Printf("name: %s, password: %s\n Login in", input.Text, passInput.Text)
	})

	sexRadio := widget.NewRadio([]string{"male", "female"}, func(value string) {
		fmt.Printf("sex %s\n", value)
	})
	sexBox := widget.NewHBox(widget.NewLabel("Sex"), sexRadio)

	provinceSelect := widget.NewSelect([]string{"beijing", "zhejiang", "shanghai"}, func(value string) {
		fmt.Println("province:", value)
	})
	provinceBox := widget.NewHBox(widget.NewLabel("Province"), layout.NewSpacer(), provinceSelect)

	content := widget.NewVBox(line1, line2, sexBox, provinceBox, loginBtn)
	w.SetContent(content)
	w.Resize(fyne.NewSize(150, 150))
	w.ShowAndRun()
}

func main() {
	test()
}

效果

Go中GUI库fyne_第7张图片

Form

表单控件Form用于对很多Label和输入控件进行布局。如果指定了OnSubmit或OnCancek函数,表单控件

package main

import (
	"fmt"

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

func test() {
	a := app.New()
	w := a.NewWindow("Entry")
	input := widget.NewEntry()
	passWord := widget.NewPasswordEntry()
	form := widget.NewForm(
		&widget.FormItem{Text: "Name", Widget: input},
		&widget.FormItem{Text: "password", Widget: passWord},
	)
	form.OnSubmit = func() {
		fmt.Printf("Name: %s\nPassword: %s\n", input.Text, passWord.Text)
	}
	form.OnCancel = func() {
		fmt.Printf("Login end")
	}

	w.SetContent(form)
	w.Resize(fyne.NewSize(150, 150))
	w.ShowAndRun()
}

func main() {
	test()
}

效果

Go中GUI库fyne_第8张图片

ProgressBar

进度条(ProgressBar)用来表示任务的进度。例如文件下载的进度。创建当大widget.NewProgressBar(),默认最小值0.0,最大值为1.1,可通过Min/Max字段,调用SetValue()方法来控制进度,还有一种进度条是循环动画,表示任务在进行中。

package main

import (
	"time"

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

func test() {
	a := app.New()
	w := a.NewWindow("Entry")
	bar1 := widget.NewProgressBar()
	bar1.Min = 0
	bar1.Max = 100
	bar2 := widget.NewProgressBarInfinite()
	go func() {
		for i := 0; i <= 100; i++ {
			time.Sleep(time.Millisecond * 500)
			bar1.SetValue(float64(i))
		}
	}()

	content := widget.NewVBox(bar1, bar2)
	w.SetContent(content)
	w.Resize(fyne.NewSize(150, 150))
	w.ShowAndRun()
}

func main() {
	test()
}

效果

Go中GUI库fyne_第9张图片

TabContainer

标签容器(TabContainer)允许用户在不同的内容面板之间切换。标签可以是文本或图标。我们通过不同的方法创建不同位置的标签:

  • TabLocationBottom:显示在底部;
  • TabLocationLeading:显示在顶部左边;
  • TabLocationTrailing:显示在顶部右边。
package main

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

func test() {
	a := app.New()
	w := a.NewWindow("TabContainer")
	label1 := widget.NewLabel("label1")
	label11 := widget.NewLabel("label11")
	label11.Hide()
	label2 := widget.NewLabel("label2")

	showCheck := widget.NewCheck("Hidden?", func(val bool) {
		if val {
			label11.Hide()
		} else {
			label11.Show()
		}
	})
	b1 := widget.NewHBox(
		label1,
		label11,
		showCheck,
	)
	b2 := widget.NewHBox(
		label2,
	)
	tabs := widget.NewTabContainer(
		widget.NewTabItem("11", b1),
		widget.NewTabItem("22", b2),
	)

	w.SetContent(tabs)
	w.Resize(fyne.NewSize(150, 150))
	w.ShowAndRun()
}

func main() {
	test()
}

效果

Go中GUI库fyne_第10张图片

Toolbar

package main

import (
	"fmt"

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

func test() {
	a := app.New()
	w := a.NewWindow("Toolbar")

	toolbar := widget.NewToolbar(
		widget.NewToolbarAction(theme.DocumentCreateIcon(), func() {
			fmt.Println("create new Doc")
		}),
		widget.NewToolbarSeparator(),
		widget.NewToolbarAction(theme.ContentCutIcon(), func() {
			fmt.Println("Cut")
		}),
		widget.NewToolbarAction(theme.ContentCopyIcon(), func() {
			fmt.Println("Copy")
		}),
		widget.NewToolbarAction(theme.ContentPasteIcon(), func() {
			fmt.Println("Paste")
		}),
		widget.NewToolbarSpacer(),
		widget.NewToolbarAction(theme.HelpIcon(), func() {
			fmt.Println("Display help")
		}),
	)
	content := fyne.NewContainerWithLayout(
		layout.NewBorderLayout(toolbar, nil, nil, nil),
		toolbar, widget.NewLabel("show Toolbar"),
	)
	w.SetContent(content)
	w.Resize(fyne.NewSize(150, 150))
	w.ShowAndRun()
}

func main() {
	test()
}

效果

Go中GUI库fyne_第11张图片

扩展

下面我们通过扩展控件来让widget.Icon能够相应鼠标左键。

package main

import (
	"fmt"

	"fyne.io/fyne"
	"fyne.io/fyne/app"
	"fyne.io/fyne/theme"
	"fyne.io/fyne/widget"
)

type tappableIcon struct {
	widget.Icon
}

func newTappableIcon(res fyne.Resource) *tappableIcon {
	icon := &tappableIcon{}
	icon.ExtendBaseWidget(icon)
	icon.SetResource(res)
	return icon
}

type Tappable interface {
	Tapped(*fyne.PointEvent)
}

// 鼠标左键单机事件
func (t *tappableIcon) Tapped(e *fyne.PointEvent) {
	// 输出鼠标相对于串口的绝对位置(窗口的左上角),相对位置(容器的左上角)
	fmt.Println("tapped at", e)
}

func test() {
	a := app.New()
	w := a.NewWindow("Toolbar")
	w.SetContent(newTappableIcon(theme.FyneLogo()))
	w.Resize(fyne.NewSize(200, 200))
	w.ShowAndRun()
}

func main() {
	test()
}

效果

Go中GUI库fyne_第12张图片

Layout

布局(Layout)可以帮助我们在会使用控件的基础上,对控件的位置进行排版使之更加美观。

BoxLayout

package main

import (
	"image/color"

	"fyne.io/fyne"
	"fyne.io/fyne/app"
	"fyne.io/fyne/canvas"
	"fyne.io/fyne/layout"
)

func test() {
	a := app.New()
	w := a.NewWindow("Box Layout")

	h1 := fyne.NewContainerWithLayout(
		layout.NewHBoxLayout(),
		canvas.NewText("left", color.Black),
		layout.NewSpacer(),
		canvas.NewText("right", color.Black),
	)
	h2 := fyne.NewContainerWithLayout(
		layout.NewHBoxLayout(),
		layout.NewSpacer(),
		canvas.NewText("left", color.Black),
		canvas.NewText("right", color.Black),
	)
	w.SetContent(fyne.NewContainerWithLayout(layout.NewVBoxLayout(), h1, h2))
	w.Resize(fyne.NewSize(200, 200))
	w.ShowAndRun()
}

func main() {
	test()
}

效果

Go中GUI库fyne_第13张图片

GridLayout

网格布局,我们通过layout.NewGridLayout(cols),传入每行的列数来创建。使用该布局时,当我们缩放界面时控件的大小会自动调整

package main

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

func test() {
	a := app.New()
	w := a.NewWindow("Grid Layout")
	img1 := canvas.NewImageFromResource(theme.FyneLogo())
	img2 := canvas.NewImageFromResource(theme.FyneLogo())
	img3 := canvas.NewImageFromResource(theme.FyneLogo())
	w.SetContent(fyne.NewContainerWithLayout(layout.NewGridLayout(2), img1, img2, img3))

	w.Resize(fyne.NewSize(200, 200))
	w.ShowAndRun()
}

func main() {
	test()
}

效果
Go中GUI库fyne_第14张图片

GridWrapLayout

GridWrapLayout相当于时GridLayout的扩展版,我们可以在创建时指定每个子控件的size,当界面大小变化了这些子控件就会重新排列。

package main

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

func test() {
	a := app.New()
	w := a.NewWindow("Grid Layout")
	img1 := canvas.NewImageFromResource(theme.FyneLogo())
	img2 := canvas.NewImageFromResource(theme.FyneLogo())
	img3 := canvas.NewImageFromResource(theme.FyneLogo())
	w.SetContent(fyne.NewContainerWithLayout(layout.NewGridWrapLayout(fyne.NewSize(100, 100)), img1, img2, img3))

	w.Resize(fyne.NewSize(200, 200))
	w.ShowAndRun()
}

func main() {
	test()
}

效果

Go中GUI库fyne_第15张图片

BorderLayout

从名字我们大致可以才出来,这是一种边框布局。当我们需要把控件设置在四个方向的边框上时可以使用,最典型的就是Toolbar的布局。

package main

import (
	"image/color"

	"fyne.io/fyne"
	"fyne.io/fyne/app"
	"fyne.io/fyne/canvas"
	"fyne.io/fyne/layout"
)

func test() {
	a := app.New()
	w := a.NewWindow("Grid Layout")
	left := canvas.NewText("left", color.Black)
	right := canvas.NewText("right", color.Black)
	top := canvas.NewText("top", color.Black)
	bottom := canvas.NewText("bottom", color.Black)
	container := fyne.NewContainerWithLayout(
		layout.NewBorderLayout(top, bottom, left, right),
		top, bottom, left, right,
	)
	w.SetContent(container)
	w.Resize(fyne.NewSize(200, 200))
	w.ShowAndRun()
}

func main() {
	test()
}

效果

Go中GUI库fyne_第16张图片

FormLayout

表单布局其实就是两列的GridLayout,但是做了一些微调。

package main

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

func test() {
	a := app.New()
	w := a.NewWindow("Grid Layout")
	input1 := widget.NewEntry()
	input1.SetPlaceHolder("enter name")
	input2 := widget.NewEntry()
	input2.SetPlaceHolder("enter age")
	label1 := widget.NewLabel("Name")
	label2 := widget.NewLabel("age")
	container := fyne.NewContainerWithLayout(
		layout.NewFormLayout(),
		label1, input1,
		label2, input2,
	)
	w.SetContent(container)
	w.Resize(fyne.NewSize(200, 200))
	w.ShowAndRun()
}

func main() {
	test()
}

效果

Go中GUI库fyne_第17张图片

CenterLayout

CenterLayout就是将空间布局在容器的中心位置,按照传入的顺序,后面传入的控件可能会遮挡前面传入的控件。

package main

import (
	"image/color"

	"fyne.io/fyne"
	"fyne.io/fyne/app"
	"fyne.io/fyne/canvas"
	"fyne.io/fyne/layout"
	"fyne.io/fyne/theme"
)

func test() {
	a := app.New()
	w := a.NewWindow("Grid Layout")
	image := canvas.NewImageFromResource(theme.FyneLogo())
	// 设置图片为填充模式,会尽可能占满容器
    image.FillMode = canvas.ImageFillOriginal
	text := canvas.NewText("Fyne Logo", color.Black)
	container := fyne.NewContainerWithLayout(
		layout.NewCenterLayout(),
		image, text,
	)
	w.SetContent(container)
	w.Resize(fyne.NewSize(200, 200))
	w.ShowAndRun()
}

func main() {
	test()
}

效果

Go中GUI库fyne_第18张图片

MaxLayout

MaxLayout会让容器中的元素都显示为最大的尺寸。

package main

import (
	"image/color"

	"fyne.io/fyne"
	"fyne.io/fyne/app"
	"fyne.io/fyne/canvas"
	"fyne.io/fyne/layout"
	"fyne.io/fyne/theme"
)

func test() {
	a := app.New()
	w := a.NewWindow("Max  Layout")
	image := canvas.NewImageFromResource(theme.FyneLogo())
	text := canvas.NewText("Fyne Logo", color.Black)
	container := fyne.NewContainerWithLayout(
		layout.NewMaxLayout(),
		image, text,
	)
	w.SetContent(container)
	w.Resize(fyne.NewSize(200, 200))
	w.ShowAndRun()
}

func main() {
	test()
}

效果

Go中GUI库fyne_第19张图片

你可能感兴趣的:(Go,golang)