Go红队开发—图形化界面

news/2025/11/5 17:05:17/文章来源:https://www.cnblogs.com/dhan/p/19194160

目录
  • GUi 图形化
    • 配置
    • 第一个GUI
    • 常用 widget 组件
    • Layout 布局
    • 绝对布局
    • dialog弹框
      • 注意事项
      • 类别
    • 案例demo所有代码

好久没做golang开发了,之前的文章一直在做cli的安全工具开发,这里了解一下gui图形化的开发,后续目前还不知道能发什么了,主要是cli和gui这些无非都是将之前学过的集成在一起而已,我个人是感觉这个合集已经差不多完成了,若是还有在看我这个合集的师傅觉得还想看什么的可以给一些意见。

GUi 图形化

这里只使用 fyne 库,其他库不讨论。

配置

  • 配置GCC:我下载的Gcc版本
拿这个来配置的好处就是不用安装,直接下载解压,然后配置环境变量即可

500

  • 运⾏时,需要开启CGO_ENABLED=1
go env -w CGO_ENABLED=1
# 解释
Fyne 的相关源码⽂件带有 cgo 构建标签。你如果把 CGO_ENABLED=0 关掉了,带该标签的源码会被排除,编译器要么找不到实现,要么⾛到不兼容的路径,从⽽出现“build constraints exclude all Go files” 之类的错误。
反正跟着来就行了
  • 看你使用的是什么环境,我使用的windows,我要切换编译平台回来
go env -w GOOS=windows 
# 若是linux就修改linux
  • 编写一个 “Hello Fyne” 简单窗口
    • 当然创建go项目的时候记得:
      • go mod init 项目名; go mod tidy
package mainimport ("fyne.io/fyne/v2""fyne.io/fyne/v2/app""fyne.io/fyne/v2/widget"
)func main() {myApp := app.New()myWin := myApp.NewWindow("Hello")myWin.SetContent(widget.NewLabel("Hello Fyne!"))myWin.Resize(fyne.NewSize(200, 200))myWin.ShowAndRun()
}
  • 运行代码之前一定要重新打开一下你的vscode或者你的代码编辑器或者终端,为了让之前设置的环境变量生效
# 再次运行代码
go run main.go
下图中我的fyne没有用v2,我后面换了,我上面代码是正确的,只是图片中我忘记切换新版本的库了

630

第一个GUI

func first_gui() {myApp := app.New()                               // 创建一个appmyWin := myApp.NewWindow("Hello")                // 创建一个窗口,之后就要放内容进这个窗口了,同时给这个窗口命名myWin.SetContent(widget.NewLabel("Hello Fyne!")) //简单设置一个标签内容,然后用窗口的SetContent设置内容myWin.Resize(fyne.NewSize(200, 200))             // 设置窗口大小myWin.ShowAndRun()                               // Show显示和Run运行,这里其实是可以分开用两个函数执行,ShowAndRun就是一个命令执行了两个
}

没什么好说,直接看代码来的直接

常用 widget 组件

  • widget.NewLabel
    标签组件,这个就是标签文本
  • widget.NewButton
    按钮,第二个参数传入函数,表示这个按钮被点击后的action动作
  • widget.NewEntry
    组件实体,控件用于给用户输入简单的文本内容
    • SetReadOnly(true/false)设置是否只读
    • SetPlaceHolder设置占位字符
    • widget.NewEntry().MultiLine = true 这样设置可以多行文本
  • widget.NewPasswordEntry
    密码输入框,这个和widget.NewEntry一样,只不过这里是密码的方式输入,所以输入的内容看不到
  • NewMultiLineEntry
    多行文本输入,但其实上面也可以通过MultiLine = true的方式进行多行输入
  • widget.NewCheck
    复选框
  • widget.NewRadioGroup
    单选框,旧版本好像是widget.NewRadio,v2的就用widget.NewRadioGroup
  • widget.NewSelect
    下拉框
  • widget.NewSlider
    滑块
  • widget.NewProgressBar
    进度条,通过SetValue来控制进度条的滑动
  • widget.NewProgressBarInfinite
    无限进度条
  • widget.NewSeparator
    分割线
  • widget.NewVBox
    简单的水平或垂直的容器,Box可以对放入box的控件采用布局
  • widget.NewCard
    卡片,给标题和这个卡片的内容
    目前可以简单的通过containernew一个box进行整合控件在某个容器里
    看下面的代码案例中最后return即可(在basicWidgets函数中)

