16.1 fyne (学习使用golang的gui包)
以前,我是一名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)布局,还提供了GridLayout、FormLayout 甚至是混合布局 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!