首页 > 其他分享 >Go语言开源跨平台GUI框架Fyne小教程|数据绑定篇

Go语言开源跨平台GUI框架Fyne小教程|数据绑定篇

时间:2023-11-25 10:02:40浏览次数:43  
标签:widget fyne GUI 绑定 binding v2 跨平台 io Fyne


Go语言开源跨平台GUI框架Fyne小教程|数据绑定篇_GO语言


入门

数据绑定是在Fyne工具包v2.0.0版本中引入的的一个强大功能。


通过使用数据绑定,我们可以避免手动管理许多标准对象,如标签、按钮和列表等,的数据内容。


Fyne内置绑定支持许多基本数据类型(如Int、String、Float等),还有列表(如StringList、BoolList)以及Map和Struct绑定。这些类型中的每一种都可以使用一个简单的构造函数来创建。例如,要创建一个零值的新字符串绑定,可以使用绑定binding.NewString()。您可以使用Get和Set方法获取或设置大多数数据绑定的值。

也可以使用类似的函数绑定到现有值,这些函数的名称以Bind开头,并且它们都接受指向绑定类型的指针。例如,要绑定到现有的int,我们可以使用binding.BindInt(&myInt)。

通过保留对绑定值的引用而不是原始变量,我们可以配置小部件和函数来自动响应任何更改!有经验的朋友可以把Fyne框架中的绑定方案与Web开发前端框架Vue或者React中的绑定进行简单对比。

但是:如果想直接更改外部数据的话,请务必调用Reload()方法,以确保绑定系统读取最新的值。请参考下面基础实例代码(本例中没有涉及外部数据问题):

package main