穿插下container:container.NewVBox和 【container.NewScrollcontainer.NewHScroll

  • container.NewVBox
    这个很重要,因为我们可以打包空间在这个box里面,然后这个box就可以作为某个模块插入到你想要的功能看模块中去了,当然这里使用的是VBox,V表示垂直的布局,后面会学到HBox,表示水平布局
  • container.NewScroll
    这个是整合box的时候,container作为上下滑动还是左右滑动,通过传入VBox还是HBox来判断左右还是上下滑动,一般都是VBox上下滑动,因为我们习惯就是这样,比较好看。

  • widget.NewSelectEntry
    可输入的下拉框
  • widget.NewAccordion
    父:折叠面板
    子:通过widget.NewAccordionItem来创建展开后的面板项目
  • widget.NewForm
    表单,这里看代码吧,涉及到提交和取消函数
// Form - 表单nameEntry := widget.NewEntry()ageEntry := widget.NewEntry()genderRadio := widget.NewRadioGroup([]string{"男", "⼥"}, nil)form := widget.NewForm(widget.NewFormItem("姓名", nameEntry),widget.NewFormItem("年龄", ageEntry),widget.NewFormItem("性别", genderRadio),)form.OnSubmit = func() {fmt.Printf("表单提交 - 姓名: %s, 年龄: %s, 性别: %s\n",nameEntry.Text, ageEntry.Text, genderRadio.Selected)}form.OnCancel = func() {fmt.Println("表单取消")}
  • widget.NewTabContainer
    标签容器,这个可以理解为像浏览器不同窗口之间切换的样子
    这个显示的标签可以修改位置:平时浏览器的窗口都是显示在上方,这里修改的位置就是窗口的那个位置
    • TabLocationBottom:显示在底部
    • TabLocationLeading:显示在顶部左边
    • TabLocationTrailing:显示在顶部右边
      直接看一段简单的伪代码:
tabs := widget.NewTabContainer(widget.NewTabItem("Profile", profile),widget.NewTabItem("Setting", setting),
)
myWindow.SetContent(tabs)
  • widget.NewToolbar
    工具栏,很简单的用法,就是NewToolbarAction创建然后第一个参数给图标,第二个参数给action动作
    直接看下面的代码:
    500
func fyne_toolbar() {myApp := app.New()myWindow := myApp.NewWindow("Toolbar")toolbar := widget.NewToolbar(widget.NewToolbarAction(theme.DocumentCreateIcon(), func() {fmt.Println("New document")}),widget.NewToolbarSeparator(),widget.NewToolbarAction(theme.ContentCutIcon(), func() {fmt.Println("Cut")}),widget.NewToolbarAction(theme.ContentCopyIcon(), func() {fmt.Println("Copy")}),widget.NewToolbarAction(theme.ContentPasteIcon(), func() {fmt.Println("Paste")}),widget.NewToolbarSpacer(),widget.NewToolbarAction(theme.HelpIcon(), func() {log.Println("Display help")}),)content := fyne.NewContainerWithLayout(layout.NewBorderLayout(toolbar, nil, nil, nil),toolbar, widget.NewLabel(`Lorem ipsum dolor, sit amet consectetur adipisicing elit.Quidem consectetur ipsam nesciunt,quasi sint expedita minus aut,porro iusto magnam ducimus voluptates cum vitae.Vero adipisci earum iure consequatur quidem.`),)myWindow.SetContent(content)myWindow.ShowAndRun()
}

Layout 布局

首先我们的控件都是要放进box或者一个容器中,所以我们布局要学的东西在上面可能有看到过,只是没注意到这个是什么布局罢了

  • container.New
    没有布局,要v2版本的fyne,旧版本记得是没有的
  • container.NewVBox
    V表示垂直布局
  • container.NewHBox
    H表示水平布局
  • container.NewBorder
    边框布局
  • layout.NewGridLayout
    固定列数⽹格,这个要用一个空的container然后再传入布局
gridLayout := container.New(layout.NewGridLayout(3), // 3列widget.NewButton("1", nil),widget.NewButton("2", nil),widget.NewButton("3", nil),widget.NewButton("4", nil),widget.NewButton("5", nil),widget.NewButton("6", nil),
)
  • container.NewGridWithColumns
    指定列数的⽹格
  • container.NewGridWithRows
    指定⾏数的⽹格
  • container.NewCenter
    居中布局
  • container.NewMax
    最大化布局
  • container.NewStack
    堆叠布局
  • container.NewPadded
    带内边距的布局
  • container.NewScroll
    这个是垂直的滚动

绝对布局

  • container.NewWithoutLayout
    创建一个没有布局的容器,然后自己来一个个放组件进去,定位也自己写
    直接看代码:
