jjzjj

Go 图形库 fyne

~羽~. 2023-09-17 原文

文章目录


跨平台的图形化界面库,底层也是C写的东西

安装环境

  1. Install Go
  2. Install Gcc
  3. go get fyne.io/fyne/v2 or go get -u fyne.io/fyne 这两个版本是不一样的建议只装第一个

Hello World

package main  
  
import (  
   "fyne.io/fyne"  
   "fyne.io/fyne/app"   "fyne.io/fyne/widget")  
  
func main() {  
   //创建一个窗口  
   a := app.New()  
   //窗口标题  
   w := a.NewWindow("hello world!")  
   //写入文本  
   w.SetContent(widget.NewLabel("Hello world"))  
   //设置窗口大小  
   w.Resize(fyne.NewSize(200, 200))  
   //展示窗口  
   w.ShowAndRun()  
}

实际上myWin.ShowAndRun()等价于

myWin.Show()
myApp.Run()

myWin.Show()显示窗口,myApp.Run()开启事件循环。

注意一点,fyne默认窗口大小是根据内容的宽高来设置的。上面我们调用myWin.Resize()手动设置了大小。否则窗口只能放下字符串Hello World!

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所有的窗体控件和交互元素都在这个子包中。

常用函数介绍

Title

w := a.NewWindow(“hello world!”)
就从上面hello world中看

//窗口标题  
w := a.NewWindow("hello world!")

窗口的默认大小

//设置窗口大小  
w.Resize(fyne.NewSize(200, 200))

widget 组件

Label

一个简单的控件,可以用来显示字符串. 类似canvas.Text ,不同之处在于Label可以用来处理简单的格式化问题

package main  
  
import (  
   "fyne.io/fyne/v2"  
   "fyne.io/fyne/v2/app"   "fyne.io/fyne/v2/widget")  
  
func main() {  
   a := app.New()  
   Mywin := a.NewWindow("Label")  
  
   Mywin.SetContent(widget.NewLabel("name"))  
   Mywin.Resize(fyne.Size{800, 500})  
   //设置窗口居中  
   Mywin.CenterOnScreen()  
   Mywin.ShowAndRun()  
}

Button

按钮 ,可以让用户点击的. Button可以包含文本,图标.
widget.NewButton()创建一个默认的文本按钮,传入文本和一个无参的回调函数。
带图标的按钮需要调用widget.NewButtonWithIcon(),传入文本和回调参数,还需要一个fyne.Resource类型的图标资源.

package main  
  
import (  
   "fmt"  
   "fyne.io/fyne/theme"   "fyne.io/fyne/v2"   "fyne.io/fyne/v2/app"   "fyne.io/fyne/v2/layout"   "fyne.io/fyne/v2/widget")  
  
func main() {  
   a := app.New()  
  
   Mywin := a.NewWindow("test Button")  
  
   Label := widget.NewLabel("输出:")  
   button1 := widget.NewButton("Button1", func() {  
      fmt.Println("test button ")  
      Label.SetText("button")  
   })  
  
   button2 := widget.NewButtonWithIcon("Button2", theme.HomeIcon(), func() {  
      fmt.Println("icon button")  
      Label.SetText("icon Button")  
   })  
  
   container := fyne.NewContainerWithLayout(layout.NewVBoxLayout(), button1, button2, Label)  
   Mywin.SetContent(container)  
   Mywin.Resize(fyne.Size{800, 500})  
   Mywin.CenterOnScreen()  
   Mywin.ShowAndRun()  
}

Box

一个盒子
其实就是一个简单的水平或垂直的容器.在内部,Box对子控件采用盒状布局
(Box Layout).我们可以通过传入控件对象给widget.NewHBox()widget.NewVBox()创建盒子。或者调用已经创建好的widget.Box对象的Append()Prepend()向盒子中添加控件。前者在尾部追加,后者在头部添加。


package main  
  
import (  
   fyne1 "fyne.io/fyne"  
   "fyne.io/fyne/app"   widget1 "fyne.io/fyne/widget"  
)  
  
