16.1 fyne (学习使用golang的gui包)

deer332025-02-01技术文章87

以前,我是一名java开发者,虽然对GUI开发并没什么特别的了解,但是多少在学习java的时候对GUI开发还是挺感兴趣的。最早,大家使用swing,swt等工具,看起来还可以。学习时期,用来模仿一下QQ界面开发,对于新手来说还是蛮兴奋的。后来又用过JavaFX,当时主要是用来开发点给策划用的可视化的小工具,比如那种开服工具,配置表管理工具之类的,这个工具算是比较好用,它可以直接编辑界面,像是搞可视化开发一样,还支持css之类的,你还以为在开发网站呢。嘿嘿

既然学习了golang,咱们总得了解一下golang是不是也可以做相关的开发呢。网上我搜索了一下,还真有,不过原生golang是不支持GUI开发的,都是需要第三方使用CGO的方式开发的框架的。不过没事儿,学习嘛,总得抱着敬畏之心,今天就学一学吧。

今天,我选择的是fyne这个框架了。

在开始之前,首先需要安装好gcc了,因为毕竟用的是CGO嘛。在windows上选择gcc的方案无非就两种,一种是MinGW,一种是CgyWin了,他们都是可以开发C/C++的好工具。当然,我这里为了轻量一点,选择了MinGW。

安装MinGW,其实如果用官网提供的下载器真的很慢,网络问题让你可能崩溃,所以我直接选择了使用下载压缩包直接配置就行了

下面是下载压缩包的路径:

https://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win64/Personal%20Builds/mingw-builds/8.1.0/threads-posix/seh/x86_64-8.1.0-release-posix-seh-rt_v6-rev0.7z/download

贴到浏览器里面就可以下载呢!

我这里下载完成了,然后解压吧。


接着,咱们配置一下环境变量,找到mingw64/bin目录,我的是:E:\mingw\mingw64\bin

然后将这个路径加入到环境变量PATH里面去:


然后一路确定吧,嘿嘿!!

好了,配置完成了,咱们接着在cmd窗口中看看正不正常。

嗯嗯!不错,gcc安装成功了。

接着,咱们可以开始搞fyne咯。

首先咱们得下载包:

go get fyne.io/fyne/v2

我这里已经下载好了。

找了1个简单的例子,先运行起来看看。


package main

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

func main() {
	a := app.New()                                //创建应用
	w := a.NewWindow("学习")                        //创建窗口 窗口名称是学习
	w.Resize(fyne.NewSize(1000, 600))             //设置窗口大小
	w.CenterOnScreen()                            //把窗口设置到屏幕中央
	w.SetContent(widget.NewLabel("Hello World!")) //放一个标签到窗口中
	w.ShowAndRun()                                ///显示窗口并运行
}

这是1个简单的例子:咱们运行一下试试。



嘿嘿。这就运行起来了1个窗口应用了。看看窗口标题是“学习”,内容中也有我设置的标签“Hello World”了。似乎好像已经开始有点意思了。

我们刚刚用的widget.NewLabel组件,这就是在窗口中显示一个文本的组件。我们想想一般窗口中会有哪些组件呢?按钮?输入框?选项框?这些都是。那么我试试跑起来一个呢。

放个按钮试试:

package main