// 绝对定位容器
absolute := container.NewWithoutLayout(widget.NewButton("按钮1", nil), widget.NewButton("按钮2", nil),widget.NewLabel("标签"),
)// ⼿动设置位置和⼤⼩
// 容器里面的组件就按照你当初放的那样,进行一个数组访问即可
// Move移动,Resize重置大小
if len(absolute.Objects) >= 3 {absolute.Objects[0].Move(fyne.NewPos(10, 10))absolute.Objects[0].Resize(fyne.NewSize(100, 30))absolute.Objects[1].Move(fyne.NewPos(120, 10))absolute.Objects[1].Resize(fyne.NewSize(100, 30))absolute.Objects[2].Move(fyne.NewPos(10, 50))absolute.Objects[2].Resize(fyne.NewSize(210, 30))
}return widget.NewCard("绝对定位布局", "", absolute)

dialog弹框

这个就是一些遇到错误就弹框出来,或者删除保存弹出来让你确认之类的等等的一些弹框

注意事项

  • 你这个弹框对应的父窗口一定要看清楚了
    比如你是在A窗口弹框的,那就传A的这个窗口进去作为参数给到该弹框,告诉他应该在A窗口中进行弹框
    否则可能会出现看不到或者被覆盖等等未知情况

类别

  • 信息弹框
    这个就是简单一个弹框提示
dialog.ShowInformation(title, message, parentWindow)
  • 错误弹框
    需要传入错误类型,将你这个错误信息弹出来
dialog.ShowError(err, parentWindow)
  • 确认弹框
    “确定/取消”,回调接收布尔值,使用该bool来判断用户是确定还是取消接着下一步操作
dialog.ShowConfirm(title, message, func(confirmed bool), parentWindow)
  • 自定义弹框
    这个其实就是一套娃,点击某个功能后,你希望弹出什么内容都可以,嵌入该对话框中,比如你点击后弹出的框是另外一个功能更多的程序都可以,但是这样你的这个弹框就有点大了,这个还是看具体情况具体分析。
    NewCustomNewCustomConfirm 的区别:
    • NewCustom:这个只有一个关闭按钮,
    • NewCustomConfirm:会让你去“确认/取消”,然后拿到用户的确认或取消的结果进行一下步操作
dialog.NewCustom(title, dismissText, content, parentWindow)
dialog.NewCustomConfirm(title, confirmText, dismissText, content, func(confirmed bool), parentWindow)
  • 打开文件/文件夹窗口
    • 能够对打开文件夹后做的一些限制:
      • SetFilter(storage.NewExtensionFileFilter([]string{".txt"})) 限制可选可看到的文件后缀类型
      • SetLocation(storage.NewFileURI(path)) 设置初始位置
    • 回调参数为 nil 表示用户取消
dialog.NewFileOpen(func(fyne.URIReadCloser, error), parentWindow)
dialog.NewFileSave(func(fyne.URIWriteCloser, error), parentWindow)
dialog.NewFolderOpen(func(fyne.ListableURI, error), parentWindow)//限制可选可看到的文件后缀类型
SetFilter(storage.NewExtensionFileFilter([]string{".txt"}))
//设置初始位置
SetLocation(storage.NewFileURI(path))
  • 进度弹框
    举例:点击开始扫描,然后弹进度条框/旋转等待任务完成,这种就很常见
    • 需要配合fyne.Do去执行
//通过SetValue设置进度
dialog.NewProgress(title, message, parentWindow)//显示的是旋转,这里就不用setvalue了,没有进度大小
dialog.NewProgressInfinite(title, message, parentWindow)

在 Fyne v2.6.0 及以上版本中,如果你在 非主线程(例如在 goroutine 中)更新UI组件,必须使用 fyne.Do 或 fyne.DoAndWait 来包装这些操作
fyne.Do 与 fyne.DoAndWaitfyne.Do 会异步地将函数调度到主线程执行,不会阻塞你当前的 goroutine,适用于像更新进度条这样的场景。fyne.DoAndWait 则会同步等待函数在主线程执行完毕

案例demo所有代码

如下图所示:很多测试单元函数被我注释了,想要运行哪个就自己解开,不能运行多个,只能一个一个的函数去运行,因为我没有单独把窗口app拎出来

  • 源代码如下