func main() {  
   a := app.New()  
  
   Mywin := a.NewWindow("Box test")  
   //在v2版本是没有的  
   content := widget1.NewVBox(  
      widget1.NewLabel("The top of Vbox"),  
      widget1.NewHBox(  
         widget1.NewLabel("Label 1"),  
         widget1.NewLabel("Lable 2"),  
      ),  
   )  
   content.Append(widget1.NewButton("Append", func() {  
      content.Append(widget1.NewLabel("Append"))  
   }))  
   content.Append(widget1.NewButton("Prepend", func() {  
      content.Prepend(widget1.NewLabel("Prepended"))  
   }))  
  
   Mywin.SetContent(content)  
   Mywin.Resize(fyne1.Size{800, 500})  
   Mywin.CenterOnScreen()  
   Mywin.ShowAndRun()  
}

注意这其中的版本问题

Entry 输入框

输入框是用于用户输入简单的文本内容的.调用widget.NewEntry()即可创建一个输入框组件.我们一般保存输入框控件的引用,以便访问其Text字段来获取内容. 注册OnChanged 回调函数. 每当内容有修改的时候, Onchanged就会被调用.我们可以调用SetReadOnly(true)来设置输入框的只读属性 . 方法SetPlaceHolder()用来设置占位字符串,设置字段Multiline让输入框接收多行文本.另外,我们可以使用NewPasswordEntry()创建一个密码输入框,输入的文本是不会以明文显示的.

package main  
  
import (  
   "fmt"  
   "fyne.io/fyne"   "fyne.io/fyne/app"   "fyne.io/fyne/layout"   "fyne.io/fyne/widget")  
  
func main() {  
   a := app.New()  
   mywin := a.NewWindow("test Entry")  
  
   nameentry := widget.NewEntry()  
  
   nameentry.SetPlaceHolder("input name")  
   nameentry.OnChanged = func(content string) {  
      fmt.Println("name", nameentry.Text, "entry")  
   }  
  
   passentry := widget.NewPasswordEntry()  
   passentry.SetPlaceHolder("input passwd")  
  
   nameBox := widget.NewHBox(widget.NewLabel("name"), layout.NewSpacer(), nameentry)  
   passwdBox := widget.NewHBox(widget.NewLabel("password"), layout.NewSpacer(), passentry)  
  
   loginBtn := widget.NewButton("Login", func() {  
      fmt.Println("name", nameentry.Text, "password", passentry, "login in")  
   })  
  
   multiEntry := widget.NewEntry()  
   multiEntry.SetPlaceHolder("please entry\n your description")  
  
   multiEntry.MultiLine = true  
  
   content := widget.NewVBox(nameBox, passwdBox, loginBtn, multiEntry)  
   mywin.SetContent(content)  
   mywin.Resize(fyne.Size{800, 500})  
   mywin.CenterOnScreen()  
   mywin.ShowAndRun()  
}

添加一个容器、条目、按钮

package main  
  
import (  
   "fyne.io/fyne"  
   "fyne.io/fyne/app"   "fyne.io/fyne/container"   "fyne.io/fyne/widget")  
  
type App struct {  
   output *widget.Label  
}  
  
var Myapp App  
  
func main() {  
   a := app.New()  
   Mywindow := a.NewWindow("Hello world!")  
   //三个组件:输出点,输入点,按钮  
   output, entry, btn := Myapp.makeUI()  
  
   //创建一个容器,容器中的VBox填入三个组件  
   Mywindow.SetContent(container.NewVBox(output, entry, btn))  
   Mywindow.Resize(fyne.Size{Width: 500, Height: 500})  
   Mywindow.ShowAndRun()  
}  
  
func (app *App) makeUI() (*widget.Label, *widget.Entry, *widget.Button) {  
   output := widget.NewLabel("Hello world!")  
   entry := widget.NewEntry()  
   btn := widget.NewButton("Enter", func() {  
      app.output.SetText(entry.Text)  
   })  
   app.output = output  
   //高亮按钮(让按钮变得重要,按钮会变成蓝色)  
   btn.Importance = widget.HighImportance  
  
   return output, entry, btn  
}

一个简单的markdown编辑器

从中学到:

  • 比较少的代码
  • 创建一个菜单栏
  • 打开,保存文件操作
  • 关于主题的东西。

Step1

窗口雏形

package main  
  