import (
	"log"

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

func main() {
	boundString := binding.NewString()
	s, _ := boundString.Get()
	log.Printf("Bound = '%s'", s)

	myInt := 5
	boundInt := binding.BindInt(&myInt)
	i, _ := boundInt.Get()
	log.Printf("Source = %d, bound = %d", myInt, i)
}


接下来,我们把上述绑定技巧与Fyne GUI组件结合,来了解如何使用绑定简化小部件相关的数据管理问题。

绑定简单的GUI小部件(Gadgets)

绑定小部件的最简单形式是:绑定项作为值而不是静态值传递给此小部件许多小部件提供了一个WithData构造函数,该构造函数将接受类型化的数据绑定项。要设置绑定,只需传入正确的类型即可。

尽管这在最初的代码中看起来没有多大好处,但您可以看到它是如何确保显示的内容始终与数据源保持同步的。您会注意到,我们不需要在Label小部件上调用Refresh(),甚至不需要保留对它的引用,但它会适当地更新。请参考下面的基本示例来体会小部件Label如何与其数据源始终保持同步!

package main

import (
	"crypto/rand"
	"encoding/base64"
	"fmt"

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

func main() {
	myApp := app.New()
	w := myApp.NewWindow("Simple")

	str := binding.NewString()
	str.Set("Initial value")

	text := widget.NewLabelWithData(str)

	button := widget.NewButton("click me", func() {
		randomString, err := generateRandomString(10)
		if err != nil {
			fmt.Println("Failed to generate random string:", err)
			return
		}
		str.Set(randomString)
	})
	content := container.New(layout.NewHBoxLayout(), text, layout.NewSpacer(), button)
	w.SetContent(content)

	w.ShowAndRun()
}
func generateRandomString(length int) (string, error) {
	const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
	charsetBytes := make([]byte, length)
	for i := range charsetBytes {
		_, err := rand.Read(charsetBytes[i : i+1])
		if err != nil {
			return "", err
		}
	}
	return base64.URLEncoding.EncodeToString(charsetBytes), nil
}


Go语言开源跨平台GUI框架Fyne小教程|数据绑定篇_数据_02


双向数据绑定问题

到目前为止,我们已经将数据绑定视为保持用户界面元素及时更新的一种方式。然而,更常见的应用需求是:更新UI小部件中的值的同时,使数据处处保持最新。值得庆幸的是,Fyne中提供的绑定是“双向”的,这意味着不仅可以将值推入其中,而且也可以读取绑定对象中的值。这样一来,绑定对象中的数据的变化将在没有任何附加代码的情况下传达给所有相关连的代码。

为了展示这一点,我们可以编写一个简单的测试应用程序,以显示Label和Entry绑定到相同的值。通过操作,您可以看到通过Entry编辑值也会更新标签中的文本。这一切都如我们所期望的,而且无需调用刷新或引用我们代码中的小部件。

通过使应用程序进行数据绑定,您不需要使用保存指向所有小部件的指针操作。相反,通过将数据捕获为一组绑定值,用户界面就可以成为完全独立的代码。这样的方案显然更易于阅读、管理及数据维护。

package main

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

func main() {
	myApp := app.New()
	w := myApp.NewWindow("Two Way Binding Test")

	str := binding.NewString()
	str.Set("Initial Text!")

	w.SetContent(container.NewVBox(
		widget.NewLabelWithData(str),
		widget.NewEntryWithData(str),
	))

	w.ShowAndRun()
}

Go语言开源跨平台GUI框架Fyne小教程|数据绑定篇_Fyne_03

程序启动后,在下部文本框中输入新的内容,上面标签控件中的内容进行同步更新。这就是双向绑定的特征与优势!

数据转换问题

到目前为止,我们已经使用了数据绑定,其中数据类型与输出类型是匹配的(例如String和Label或Entry)。然而,实际开发中通常需要呈现尚未采用正确格式的数据。为此,绑定包binding提供了许多有用的转换函数。

最常见的是,转换函数用于将不同类型的数据转换为字符串,以便在Label或Entry窗口小部件中显示。请参阅下面代码中如何使用binding.FloatToString将Float转换为String。本例中,你可以通过移动滑块来编辑原始值。每次数据更改时,它都会运行转换代码并更新任何与之连接的小部件。

还可以使用格式字符串为用户界面添加更自然的输出。您可以看到,我们的短绑定也将Float转换为String,但通过使用WithFormat帮助程序,我们可以传递一个格式字符串(类似于fmt包)来提供自定义输出。

package main

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

func main() {
	myApp := app.New()
	w := myApp.NewWindow("Conversion")

	f := binding.NewFloat()
	str := binding.FloatToString(f)
	short := binding.FloatToStringWithFormat(f, "%0.0f%%")
	f.Set(25.0)

	w.SetContent(container.NewVBox(
		widget.NewSliderWithData(0, 100.0, f),
		widget.NewLabelWithData(str),
		widget.NewLabelWithData(short),
	))
	w.Resize(fyne.NewSize(480, 360))

	w.ShowAndRun()
}

Go语言开源跨平台GUI框架Fyne小教程|数据绑定篇_数据绑定_04

上面小例中,我们实现了数据的定制格式化显示。由于数据双向绑定的作用,简单拖动一个水平滚动条,可得到如下图所示的显示效果。

Go语言开源跨平台GUI框架Fyne小教程|数据绑定篇_List_05

List数据绑定实例

为了演示如何使用双向绑定组件来连接更复杂的Fyne类型,我们构造一个稍微复杂的例子,使用List小部件,观察数据绑定如何使其更易于使用。首先,我们创建一个StringList数据绑定,它是String数据类型的列表。一旦我们有了列表类型的数据,我们就可以将其连接到标准的列表小部件。为此,我们使用widget.NewListWithData构造函数,与其他小部件非常相似。

将下面代码与最基本的列表示例进行比较,您将看到两个主要更改:第一个是我们将数据类型作为第一个参数而不是长度回调函数进行传递;第二个更改是最后一个参数,即UpdateItem回调。下面的修订版本使用了binding.DataItem而不是widget.ListIndexID。当使用这个回调结构时,我们应该绑定到模板标签小部件,而不是调用SetText函数。这意味着,如果数据源中的任何字符串发生更改,则表中每个受影响的行都将刷新。

package main

import (
	"fmt"

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

func main() {
	myApp := app.New()
	myWindow := myApp.NewWindow("List数据绑定示例")
	t := SetMyTheme()
	myApp.Settings().SetTheme(t)

	data := binding.BindStringList(
		&[]string{"条目 1", "条目 2", "条目 3", "条目 4", "条目 5", "条目 6", "条目 7"},
	)

	list := widget.NewListWithData(data,
		func() fyne.CanvasObject {
			return widget.NewLabel("template")
		},
		func(i binding.DataItem, o fyne.CanvasObject) {
			o.(*widget.Label).Bind(i.(binding.String))
		})

	add := widget.NewButton("添加", func() {
		val := fmt.Sprintf("条目 %d", data.Length()+1)
		data.Append(val)
	})
	myWindow.SetContent(container.NewBorder(nil, add, nil, nil, list))
	myWindow.Resize(fyne.NewSize(480, 360))
	myWindow.ShowAndRun()
}

其中,SetMyTheme方法在文件custom_theme.go中定义,内容如下(在此先不作解释,有兴趣的读者可以参考官网中的自定义主题):

package main

import (
	"image/color"
	"strings"

	"fyne.io/fyne/v2"
	"fyne.io/fyne/v2/theme"
)

type myTheme struct {
	regular, bold, italic, boldItalic, monospace fyne.Resource
}

func (t *myTheme) Color(name fyne.ThemeColorName, variant fyne.ThemeVariant) color.Color {
	return theme.DefaultTheme().Color(name, variant)
}

func (t *myTheme) Icon(name fyne.ThemeIconName) fyne.Resource {
	return theme.DefaultTheme().Icon(name)
}

func (m *myTheme) Font(style fyne.TextStyle) fyne.Resource {
	if style.Monospace {
		return m.monospace
	}
	if style.Bold {
		if style.Italic {
			return m.boldItalic
		}
		return m.bold
	}
	if style.Italic {
		return m.italic
	}
	return m.regular
}

func (m *myTheme) Size(name fyne.ThemeSizeName) float32 {
	return theme.DefaultTheme().Size(name)
}

func (t *myTheme) SetFonts(regularFontPath string, monoFontPath string) {
	t.regular = theme.TextFont()
	t.bold = theme.TextBoldFont()
	t.italic = theme.TextItalicFont()
	t.boldItalic = theme.TextBoldItalicFont()
	t.monospace = theme.TextMonospaceFont()

	if regularFontPath != "" {
		t.regular = loadCustomFont(regularFontPath, "Regular", t.regular)
		t.bold = loadCustomFont(regularFontPath, "Bold", t.bold)
		t.italic = loadCustomFont(regularFontPath, "Italic", t.italic)
		t.boldItalic = loadCustomFont(regularFontPath, "BoldItalic", t.boldItalic)
	}
	if monoFontPath != "" {
		t.monospace = loadCustomFont(monoFontPath, "Regular", t.monospace)
	} else {
		t.monospace = t.regular
	}
}

func loadCustomFont(env, variant string, fallback fyne.Resource) fyne.Resource {
	variantPath := strings.Replace(env, "Regular", variant, -1)

	res, err := fyne.LoadResourceFromPath(variantPath)
	if err != nil {
		fyne.LogError("Error loading specified font", err)
		return fallback
	}

	return res
}
func SetMyTheme() *myTheme {
	t := &myTheme{}
	t.SetFonts("./assets/ArialUnicode.ttf", "")
	return t
}

Go语言开源跨平台GUI框架Fyne小教程|数据绑定篇_数据绑定_06

在我们的演示代码中有一个“添加”按钮,当点击它时,它将向数据源附加一个新值。这样做将自动触发数据更改处理程序,并展开List小部件以显示新数据。

小结

本文介绍了Fyne框架中数据绑定组件binding在数据与GUI展示组件中的重要作用,并给出了几个基本类型的实例展示。但是,对于binding组件内在实现逻辑并没有作过多和细致解释,后面将撰文作专门探讨,敬请期待。

标签:widget,fyne,GUI,绑定,binding,v2,跨平台,io,Fyne
From: https://blog.51cto.com/zhuxianzhong/8556813

相关文章

  • CPF C#跨平台UI框架开源了
    介绍C#跨平台UI框架提供NETStandard2.0和net4的库,通过Netcore可以跨平台,支持Windows、Mac、Linux,Net4的可以支持XP。各个平台运行效果一致,不依赖系统控件。支持窗体,控件任意透明,支持异形窗体,支持SVG图标显示。支持动画,数据绑定,Mvvm模式,CSS等,简化依赖属性,数据绑定的写法,提供数......
  • Unity IMGUI 快速上手【转】
    文章转载自IMGUIcrashcourseUnity'sIMGUIsystemisquitesimplebutverypowerful.Themainpartsare:TheOnGUIcallback.ThiscallbackisusedtohandleeverythingthathastodowithGUI.TheEventclasswhichistightlyconnectedtoOnGUI.TheGUI......
  • UGUI - 获取鼠标点击对象
    记录UGUI点击不生效,通过下面方法可以拿到点击的所有对象:if(Input.GetMouseButtonDown(0)){PointerEventDataeventData=newPointerEventData(EventSystem.current);eventData.position=newVector2(Input.mousePosition.x,Input.mousePosition.y);List<Ra......
  • m基于Faster-RCNN网络的猫脸检测和猫眼定位系统matlab仿真,带GUI界面
    1.算法仿真效果matlab2022a仿真结果如下:  2.算法涉及理论知识概要       猫作为一种受欢迎的宠物,其图像在互联网上大量存在。对猫脸和猫眼进行准确检测和定位,在宠物识别、情感分析等领域具有广泛的应用价值。然而,由于猫脸和猫眼的多样性以及复杂背景的干扰,传统......
  • fgui在 cocos creator 如何让一个文本做成一个滚动的效果,使用 ts 脚本来实现
    在CocosCreator中使用FGUI(FairyGUI)实现文本的滚动效果,可以通过TypeScript脚本来实现。以下是一个简单的示例:首先,确保已经安装了FGUI插件,并且已经正确配置了TypeScript。然后,创建一个TypeScript脚本,例如ScrollText.ts,并将其附加到你的文本组件所在的节点上。下面是代......
  • GUI--JFrame学习02(实现加减法)
    实现代码packagegui;importjavax.swing.*;importjavax.swing.plaf.FontUIResource;importjava.awt.*;importjava.awt.event.ActionEvent;importjava.awt.event.ActionListener;importjava.util.Enumeration;importjava.util.Random;publicclassTestGuiexte......
  • Python GUI编程
    #!/usr/bin/python--coding:UTF-8--Python2.x导入方法fromTkinterimport*#导入Tkinter库Python3.x导入方法#fromtkinterimport*root=Tk()#创建窗口对象的背景色#创建两个列表li=['C','python','php','htm......
  • JD-GUI 反编译jar包
    JD-GUI反编译jar包,jar包反编译成java文件下载网上开源jar包或者其他项目提供了jar但是没有源码此时需要反编译1.下载jd-guihttps://jd-gui.apponic.com/mac/选择适合自己的版本2.解压tar-xvfjd-gui-osx-1.6.6.tar3.点击jd-gui.app打开软件如果出现无法验证开发者,你确定要运行此......
  • 《安富莱嵌入式周报》第327期:Cortex-A7所有外设单片机玩法LL/HAL库全面上线,分享三款GU
    周报汇总地址:http://www.armbbs.cn/forum.php?mod=forumdisplay&fid=12&filter=typeid&typeid=104 1、2023Hackaday大赛胸牌开源Vectorscope-main.zip(66.83MB)https://github.com/Hack-a-Day/Vectorscope前段时间分享后,好几个网友咨询这个胸牌有没有开源,搜到了开源地址......
  • GUI--JFrame学习01(基本控件)
    [Java进阶]Swing两万字大总结一(超详细教程,这不得收藏一波)_swing教程_程序喵正在路上的博客-CSDN博客1.创建第一个JFrame窗体packagelearn;importjavax.swing.*;importjava.awt.*;publicclassMyFrame01extendsJFrame{publicvoidCreateJFrame(Stringtitl......