package mainimport ("fmt""image/color""log""net/url""os""time""fyne.io/fyne/v2""fyne.io/fyne/v2/app""fyne.io/fyne/v2/canvas""fyne.io/fyne/v2/container""fyne.io/fyne/v2/dialog""fyne.io/fyne/v2/layout""fyne.io/fyne/v2/theme""fyne.io/fyne/v2/widget" // 导⼊扩展包"github.com/flopp/go-findfont"
)func fyne_test() {myApp := app.New()myWin := myApp.NewWindow("窗口标题")myWin.SetContent(widget.NewLabel("label内容"))myWin.Resize(fyne.NewSize(200, 200)) // 设置窗口大小myWin.CenterOnScreen()               // 窗口居中显示// ---------------------------------// 全屏// myWin.SetFullScreen(true)// 判断是否全屏// isFullScrenn := myWin.FullScreen()// fmt.Println(isFullScrenn)// ---------------------------------// ---------------------------------// 主窗口设置myWin.SetMaster()mainMenu := fyne.NewMainMenu(fyne.NewMenu("文件",fyne.NewMenuItem("新建", func() { fmt.Println("点击了新建") }),fyne.NewMenuItem("打开", func() { fmt.Println("点击了打开") }),fyne.NewMenuItem("推出", func() { myApp.Quit() }),),)myWin.SetMainMenu(mainMenu)// ---------------------------------// 拦截关闭事件myWin.SetCloseIntercept(func() {dialog.ShowConfirm("确认", "确定要退出吗?", func(ok bool) {if ok {myApp.Quit()}}, myWin)})myWin.ShowAndRun() //运行
}// 更新时间
func time_clock() {updateTime := func(clock *widget.Label) {formatted := time.Now().Format("Time: 03:04:05") //获取格式化时间clock.SetText(formatted)}app := app.New()                       // 创建应用程序实例window := app.NewWindow("Hello world") // 创建窗口,标题为"Hello Wolrd"clock := widget.NewLabel("")updateTime(clock)go func() {for range time.Tick(time.Second) {updateTime(clock) // 每秒更新一次时间}}()window.SetContent(clock) // 往窗口中放入一个内容为"Hello world!"的标签控件window.ShowAndRun()      //展示并运行程序
}// 基础控件
func basicWidgets() fyne.CanvasObject {// 1. Label - ⽂本标签label := widget.NewLabel("这是⼀个标签")// 2. Button - 按钮button := widget.NewButton("点击我", func() {fmt.Println("按钮被点击")})// 3. Entry - 单⾏输⼊框entry := widget.NewEntry()entry.SetPlaceHolder("请输⼊⽂本..")// 4. PasswordEntry - 密码输⼊框passwordEntry := widget.NewPasswordEntry()passwordEntry.SetPlaceHolder("输⼊密码...")// 5. MultiLineEntry - 多⾏⽂本输⼊multiEntry := widget.NewMultiLineEntry()multiEntry.SetPlaceHolder("多⾏⽂本...")multiEntry.Resize(fyne.NewSize(300, 100))// 6. Check - 复选框check := widget.NewCheck("同意条款", func(checked bool) {fmt.Println("复选框状态:", checked)})// 7. RadioGroup - 单选按钮组radio := widget.NewRadioGroup([]string{"选项1", "选项2", "选项3"}, func(value string) {fmt.Println("选中:", value)})// 8. Select - 下拉选择框selectWidget := widget.NewSelect([]string{"苹果", "⾹蕉", "橙⼦"}, func(value string) {fmt.Println("选择了:", value)})// 9. Slider - 滑块slider := widget.NewSlider(0, 100)slider.OnChanged = func(value float64) {fmt.Printf("滑块值: %.2f", value)}// 10. ProgressBar - 进度条progress := widget.NewProgressBar()progress.SetValue(0.5) // 50%// 11. ProgressBarInfinite - ⽆限进度条infiniteProgress := widget.NewProgressBarInfinite()// 12. Hyperlink - 超链接link, _ := url.Parse("https://fyne.io")hyperlink := widget.NewHyperlink("访问 Fyne 官⽹", link)// 13. Separator - 分隔线separator := widget.NewSeparator()return container.NewVBox(widget.NewCard("基础控件", "", container.NewVBox(label,button,entry,passwordEntry,multiEntry,check,radio,selectWidget,slider,progress,infiniteProgress,hyperlink,separator,)),)
}
func basicwidget_test() {myapp := app.New()w := myapp.NewWindow("基础控件⽰例")widgets := basicWidgets()w.SetContent(widgets)w.ShowAndRun()
}// 高级控件
func advancedWidgets() fyne.CanvasObject {// 1. Form - 表单nameEntry := widget.NewEntry()ageEntry := widget.NewEntry()genderRadio := widget.NewRadioGroup([]string{"男", "⼥"}, nil)form := widget.NewForm(widget.NewFormItem("姓名", nameEntry),widget.NewFormItem("年龄", ageEntry),widget.NewFormItem("性别", genderRadio),)form.OnSubmit = func() {fmt.Printf("表单提交 - 姓名: %s, 年龄: %s, 性别: %s\n",nameEntry.Text, ageEntry.Text, genderRadio.Selected)}form.OnCancel = func() {fmt.Println("表单取消")}// 2. 日期选择器 - 自定义实现selectedDateLabel := widget.NewLabel("未选择日期")dateEntry := widget.NewEntry()dateEntry.SetPlaceHolder("YYYY-MM-DD")dateButton := widget.NewButton("选择今日", func() {today := time.Now().Format("2006-01-02")dateEntry.SetText(today)selectedDateLabel.SetText("选中日期: " + today)fmt.Println("选择的日期:", today)})dateContainer := container.NewVBox(selectedDateLabel,dateEntry,dateButton,)// 3. SelectEntry - 可输⼊的下拉框selectEntry := widget.NewSelectEntry([]string{"选项1", "选项2", "选项3"})selectEntry.PlaceHolder = "输⼊或选择..."selectEntry.OnChanged = func(value string) {fmt.Println("选择或输⼊:", value)}// 4. Accordion - 折叠⾯板accordion := widget.NewAccordion(widget.NewAccordionItem("基本信息",container.NewVBox(widget.NewLabel("这是基本信息⾯板"),widget.NewEntry(),)),widget.NewAccordionItem("⾼级设置",container.NewVBox(widget.NewLabel("这是⾼级设置⾯板"),widget.NewCheck("启⽤⾼级功能", nil),)),widget.NewAccordionItem("其他选项",container.NewVBox(widget.NewLabel("这是其他选项⾯板"),widget.NewSlider(0, 100),)),)// //左右滑动的container// scrollContainer := container.NewHScroll(container.NewVBox(// 	widget.NewLabel("这是左滑的"),// 	widget.NewLabel("这是右滑的"),// ))// 组合所有控件 上下滑动return container.NewScroll(container.NewVBox(widget.NewCard("表单控件", "", form),widget.NewCard("⽇期选择", "", container.NewVBox(dateContainer,widget.NewSeparator(),)),widget.NewCard("可输⼊下拉框", "", selectEntry),widget.NewCard("折叠⾯板", "", accordion),))}
func advancedWidgets_test() {myapp := app.New()w := myapp.NewWindow("高级控件⽰例")widgets := advancedWidgets()w.SetContent(widgets)w.ShowAndRun()
}// 假设你使用的是旧版本,不是v2以上的,那若你有中文的话,就需要进行中文字体设置
func packet_myfont() {fontPath, err := findfont.Find("FontLibrary/MSYHBD.TTC") // 这个字体文件直接找自己喜欢的即可,能成功加载路径即可if err != nil {panic(err)}// load the font with the freetype library// fontData, err := os.ReadFile(fontPath)// if err != nil {// 	panic(err)// }// _, err = truetype.Parse(fontData)// if err != nil {// 	panic(err)// }os.Setenv("FYNE_FONT", fontPath)}// 画布练习
func fyne_canvas() {myApp := app.New()myWin := myApp.NewWindow("画布测试")myWin.Resize(fyne.NewSize(400, 300))myWin.ShowAndRun()
}// 工具栏练习
func fyne_toolbar() {myApp := app.New()myWindow := myApp.NewWindow("Toolbar")toolbar := widget.NewToolbar(widget.NewToolbarAction(theme.DocumentCreateIcon(), func() {fmt.Println("New document")}),widget.NewToolbarSeparator(),widget.NewToolbarAction(theme.ContentCutIcon(), func() {fmt.Println("Cut")}),widget.NewToolbarAction(theme.ContentCopyIcon(), func() {fmt.Println("Copy")}),widget.NewToolbarAction(theme.ContentPasteIcon(), func() {fmt.Println("Paste")}),widget.NewToolbarSpacer(),widget.NewToolbarAction(theme.HelpIcon(), func() {log.Println("Display help")}),)content := fyne.NewContainerWithLayout(layout.NewBorderLayout(toolbar, nil, nil, nil),toolbar, widget.NewLabel(`Lorem ipsum dolor, sit amet consectetur adipisicing elit.Quidem consectetur ipsam nesciunt,quasi sint expedita minus aut,porro iusto magnam ducimus voluptates cum vitae.Vero adipisci earum iure consequatur quidem.`),)myWindow.SetContent(content)myWindow.ShowAndRun()}// 相对布局练习
func fyne_Layouts() {myApp := app.New()myWin := myApp.NewWindow("Layouts")// 设置窗⼝⼤⼩myWin.Resize(fyne.NewSize(600, 700))// 1. BoxLayout - 垂直布局vboxLayout := container.NewVBox(widget.NewLabel("垂直布局 - 项⽬1"),widget.NewLabel("垂直布局 - 项⽬2"),widget.NewButton("按钮", nil),)// 2. BoxLayout - ⽔平布局hboxLayout := container.NewHBox(widget.NewLabel("⽔平1"),widget.NewLabel("⽔平2"),widget.NewButton("按钮", nil),)// 3. BorderLayout - 边框布局borderLayout := container.NewBorder(widget.NewLabel("顶部"),   // topwidget.NewLabel("底部"),   // bottomwidget.NewLabel("左侧"),   // leftwidget.NewLabel("右侧"),   // rightwidget.NewLabel("中⼼内容"), // center)// 4. GridLayout - 固定列数⽹格gridLayout := container.New(layout.NewGridLayout(3), // 3列widget.NewButton("1", nil),widget.NewButton("2", nil),widget.NewButton("3", nil),widget.NewButton("4", nil),widget.NewButton("5", nil),widget.NewButton("6", nil),)// 5. GridWithColumns - 指定列数的⽹格gridWithColumns := container.NewGridWithColumns(2,widget.NewLabel("单元格 1"),widget.NewLabel("单元格 2"),widget.NewLabel("单元格 3"),widget.NewLabel("单元格 4"),)// 6. GridWithRows - 指定⾏数的⽹格gridWithRows := container.NewGridWithRows(3,widget.NewLabel("⾏ 1"),widget.NewLabel("⾏ 2"),widget.NewLabel("⾏ 3"),)// 7. CenterLayout - 居中布局centerLayout := container.NewCenter(widget.NewLabel("居中的内容"),)// 8. MaxLayout - 最⼤化布局(重叠)maxLayout := container.NewMax(canvas.NewRectangle(color.RGBA{R: 100, G: 100, B: 100, A: 255}),container.NewCenter(widget.NewLabel("覆盖在矩形上")),)// 9. StackLayout - 堆叠布局stackLayout := container.NewStack(canvas.NewRectangle(color.RGBA{R: 200, G: 0, B: 0, A: 100}),container.NewCenter(widget.NewLabel("堆叠内容")),)// 10. PaddedLayout - 带内边距的布局paddedLayout := container.NewPadded(widget.NewButton("有内边距的按钮", nil),)w := container.NewScroll(container.NewVBox(widget.NewCard("VBox 垂直布局", "", vboxLayout),widget.NewCard("HBox ⽔平布局", "", hboxLayout),widget.NewCard("Border 边框布局", "", borderLayout),widget.NewCard("Grid ⽹格布局", "", gridLayout),widget.NewCard("GridWithColumns", "", gridWithColumns),widget.NewCard("GridWithRows", "", gridWithRows),widget.NewCard("Center 居中布局", "", centerLayout),widget.NewCard("Max 最⼤化布局", "", maxLayout),widget.NewCard("Stack 堆叠布局", "", stackLayout),widget.NewCard("Padded 内边距布局", "", paddedLayout),))myWin.SetContent(w)myWin.ShowAndRun()
}// 绝对布局练习
func fyne_absolute_layout() {myApp := app.New()myWin := myApp.NewWindow("绝对定位布局")myWin.Resize(fyne.NewSize(300, 200))// 绝对定位容器absolute := container.NewWithoutLayout(widget.NewButton("按钮1", nil),widget.NewButton("按钮2", nil),widget.NewLabel("标签"),)// ⼿动设置位置和⼤⼩// 容器里面的组件就按照你当初放的那样,进行一个数组访问即可// Move移动,Resize重置大小if len(absolute.Objects) >= 3 {absolute.Objects[0].Move(fyne.NewPos(10, 10))absolute.Objects[0].Resize(fyne.NewSize(100, 30))absolute.Objects[1].Move(fyne.NewPos(120, 10))absolute.Objects[1].Resize(fyne.NewSize(100, 30))absolute.Objects[2].Move(fyne.NewPos(10, 50))absolute.Objects[2].Resize(fyne.NewSize(210, 30))}w := widget.NewCard("绝对定位布局", "", absolute)myWin.SetContent(w)myWin.ShowAndRun()
}
func fyne_dialog_for_NewProgess() {myApp := app.New()myWindow := myApp.NewWindow("Determinate Progress Example")progressInfo := widget.NewLabel("准备开始...")button := widget.NewButton("开始任务", func() {// 创建进度对话框,初始进度0%progDialog := dialog.NewProgress("处理中", "正在进行一项耗时的确定性任务...", myWindow)progDialog.Show()totalSteps := 100// 使用 fyne.Do 确保初始设置在主线程执行fyne.Do(func() {progDialog.SetValue(0) // 确保从0开始})// 模拟任务进度更新go func() {for i := 0; i <= totalSteps; i++ {currentI := i // 创建局部变量避免闭包问题currentProgress := float64(currentI) / float64(totalSteps)if currentI == totalSteps {time.Sleep(500 * time.Millisecond) // 最后稍作停顿// 使用 fyne.Do 包装UI更新fyne.Do(func() {progDialog.SetValue(1.0) // 完成时设置为1.0 (100%)progressInfo.SetText("任务完成!")})time.Sleep(500 * time.Millisecond)fyne.Do(func() {progDialog.Hide() // 完成任务后隐藏})return}// 使用 fyne.Do 包装所有UI更新操作fyne.Do(func() {progDialog.SetValue(currentProgress) // 更新进度 (0.0 到 1.0)progressInfo.SetText(fmt.Sprintf("进度: %d/%d", currentI, totalSteps))})time.Sleep(50 * time.Millisecond) // 模拟工作}}()})content := container.NewVBox(progressInfo, button)myWindow.SetContent(content)myWindow.Resize(fyne.NewSize(400, 200))myWindow.ShowAndRun()
}func fyne_dialog_for_NewProgessInfinite() {myApp := app.New()myWindow := myApp.NewWindow("Infinite Progress Example")statusLabel := widget.NewLabel("等待操作...")startButton := widget.NewButton("开始不确定任务", func() {// 创建无限进度对话框infiniteDialog := dialog.NewProgressInfinite("请等待", "正在进行一项不确定时间的任务...", myWindow)infiniteDialog.Show()fyne.Do(func() {statusLabel.SetText("任务进行中...")})// 模拟一个耗时不确定的任务go func() {time.Sleep(5 * time.Second) // 模拟工作,比如网络请求// 使用 fyne.Do 包装UI更新fyne.Do(func() {infiniteDialog.Hide() // 任务完成后隐藏对话框statusLabel.SetText("不确定任务已完成!")})}()})content := container.NewVBox(statusLabel, startButton)myWindow.SetContent(content)myWindow.Resize(fyne.NewSize(400, 200))myWindow.ShowAndRun()
}func test_Progess() {//这里是手动切换调用哪个的函数方法, 我在main中调用他这个函数即可// fyne_dialog_for_NewProgess()fyne_dialog_for_NewProgessInfinite()
}
func main() {// packet_myfont() //设置中文字体,不乱码// fyne_test()// basicwidget_test()// advancedWidgets_test()// time_clock()// fyne_canvas() //画布测试// fyne_toolbar() // 工具栏// fyne_Layouts() // 相对布局测试// fyne_absolute_layout() // 绝对布局测试test_Progess() //进度条测试
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/956962.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用FireDAC的TFDManager和TFDConnection组件实现运行时动态数据库连接

以下是一个通用的数据库连接方案,使用FireDAC的TFDManager和TFDConnection组件实现运行时动态数据库连接。支持多种数据库类型,采用面向对象封装:unit DatabaseManager;interfaceusesSystem.SysUtils, System.Class…

laas paas saas 架构说明

--------------------------------------------------------------------------------------------------------------------------- 在 Java 开发领域,IaaS、PaaS、SaaS 分别对应基础设施、平台、软件三个层级的服务…

Delphi RESTful API

服务器 第一步: 创建Delphi REST服务器(使用WebBroker或DataSnap,这里使用WebBroker)在 Delphi 中创建 "Stand-alone Web Server Application" 将 API 代码粘贴到 WebModule 中 设置端口(默认为 8080)…

TFDMemTable 是断开性数据集

TFDMemTable 是断开性数据集,数据存储在内存中,无需实时连接数据库它适用于缓存数据、离线操作、临时数据处理等场景 虽然 TFDMemTable 是断开性的,但它仍然可以通过 TFDQuery 等组件与数据库进行数据交互

2025年河南公共走廊全钢防火隔断公司权威推荐榜单:商场全钢防火隔断/公共走廊防火隔断/公共走廊防火隔墙源头厂家精选

在商业建筑、学校、医院等公共空间,全钢防火隔断作为保障人员安全疏散的关键设施,其防火性能与结构稳定性直接关系到生命财产安全。根据建筑防火行业统计,2025年国内防火建材市场规模预计突破800亿元,其中全钢防火…

2025年板式换热机组制造厂权威推荐榜单:容积式换热机组/热力机组/换热机组源头厂家精选

在工业节能改造与区域能源管理加速推进的背景下,板式换热机组凭借其高效传热、紧凑结构及智能控制等优势,已成为区域供暖、化工流程及建筑能源系统的核心装备。根据行业数据统计,2025年国内换热机组市场规模预计突破…

十四.JavaScript 简介及导入方式

十四.JavaScript 简介及导入方式 定义:JS导入方式:有两种 显示:Fn+F12-->Console内联(script)外联:创建.js文件-导入.js文件基本输入输出: 打印到控制窗口:console.log() 显示到下拉框:alert() 十五.Java…

2025 年 11 月音乐喷泉工程,景区互动音乐喷泉,彩色灯光音乐喷泉厂家最新推荐,聚焦资质、案例、售后的五家机构深度解读!

引言 近期,专业景观行业协会针对音乐喷泉工程、景区互动音乐喷泉、彩色灯光音乐喷泉领域开展了 2025 年度厂家测评工作,本次测评覆盖行业内百余家主流企业,通过资质审核、技术实力评估、项目案例考察、售后服务调研…

11.5 NOIP 模拟赛 T1. 荣耀

思路 \(f(n)\): \(n\) 个位置中 \(0\) 号勋章出现偶数次的方案数 \(g(n)\): \(n\) 个位置中 \(0\) 号勋章出现奇数次的方案数 \[f(n) = m \cdot f(n-1) + g(n-1) \\ g(n) = f(n-1) + m \cdot g(n-1) \]可以 \[\begin{b…

智能体自动化 ui 测试

智能体自动化 ui 测试

Windows开机自动播放视频设置

打开计划任务程序 有两种方法打开输入命令 运行输入taskschd.msc开始菜单 Windows管理工具-计划任务程序设置开机任务 创建任务设置任务名称设置触发器 需设置延迟任务时间否则可能开机页面闪一下但是不启动设置操作启…

快速创建模拟 REST API

JSON Server 是一个用于快速创建模拟 REST API 的工具,主要用在前端开发和原型设计中。 主要特点 零编码:只需一个 JSON 文件即可创建完整的 API RESTful:自动提供完整的 REST API 端点 真实 HTTP:使用真实的 HTTP…

CSS简介及导入方式

简介:css语法:选择器放在head中间 CSS三种导入方式:九、CSS选择器 定义、类型:1-4:5-6:7-8:伪元素选择器 十、CSS常用属性 font:字体的复合属性 line-height:行高 行内块元素:十一、盒子模型 概念:实现:十二…

2025 年 11 月倍捻机,直捻机,大卷装倍捻机厂家最新推荐,实力品牌深度解析采购无忧之选!

引言 近期,行业权威协会针对倍捻机、直捻机、大卷装倍捻机领域开展了全面测评,测评覆盖近 50 家主流厂家,从技术参数、生产稳定性、节能表现、售后服务等 12 项核心指标进行量化打分,最终筛选出综合实力突出的品牌…

皮试

皮试结果

使用rsync及inotify实现两台Linux设备间的文件夹同步

一、背景 环境 由于业务需要,软件需要在CentOS 6.10 32位环境中进行编译。由于系统版本较老,无法实现使用vscode远程连接到CentOS上进行软件开发。为了仍能享受到vscode的便利,现借助SUSE作为vscode远程连接的开发环…

教育部等七部门关于加强中小学科技教育的意见-解读

这份《教育部等七部门关于加强中小学科技教育的意见》(教基〔2025〕7号)是国家层面推动中小学科技教育系统化、高质量发展的纲领性文件,体现了国家对科技创新人才培养的高度重视和战略布局。作为青少年编程教育科技…

2025年酒柜定制厂排名:酒柜定制选择哪家好?

葡萄酒收藏已从小众爱好演变为彰显品味的生活方式,而专业酒柜则是守护美酒价值的核心载体。面对市场上良莠不齐的酒柜定制服务,如何找到真正靠谱的酒柜定制厂?本文基于行业调研与用户反馈,精选2025年十大酒柜定制厂…

2025 年 11 月智能倍捻机,节能倍捻机,高速大卷装倍捻机厂家最新推荐,实力品牌深度解析采购无忧之选!

引言 当前纺机行业对智能倍捻机、节能倍捻机、高速大卷装倍捻机的需求持续攀升,为帮助企业精准选择优质设备,行业权威协会开展专项测评,从技术参数、生产效率、能耗表现、售后服务等 8 大维度,对 50 余家厂家的设备…

AT_arc188_d [ARC188D] Mirror and Order

题目大意 我们称两个长度为 \(n\) 的数组所构成的数组对 \((a, b)\) 是合法的当且仅当其能够满足以下构造:构造 \(n\) 个长度为 \(3\) 且对应每一位上都不重复的使用了 \(1 \sim n\) 中的元素的数组 \(s_i\),我们令第…