import (  
   "fyne.io/fyne/v2"  
   "fyne.io/fyne/v2/app"   "fyne.io/fyne/v2/container"   "fyne.io/fyne/v2/widget")  
  
type config struct {  
   EditWidget    *widget.Entry    //编辑窗口  
   PreviewWidget *widget.RichText //预览窗口,RichText是一个富文本编辑器  
   CurrentFile   fyne.URI         //当前的文件  
   SaveMenuItem  *fyne.MenuItem   //文件保存的菜单  
}  
  
var cfg config  
  
func main() {  
   //创建一个fyneAPP  
   a := app.New()  
   // 给这个APP创建一个窗口  
   win := a.NewWindow("Markdown")  
   //拿到用户的接口  
   edit, preview := cfg.MakeUI()  
   // 设置窗口的内容  
   win.SetContent(container.NewHSplit(edit, preview))  
   //展示窗口并启动应用  
   win.Resize(fyne.Size{Width: 800, Height: 500})  
   //窗口居中  
   win.CenterOnScreen()  
   win.ShowAndRun()  
}  
  
func (app config) MakeUI() (*widget.Entry, *widget.RichText) {  
   // 这个函数会返回Markdown的编辑页面和预览页面  
   // 创建一个多行的编辑组件  
   edit := widget.NewMultiLineEntry()  
   //创建预览窗口组件  
   preview := widget.NewRichTextFromMarkdown("")  
   app.EditWidget = edit  
   app.PreviewWidget = preview  
  
   edit.OnChanged = preview.ParseMarkdown  
  
   return edit, preview  
  
}

Step2

添加菜单

这是成品
看看代码:

cfg.CreateMenuItem(win)

调用创建菜单函数

// 创建菜单项  
func (app config) CreateMenuItem(win fyne.Window) {  
   //打开菜单项  
   openMenuItem := fyne.NewMenuItem("Open..", func() {  
  
   })  
   //保存菜单项  
   saveMenuItem := fyne.NewMenuItem("save", func() {  
  
   })  
   //保存为菜单项,也就是另存为.  
   saveAsMenuItem := fyne.NewMenuItem("save as .....", func() {  
  
   })  
   // 创建一个菜单:  
   fileMenu := fyne.NewMenu("File", openMenuItem, saveMenuItem, saveAsMenuItem)  
   // 表示文件菜单有三个选项  
   menu := fyne.NewMainMenu(fileMenu)  
  
   // 设置主菜单  
   win.SetMainMenu(menu)  
}

可以看见NewMenuItem是创建展开后的小项,NewMenu是创建一个在菜单栏中可以看见点击的菜单.

可以看到在菜单项中的函数没有内容,想想就知道,这里写的内容就是点击了之后产生的操作.

   //打开菜单  
   openMenuItem := fyne.NewMenuItem("Open..", func() {  
  
   })  
   //保存菜单  
   saveMenuItem := fyne.NewMenuItem("save", func() {  
  
   })  
   //保存为菜单  
   saveAsMenuItem := fyne.NewMenuItem("save as .....", func() {  
  
   })  

接着就是写这里的内容了
不过为了保持代码整洁,我可以把这三个函数写在外面.用的时候调用就行.

//过滤只能打开md文件  
  
var filter = storage.NewExtensionFileFilter([]string{".md", ".MD"})  
  
func (app *config) saveFunc(win fyne.Window) func() {  
   return func() {  
      //当前文件不是空的时候才可以保存  
      if app.CurrentFile != nil {  
         writer, err := storage.Writer(app.CurrentFile)  
         if err != nil {  
            dialog.ShowError(err, win)  
            return  
         }  
         writer.Write([]byte(app.EditWidget.Text))  
         defer writer.Close()  
      }  
   }  
}  
  