import (
	"fmt"

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

func main() {
	a := app.New()                   //创建应用
	w := a.NewWindow("学习")           //创建窗口 窗口名称是学习
	w.Resize(fyne.NewSize(500, 200)) //设置窗口大小
	w.CenterOnScreen()               //把窗口设置到屏幕中央
	//w.SetContent(widget.NewLabel("Hello World!")) //放一个标签到窗口中
	w.SetContent(widget.NewButton("按钮", func() {
		fmt.Println("按钮被点击")
	})) //放一个按钮,并加入点击回调
	w.ShowAndRun() ///显示窗口并运行
}

执行运行:

这个按钮确实是出来了,不过会不会太大了一点???点击一下试试,发现确实是有效果,并且打印了我想要的内容。只是这个按钮这么大,会不会有点大毛病呀!!!

从我以前有过的一点点GUI开发的经验来说,这种一般都是布局的问题。我们看看有没有什么布局的组件?还真有。

网上搜索了一下,说Fyne 布局支持水平(HBoxLayout)和垂直(VBoxLayout)布局,还提供了GridLayoutFormLayout 甚至是混合布局 CombinedLayout 等高级布局方式。

先来试试垂直布局吧,这些布局的都在container包里面哈:

package main

import (
	"fmt"

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

func main() {
	a := app.New()                   //创建应用
	w := a.NewWindow("学习")           //创建窗口 窗口名称是学习
	w.Resize(fyne.NewSize(500, 200)) //设置窗口大小
	w.CenterOnScreen()               //把窗口设置到屏幕中央
	//w.SetContent(widget.NewLabel("Hello World!")) //放一个标签到窗口中
	// w.SetContent(widget.NewButton("按钮", func() {
	// 	fmt.Println("按钮被点击")
	// })) //放一个按钮,并加入点击回调
	l := widget.NewLabel("Hello World!")
	l.Alignment = fyne.TextAlignCenter
	vbox := container.NewVBox(
		l,
		widget.NewButton("按钮", func() {
			fmt.Println("按钮被点击")
		}),
	) //创建一个垂直布局容器
	w.SetContent(vbox)
	w.ShowAndRun() ///显示窗口并运行
}

我在垂指布局的容器中放入了1个标签控件和1个按钮控件,标签控件让他水平居中了。

这是我的执行效果:


再来看看水平布局呢?

package main

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

func main() {
	a := app.New()                   //创建应用
	w := a.NewWindow("学习")           //创建窗口 窗口名称是学习
	w.Resize(fyne.NewSize(500, 200)) //设置窗口大小
	w.CenterOnScreen()               //把窗口设置到屏幕中央
	//w.SetContent(widget.NewLabel("Hello World!")) //放一个标签到窗口中
	// w.SetContent(widget.NewButton("按钮", func() {
	// 	fmt.Println("按钮被点击")
	// })) //放一个按钮,并加入点击回调
	// l := widget.NewLabel("Hello World!")
	// l.Alignment = fyne.TextAlignCenter
	// vbox := container.NewVBox(
	// 	l,
	// 	widget.NewButton("按钮", func() {
	// 		fmt.Println("按钮被点击")
	// 	}),
	// ) //创建一个垂直布局容器
	//w.SetContent(vbox)
	l := widget.NewLabel("姓名:")
	l.Alignment = fyne.TextAlignLeading
	nameInput := widget.NewEntry()
	nameInput.Resize(fyne.NewSize(200, 30))
	hbox := container.NewHBox(l, nameInput)
	hbox.Resize(fyne.NewSize(500, 50))
	w.SetContent(hbox)
	w.ShowAndRun() ///显示窗口并运行
}


总感觉这个布局怪怪的。。。

再来试试吧!!布局嵌套试试?

package main

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

func main() {
	a := app.New()                   //创建应用
	w := a.NewWindow("学习")           //创建窗口 窗口名称是学习
	w.Resize(fyne.NewSize(500, 200)) //设置窗口大小
	w.CenterOnScreen()               //把窗口设置到屏幕中央
	//w.SetContent(widget.NewLabel("Hello World!")) //放一个标签到窗口中
	// w.SetContent(widget.NewButton("按钮", func() {
	// 	fmt.Println("按钮被点击")
	// })) //放一个按钮,并加入点击回调
	// l := widget.NewLabel("Hello World!")
	// l.Alignment = fyne.TextAlignCenter
	// vbox := container.NewVBox(
	// 	l,
	// 	widget.NewButton("按钮", func() {
	// 		fmt.Println("按钮被点击")
	// 	}),
	// ) //创建一个垂直布局容器
	//w.SetContent(vbox)
	// l := widget.NewLabel("姓名:")
	// l.Alignment = fyne.TextAlignLeading
	// nameInput := widget.NewEntry()
	// nameInput.Resize(fyne.NewSize(200, 30))
	// hbox := container.NewHBox(l, nameInput)
	// hbox.Resize(fyne.NewSize(500, 50))
	// w.SetContent(hbox)
	vbox := container.NewVBox(
		container.NewHBox(
			container.NewVBox(
				container.NewHBox(
					widget.NewLabel("姓名:"),
					widget.NewEntry(),
				),
				container.NewHBox(
					widget.NewLabel("年龄:"),
					widget.NewEntry(),
				),
			),
		),
		container.NewVBox(
			widget.NewButton("提交", func() {}),
		),
	)
	w.SetContent(vbox)
	w.ShowAndRun() ///显示窗口并运行
}

执行一下:

看起来还是不那么正常。不过没事,可以学习学习其他布局看看。

package main

import (
	"fmt"

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

func main() {
	a := app.New()                   //创建应用
	w := a.NewWindow("学习")           //创建窗口 窗口名称是学习
	w.Resize(fyne.NewSize(500, 200)) //设置窗口大小
	w.CenterOnScreen()               //把窗口设置到屏幕中央
	//w.SetContent(widget.NewLabel("Hello World!")) //放一个标签到窗口中
	// w.SetContent(widget.NewButton("按钮", func() {
	// 	fmt.Println("按钮被点击")
	// })) //放一个按钮,并加入点击回调
	// l := widget.NewLabel("Hello World!")
	// l.Alignment = fyne.TextAlignCenter
	// vbox := container.NewVBox(
	// 	l,
	// 	widget.NewButton("按钮", func() {
	// 		fmt.Println("按钮被点击")
	// 	}),
	// ) //创建一个垂直布局容器
	//w.SetContent(vbox)
	// l := widget.NewLabel("姓名:")
	// l.Alignment = fyne.TextAlignLeading
	// nameInput := widget.NewEntry()
	// nameInput.Resize(fyne.NewSize(200, 30))
	// hbox := container.NewHBox(l, nameInput)
	// hbox.Resize(fyne.NewSize(500, 50))
	// w.SetContent(hbox)
	// vbox := container.NewVBox(
	// 	container.NewHBox(
	// 		container.NewVBox(
	// 			container.NewHBox(
	// 				widget.NewLabel("姓名:"),
	// 				widget.NewEntry(),
	// 			),
	// 			container.NewHBox(
	// 				widget.NewLabel("年龄:"),
	// 				widget.NewEntry(),
	// 			),
	// 		),
	// 	),
	// 	container.NewVBox(
	// 		widget.NewButton("提交", func() {}),
	// 	),
	// )
	// w.SetContent(vbox)

	g := container.NewGridWithColumns(3)
	for i := 0; i < 9; i++ {
		l := widget.NewLabel(fmt.Sprintf("%d", i))
		l.Alignment = fyne.TextAlignCenter
		g.Add(l)
	}
	w.SetContent(g)
	w.ShowAndRun() ///显示窗口并运行
}

看看执行效果:


这个可以,这个可以按照格子来布局的方式在有些场景还是不错的。

再来看看有1个网格包装的布局吧,相对于上面的网格包装的布局,这种布局可以控制格子的宽度的,这种挺不错。

package main

import (
	"fmt"

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

func main() {
	a := app.New()                   //创建应用
	w := a.NewWindow("学习")           //创建窗口 窗口名称是学习
	w.Resize(fyne.NewSize(500, 200)) //设置窗口大小
	w.CenterOnScreen()               //把窗口设置到屏幕中央
	//w.SetContent(widget.NewLabel("Hello World!")) //放一个标签到窗口中
	// w.SetContent(widget.NewButton("按钮", func() {
	// 	fmt.Println("按钮被点击")
	// })) //放一个按钮,并加入点击回调
	// l := widget.NewLabel("Hello World!")
	// l.Alignment = fyne.TextAlignCenter
	// vbox := container.NewVBox(
	// 	l,
	// 	widget.NewButton("按钮", func() {
	// 		fmt.Println("按钮被点击")
	// 	}),
	// ) //创建一个垂直布局容器
	//w.SetContent(vbox)
	// l := widget.NewLabel("姓名:")
	// l.Alignment = fyne.TextAlignLeading
	// nameInput := widget.NewEntry()
	// nameInput.Resize(fyne.NewSize(200, 30))
	// hbox := container.NewHBox(l, nameInput)
	// hbox.Resize(fyne.NewSize(500, 50))
	// w.SetContent(hbox)
	// vbox := container.NewVBox(
	// 	container.NewHBox(
	// 		container.NewVBox(
	// 			container.NewHBox(
	// 				widget.NewLabel("姓名:"),
	// 				widget.NewEntry(),
	// 			),
	// 			container.NewHBox(
	// 				widget.NewLabel("年龄:"),
	// 				widget.NewEntry(),
	// 			),
	// 		),
	// 	),
	// 	container.NewVBox(
	// 		widget.NewButton("提交", func() {}),
	// 	),
	// )
	// w.SetContent(vbox)

	// g := container.NewGridWithColumns(3)
	// for i := 0; i < 9; i++ {
	// 	l := widget.NewLabel(fmt.Sprintf("%d", i))
	// 	l.Alignment = fyne.TextAlignCenter
	// 	g.Add(l)
	// }
	// w.SetContent(g)

	gw := container.NewGridWrap(fyne.NewSize(80, 80))
	for i := 0; i < 9; i++ {
		l := widget.NewLabel(fmt.Sprintf("%d", i))
		l.Alignment = fyne.TextAlignCenter
		gw.Add(l)
	}
	w.SetContent(gw)
	w.ShowAndRun() ///显示窗口并运行
}


这种方式具有适配性,更加窗口本身的宽高来调整格子的显示位置,而不是固定显示再哪里。

再最后介绍一种布局吧,空布局(哈哈哈,我自己取的名字)。

由于在fyne中,窗口默认就提供了布局,其实有时候用起来真的难受,我很可能就简单的搞个自己用的小东西,我管它什么布局呢?我就不要布局,自己摆放个位置,设置大小,然后看着还行就可以了。于是我们就可以不使用它的默认布局。

举个例子:

package main

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

func main() {
	a := app.New()                   //创建应用
	w := a.NewWindow("学习")           //创建窗口 窗口名称是学习
	w.Resize(fyne.NewSize(320, 170)) //设置窗口大小
	w.CenterOnScreen()               //把窗口设置到屏幕中央
	c := container.NewWithoutLayout()
	//名称标签
	lname := widget.NewLabel("姓名:")
	lname.Alignment = fyne.TextAlignLeading
	lname.Resize(fyne.NewSize(50, 30))
	lname.Move(fyne.NewPos(10, 10))
	//名称输入框
	nameEntry := widget.NewEntry()
	nameEntry.Resize(fyne.NewSize(200, 40))
	nameEntry.Move(fyne.NewPos(70, 10))
	//密码标签
	lpass := widget.NewLabel("密码:")
	lpass.Alignment = fyne.TextAlignLeading
	lpass.Resize(fyne.NewSize(50, 30))
	lpass.Move(fyne.NewPos(10, 60))
	//密码输入框
	passEntry := widget.NewPasswordEntry()
	passEntry.Resize(fyne.NewSize(200, 40))
	passEntry.Move(fyne.NewPos(70, 60))
	//按钮
	btn := widget.NewButton("登录", func() {
		dialog.ShowInformation("登录成功", "你的名字是"+nameEntry.Text, w)
	})
	btn.Resize(fyne.NewSize(100, 30))
	btn.Move(fyne.NewPos(100, 120))
	c.Add(lname)
	c.Add(nameEntry)
	c.Add(lpass)
	c.Add(passEntry)
	c.Add(btn)
	w.SetContent(c)
	w.ShowAndRun() ///显示窗口并运行
}

上面我不使用布局,然后创建了1个登录窗口,然后新学了两个控件,1个是widget.NewPasswordEntry()这个是密码控件,一个是弹窗控件dialog.ShowInformation("登录成功", "你的名字是"+lname.Text, w)。

看看执行效果吧:

当执行点击:

好了。暂时先学到这里吧。下面我打算学canvas了,目的是为了测试一下我用golang简单实现的aoi,利用canvas看看效果。希望今天的学习,能有正向反馈吧。

bye!