Golang GUI编程-熟悉各种界面元素

本文介绍了Golang的GUI编程库Fyne,包括画布和画布对象、Widget、容器和布局的使用,以及快捷键、API、数据绑定和编译选项的详细解释。通过实例展示了如何创建和操作界面元素,以及如何处理用户交互和数据存储。
摘要由CSDN通过智能技术生成

画布(Canvas)和画布对象(CanvasObject)

**画布就是一块用来显示应用内容的屏幕区域。**每一个应用窗口都有一个可以通过Window.Canvas()访问的画布,但是通常你可以在window上找到功能而不是直接操作画布。

Fyne中所有可以绘制的元素都属于CanvasObject的一种。

除了更改使用Canvas.SetContent()显示的内容,也可以改变当前可见的内容。例如要改变一个长方块的FillColour,可以通过对已有组件执行刷新操作,比如:rect.Refresh()

下面举个例子:

package main

import (
	"image/color"
	"time"

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

func main() {
	myApp := app.New()
	myWindow := myApp.NewWindow("Canvas")
	myWindow.SetContent(widget.NewLabel("label1"))
	myCnavas := myWindow.Canvas()

	blue := color.NRGBA{R: 0, G: 0, B: 180, A: 255}
	rect := canvas.NewRectangle(blue)
	myCnavas.SetContent(rect)

	go func() {
		time.Sleep(time.Second)
		green := color.NRGBA{R: 0, G: 180, B: 0, A: 255}
		rect.FillColor = green
		rect.Refresh()

	}()

	myWindow.Resize(fyne.NewSize(100, 100))
	myWindow.ShowAndRun()
}

这个例子做了如下几个事情:

  1. 创建一个fyne应用对象。
  2. 为应用打开一个窗口。
  3. 为窗口加上“Label1”的标签。
  4. 为窗口创建一个画布对象。
  5. 创建一个蓝色的长方体对象
  6. 将蓝色长方体填入画布中。
  7. 创建一条协程,并在其中执行以下操作:
    7.1 等待1秒钟
    7.2 将长方体的填充颜色用绿色取代。
    7.3 刷新长方体的显示内容。
  8. 将应用窗口的大小调整为100*100的大小。
  9. 启动fyne应用的显示。

除了上面的例子,我们也可以用不同的方法绘制其它元素,例如圆和文本:

func setContentToText(c fyne.Canvas) {
	green := color.NRGBA{R: 0, G: 180, B: 0, A: 255}
	text := canvas.NewText("Text", green)
	text.TextStyle.Bold = true
	c.SetContent(text)
}

func setContentToCircle(c fyne.Canvas) {
	red := color.NRGBA{R: 0xff, G: 0x33, B: 0x33, A: 0xff}
	circle := canvas.NewCircle(color.White)
	circle.StrokeWidth = 4
	circle.StrokeColor = red
	c.SetContent(circle)
}

Widget

fyne.Widget是一种结合了互动要素的特殊的画布对象。Widget中的逻辑和显示(也叫做WidgetRenderer)是分离的。

由于Widget是画布对象的一种,因此我们可以把窗口的内容设置为一个widget。示例如下:

package main

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

func main() {
	myApp := app.New()
	myWindow := myApp.NewWindow("Widget")

	myWindow.SetContent(widget.NewEntry())
	myWindow.ShowAndRun()
}

运行效果如下:
widget输入框

容器(Container)和布局(Layouts)

上个小节我们学习了如何为一个Canvas设置Canvas Object,但是这种用法在实操中并不常见,因为只显示一个可视元素并不是很有用。我们通常使用Container来处理多元素同时显示的情况。

fyne.Container也是fyne.CanvasObject的一种,因此我们可以将其设置为fyne.Canvas的内容。下面我们通过container.NewWithoutLayout()把3个文本对象放置在一个Container中为例来说明:

package main

import (
	"image/color"

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

func main() {
	myApp := app.New()
	myWindow := myApp.NewWindow("Container")
	green := color.NRGBA{R: 0, G: 164, B: 0, A: 255}

	text1 := canvas.NewText("Hello", green)
	text2 := canvas.NewText("there", green)
	text2.Move(fyne.NewPos(60, 60))
	content := container.NewWithoutLayout(text1, text2)
// content := container.New(layout.NewGridLayout(2), text1, text2)

	myWindow.SetContent(content)
	myWindow.ShowAndRun()
}

运行结果如下:
容器
fyne.Layout可以提供一种在容器中组织元素显示位置的方法。放开上面示例代码中被注释掉的代码,可以通过有两行的网格布局显示容器中的内容。
运行效果如下:
布局
注意:调用layout命令后,会覆盖手工布局的配置。

Widget list

fyne提供了3种类型的小组件,其中标准组件和收集组件在widget包中实现,容器组件是在容器包中实现的。

packagetypename
WidgetstandardAccordion
Button
Card
Check
Entry
FileIcon
Form
Hyperlink
Icon
Label
Progress bar
RadioGroup
Select
SelectEntry
Separator
Slider
TextGrid
Toolbar
CollectionList
Table
Tree
ContainercontainerAppTabs
Split
Scroll

Layout List

布局分为两种:标准布局和联合布局。

typename
Standard LayoutsHorizontal Box (HBox)
Vertical Box (VBox)
Center
Form
Grid
GridWrap
Border
Max
Padded
Combining Layoutsmultiple layouts

Dialog List

typename
Standard DialogsColor
Confirm
FileOpen
Form
Information
Custom

Shortcuts

快捷键是可以由键盘按键组合或者上下文菜单触发的通用任务。快捷键很像键盘事件,可以和焦点元素绑定或者注册到画布上以在窗口上持续工作。

注册至画布

有许多关联至标准键盘快捷键和右键菜单定义的快捷键(例如fyne.ShortcutCopy),快捷键在增加之前首先需要进行定义。在大多数情况下是由键盘触发的快捷键,它是desktop的扩展。这一点可以通过desktop.CustomShortcut实现,例如如果你想使用tab键和control修饰符,可以通过如下代码实现:

ctrlTab := desktop.CustomShortcut{KeyName: fyne.KeyTab, Modifier: desktop.ControlModifier}

注意快捷键可以被重用,所以你也可以把它附加至菜单或者其它的项目。对于上面的例子,如果我们想让这个快捷键一直生效,我们可以把它注册至应用窗口的画布:

ctrlTab := desktop.CustomShortcut{KeyName: fyne.KeyTab, Modifier: desktop.ControlModifier}
	w.Canvas().AddShortcut(&ctrlTab, func(shortcut fyne.Shortcut) {
		log.Println("We tapped Ctrl+Tab")
	})

如你所见,通过这种方式注册快捷键需要两步:传递快捷键的定义和对应的回调函数,如果用户键入了快捷键那么回调函数会被执行并输出打印信息。

为入口设置快捷键

在一些情况下,需要为一些项目注册只在其获取焦点的时候生效的快捷键。这种方法适用于任何可以获取焦点的widget,并且通过扩展widget并增加一个TypedShortcut进行管理。这更像是增加密钥句柄,除了输入的值是yne.Shortcut

type myEntry struct {
	widget.Entry
}

func (m *myEntry) TypedShortcut(s fyne.Shortcut) {
	if _, ok := s.(*desktop.CustomShortcut); !ok {
		m.Entry.TypedShortcut(s)
		return
	}

	log.Println("Shortcut typed:", s)
}

从上述片段可以看出TypedShortcut管理者是如何实现的。从这个函数中你应该查看快捷方式是否是之前使用的自定义类型。如果是标准快捷键,通过调用原始快捷键管理器(如果widget有)也是一个好方法。这些检查完成后,你可以拿快捷键和所有其他各种类型进行比较。

PreferencesAPI的使用

存储用户配置和数据对所有应用开发者来说都是常见的任务,但是在不同平台上分别实现很耗时且乏味。为了使开发更简单,fyne提供了在文件系统上以干净且容易理解的方式存储数据的API,该API接管了这些处理中最复杂的部分。
从API的设置开始,它是Preferences包的一部分,存储和加载功能适用于Bool,Float,Int和String类型。它们当中每一个由3个不同的功能组成,一个用于加载,一个带fallback value and lastly的加载,一个用于存储数据。以下是通过string类型展示的三个功能和对应行为的例子。

// String looks up a string value for the key
String(key string) string
// StringWithFallback looks up a string value and returns the given fallback if not found
StringWithFallback(key, fallback string) string
// SetString saves a string value for the given key
SetString(key string, value string)

这些功能可以通过创建的应用变量和调用Preferences()方法调用。注意必须创建带unique id的应用。这意味着应用需要通过app.NewWithID()创建,以便获取独立的存储空间。以下代码粗略的展示了它的使用方法。

a := app.NewWithID("com.example.tutorial.preferences")
[...]
a.Preferences().SetBool("Boolean", true)
number := a.Preferences().IntWithFallback("ApplicationLuckyNumber", 21)
expression := a.Preferences().String("RegularExpression")
[...]

为了展示这一点,我们需要创建一个简单的总是会在一定时间内自动关闭的APP,这个超时时间可以由用户设置并可以被下个要启动的应用继承。

创建一个名为timeout的变量,用它来以time.Duration的格式存储时间。

var timeout time.Duration

然后我嗯创建一个选择组件,让用户通过两个预定义的字符串选择超时时间,然后让超时时间乘以字符串对应的秒数。最后,AppTimeout键用来将字符串赋值给选中的那个。

timeoutSelector := widget.NewSelect([]string{"10 seconds", "30 seconds", "1 minute"}, func(selected string) {
    switch selected {
    case "10 seconds":
        timeout = 10 * time.Second
    case "30 seconds":
        timeout = 30 * time.Second
    case "1 minute":
        timeout = time.Minute
    }

    a.Preferences().SetString("AppTimeout", selected)
})

现在我们要获取设置的数值,如果不存在,我们想要有一个fallback来将超时时间设置为尽可能小的时间来节省用户的等待时间。这可以通过将选择的timeoutSelector的值设置为加载的值或者在发生回退时设置为回退的值来实现。这样被选择的widget中的代码将会按照指定的值来运行。

timeoutSelector.SetSelected(a.Preferences().StringWithFallback("AppTimeout", "10 seconds"))

最后一步是创建一个运行在独立goroutine中的函数,以告诉应用在选择的超时时间后退出。

go func() {
    time.Sleep(timeout)
    a.Quit()
}()

以上代码合并之后就是下面的完整代码:

package main

import (
    "time"

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

func main() {
    a := app.NewWithID("com.example.tutorial.preferences")
    w := a.NewWindow("Timeout")

    var timeout time.Duration

    timeoutSelector := widget.NewSelect([]string{"10 seconds", "30 seconds", "1 minute"}, func(selected string) {
        switch selected {
        case "10 seconds":
            timeout = 10 * time.Second
        case "30 seconds":
            timeout = 30 * time.Second
        case "1 minute":
            timeout = time.Minute
        }

        a.Preferences().SetString("AppTimeout", selected)
    })

    timeoutSelector.SetSelected(a.Preferences().StringWithFallback("AppTimeout", "10 seconds"))

    go func() {
        time.Sleep(timeout)
        a.Quit()
    }()

    w.SetContent(timeoutSelector)
    w.ShowAndRun()
}

数据绑定

数据绑定从Fyne v2.0.0开始引入,以便方便的关联多个widget至一个需要不定时更新的数据源。data/binding包中有很多有用的可以处理大部分标准类型的封装。
支持绑定的widget一般都有...WithData的构建方法,你也可以通过调用Bind()Unbind()来管理已创建widget的绑定数据。以下是一个绑定至Label的字符串数据的操作例子:

package main

import (
	"time"

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

func main() {
	a := app.New()
	w := a.NewWindow("Bingding")

	str := binding.NewString()
	go func() {
		dots := "....."
		for i := 5; i >= 0; i-- {
			str.Set("Count down" + dots[:i])
			time.Sleep(time.Second)
		}
		str.Set("Blast off")
	}()

	w.SetContent(widget.NewLabelWithData(str))
	w.ShowAndRun()
}

运行结果如下:
在这里插入图片描述

编译选项

构建标签

Fyne一般通过选择驱动和配置来为你的应用真对目标系统进行配置。以下是支持的可以在开发过程中提供帮助的标签。例如如果你想模拟一个运行于桌面电脑上的移动应用,可以通过如下选项实现:

go run -tags mobile main.go
TagDescription
gles强制使用嵌入式OpenGL取代全功能OpenGL。这通常受目标设备控制且一般不需要。
hints显示开发者建议以提升或优化。配置```hints``会在应用不遵守材料设计或其他建议时产生记录
mobile这个标签会让应用在模拟的手机窗口中运行。在你想预览你的手机应用的时候很有用。
no_native_menus这是macOS的专用标签,表示应用不应该使用macOS的原生菜单。而是在应用窗口中直接显示。在使用macOS开发windows或Linux应用是很有用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

电梯人来人往

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值