// 创建一个返回值是函数的方法.  
func (app *config) saveAsFunc(win fyne.Window) func() {  
   // fyne是跨平台的,在保存文件的时候,不需要考虑操作系统的区别.  
   return func() {  
      saveDialog := dialog.NewFileSave(func(write fyne.URIWriteCloser, err error) {  
         //出现错误就直接返回,错误处理模块  
         if err != nil {  
            dialog.ShowError(err, win)  
            return  
         }  
         if write == nil {  
            // 用户点击了取消保存  
            return  
         }  
         if !strings.HasSuffix(strings.ToLower(write.URI().String()), ".md") {  
            dialog.ShowInformation("error", "please name your file with a .md extension", win)  
            return  
         }  
         //保存文件  
         _, err = write.Write([]byte(app.EditWidget.Text))  
         if err != nil {  
            return  
         }  
         app.CurrentFile = write.URI()  
  
         defer func(write fyne.URIWriteCloser) {  
            err := write.Close()  
            if err != nil {  
  
            }         }(write)  
  
         //保存文件后就来改变下窗口的Title  
         win.SetTitle(win.Title() + "-" + write.URI().Name())  
         app.SaveMenuItem.Disabled = false  
      }, win)  
      //设置默认文件名  
      saveDialog.SetFileName("untitled.md")  
      //设置文件后缀过滤  
      saveDialog.SetFilter(filter)  
      //展示出来  
      saveDialog.Show()  
   }  
}  
  
//打开文件的函数编写.  
  
func (app *config) openFunc(win fyne.Window) func() {  
   return func() {  
      openDialog := dialog.NewFileOpen(func(read fyne.URIReadCloser, err error) {  
         if err != nil {  
            dialog.ShowError(err, win)  
            return  
         }  
         if read == nil {  
            return  
  
         }  
  
         defer read.Close()  
         data, err := ioutil.ReadAll(read)  
         if err != nil {  
            dialog.ShowError(err, win)  
            return  
         }  
         app.EditWidget.SetText(string(data))  
  
         app.CurrentFile = read.URI()  
  
         win.SetTitle(win.Title() + "-" + read.URI().Name())  
         app.SaveMenuItem.Disabled = false  
  
      }, win)  
      //设置过滤  
      openDialog.SetFilter(filter)  
      openDialog.Show()  
   }  
}

有关Go 图形库 fyne的更多相关文章

  1. 世界前沿3D开发引擎HOOPS全面讲解——集3D数据读取、3D图形渲染、3D数据发布于一体的全新3D应用开发工具 - 2

    无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD

  2. ruby - 图形与 Prawn - 2

    寻找一个gem为prawn添加gtraphing功能,我找到了thisone但它似乎有点过时了。有没有更活跃的gem? 最佳答案 直接在Prawn内部绘图没有什么非常活跃的,但是Gruff是一个高度可配置的活跃gem,可以让你制作各种图表。事实上,prawn-graph基本上是gruff的包装器!我的建议是使用gruff生成所需的图表和图形,然后将它们作为图像嵌入到Prawn文档中。所以代码看起来像这样:g=Gruff::Line.new(400)g.title="TransparentBackground"g.theme={:co

  3. ruby-on-rails - Textmate 'Go to symbol' 相当于 Vim - 2

    在Railcasts上,我注意到一个非常有趣的功能“转到符号”窗口。它像Command-T一样工作,但显示当前文件中可用的类和方法。如何在vim中获取它? 最佳答案 尝试:helptags有各种程序和脚本可以生成标记文件。此外,标记文件格式非常简单,因此很容易将sed(1)或类似的脚本组合在一起,无论您使用何种语言,它们都可以生成标记文件。轻松获取标记文件(除了下载生成器之外)的关键在于格式化样式而不是实际解析语法。 关于ruby-on-rails-Textmate'Gotosymbol

  4. ruby-on-rails - 用于 Ruby 的 vim 中的全局 "Go to definition"? - 2

    自97年以来我一直在使用vi/vim进行各种快速编辑和管理任务,但最近才考虑使用它来替换Netbeans作为我选择的ruby​​编辑器。我发现一件事在Netbeans和Eclipse中非常有用的是Ctrl+Click“转到定义”功能,您可以在其中按住Ctrl键并单击一个类或方法,然后它将带您了解定义。现在,我玩过丰富的ctags和rails.vim,而且很接近,但没有雪茄。这就是我想要的:默认情况下在Netbeans和Eclipse中,您可以在本地rails中按住ctrl并单击本地方法或类项目,但你也可以ctrl+click定义在gems或用Ruby编写的系统库。以Netbeans为例

  5. ruby-on-rails - ruby 中的图形数据库? - 2

    ruby有没有图数据库。我听说过Jruby中的neo4j,是否有纯ruby​​中的图形数据库实现? 最佳答案 我自己一直在研究这个。有许多有趣的图形数据库;然而,Neo4j和OrientDB是我唯一找到的任何类型的RubyAPI。虽然不是真正的图形数据库,但FlockDB使用MySQL后端来模拟图形数据库结构。InfoGrid和HyperGraphDB看起来很有趣,但遗憾的是,没有RubyAPI。 关于ruby-on-rails-ruby中的图形数据库?,我们在StackOverflow

  6. 图形学-变换(平移矩阵,旋转矩阵,缩放矩阵,线性变换,仿射变换,齐次坐标) - 2

    1.变换1.1什么是变换?变换(Transform)是计算机图形学中非常重要的一部分。变换包含模型变换(Modelingtransform)以及视图变换(Viewtransform)。模型变换指的是变换模型(被拍摄物体)的位置,大小和角度;视图变换指的是变换照相机的位置和角度。从相对运动的角度来看,两种变换是可以相互转化的。1.2模型变换1.2.1二维变换缩放变换缩放变换(Scale)中,如果一个图片以原点(0,0)为中心缩放𝑠倍。那么点(𝑥,𝑦)变换后数学形式可以表示为写成矩阵形式为:当然,我们也可以给x轴和y轴不同的缩放倍数𝑠𝑥和𝑠𝑦。在非均匀情况下,缩放变换的矩阵形式为反射变换反射变换(

  7. ruby-on-rails - 回形针可以读取 S3 存储桶中的照片几何图形吗? - 2

    我想从我的S3容器中读取照片的几何形状。当它在我的本地时,这是有效的:defphoto_geometry(style=:original)@geometry||={}@geometry[style]||=Paperclip::Geometry.from_filephoto.path(style)end但是当我将模型切换到S3时它似乎不起作用。有什么建议吗?更大的故事是,我正在尝试编写一些代码,允许我从S3检索照片,允许用户裁剪它们,然后将它们重新上传回S3,仍然由回形针分配。编辑:这是返回的错误:Paperclip::NotIdentifiedByImageMagickError:ph

  8. 用于游戏图形的 Ruby 库/gem? - 2

    我正在用Ruby创建一个简单的Blackjack游戏,我已经完成了所有的游戏逻辑工作(终于!),目前它通过命令行运行。一切正常,所以现在我需要一个库或gem来使整个图形方面的事情变得更容易。有任何想法吗?谢谢。 最佳答案 除了在其他地方提到的Gosu,还有Rubygame这似乎也定期更新。对两者都没有意见,建议作为比较的替代方案。更新:街区的新(-ish?)child:Ray.它看起来相当面向图形。 关于用于游戏图形的Ruby库/gem?,我们在StackOverflow上找到一个类似的

  9. DiFi: A Go-as-You-Pay Wi-Fi Access System 精读笔记(三) - 2

    IV.SYSTEMIMPLEMENTATIONWeadoptmodulardesignfollowingtheintegrationofblockchain.Itbringsmoreflexibilitybyseparatingtheimplementationofdifferentfunctionalities,sowecouldleveragetheadvantagesoftheblockchain-basedsmartcontractwhilereducingoverhead.Figure3illustrateshowdifferentmodulesareinvolvedintheint

  10. go-templates - 如何根据表达式有条件地在 Go 模板中设置变量,如果不使用 if 语句包装可能会导致错误 - 2

    问题我该如何做这样的事情:{{$use_ssl:=(ne$.Env.CERT_NAME"")}}其中$.Env.CERT_NAME可能为零/未定义。如果它是零,它给出这个错误:at:errorcallingne:invalidtypeforcomparison注意:我无法控制传递给Go模板的对象,因此必须完全在模板本身内解决这个问题。我尝试过的我试图通过首先检查它是否为非空来变通:{{$use_ssl:=(($.Env.CERT_NAME)&&(ne$.Env.CERT_NAME""))}}但它给出了这个错误:unexpected"&"inoperand所以我切换到这个,这在语法上是允

随机推荐