fyne编写简单桌面应用
作者:互联网
应用的效果图
fyne项目解决问题
1. 中文字体
// 设置系统环境的字体常量
os.Setenv("FYNE_FONT", path)
2. 动态绑定
//先用string切片, string中##连接属性,再分割成struct
var vmStrList binding.ExternalStringList
vmStrList = binding.BindStringList(&[]string{})
项目主要代码
main.go
package main
import (
"image/color"
"log"
"os"
"runtime"
"strings"
"time"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/app"
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/data/binding"
"fyne.io/fyne/v2/dialog"
"fyne.io/fyne/v2/driver/desktop"
"fyne.io/fyne/v2/layout"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
"github.com/flopp/go-findfont"
"github.com/go-vgo/robotgo"
"ovirtClient/application"
)
var logger *log.Logger
var colorDanger = color.NRGBA{207, 19, 34, 255}
var colorSuccess = color.NRGBA{56, 158, 13, 255}
var colorFail = color.NRGBA{144, 147, 153, 255}
var colorWarning = color.NRGBA{212, 136, 6, 255}
func init() {
// 设置中文字体
fontPaths := findfont.List()
for _, path := range fontPaths {
if strings.Contains(path, "cangerw02.ttf") || strings.Contains(path, "simhei.ttf") || strings.Contains(path, "simsun.ttc") || strings.Contains(path, "simkai.ttf") {
os.Setenv("FYNE_FONT", path)
break
}
}
// 日志文件
file := "./vmclient" + time.Now().Format("20180102") + ".log"
logFile, err := os.OpenFile(file, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0766)
if err != nil {
panic(err)
}
logger = log.New(logFile, "", log.LstdFlags)
}
func main() {
app := app.New()
// 自定义主题
// app.Settings().SetTheme(&ThemeDefault{})
themeDiy := theme.LightTheme()
app.Settings().SetTheme(themeDiy)
// 窗口设置
var window fyne.Window
if runtime.GOOS == "linux" {
drv, _ := app.Driver().(desktop.Driver)
window = drv.CreateSplashWindow()
window.SetTitle("ovirtClient-v1.0.0")
// 屏幕最大值
width, height := robotgo.GetScreenSize()
window.Resize(fyne.NewSize(float32(width), float32(height-1)))
} else {
window = app.NewWindow("ovirtClient-v1.0.0")
}
window.SetIcon(resourceStaticAppPng)
// 数据预处理
// 获取本地登录信息
strUsername := binding.NewString()
strPassword := binding.NewString()
strDatacenter := binding.NewString()
resultLoginInfo := application.LoginInfoGet()
// ######################虚拟机选择#######################
var vmStrList binding.ExternalStringList
vmStrList = binding.BindStringList(&[]string{})
// 用户自动登录
if resultLoginInfo.ErrorCode <= 0 && resultLoginInfo.Info.Username != "" {
vmListResult := application.VmList()
logger.Println("自动登录vmList=", vmListResult.VmList)
vmStrList.Set([]string{})
for _, vmInfo := range vmListResult.VmList {
vmStrList.Append(vmInfo.Name + "##" + vmInfo.System + "##" + vmInfo.Status + "##" + vmInfo.Description)
}
} else {
vmStrList.Append("##" + "##no_login##")
}
// 系统icon
// imgWin7 := canvas.NewImageFromResource(resourceWindows7Png)
// imgWin7.FillMode = canvas.ImageFillContain
// imgWin7.SetMinSize(fyne.NewSize(26, 26))
// imgWin10 := canvas.NewImageFromResource(resourceWindows10Png)
// imgWin10.FillMode = canvas.ImageFillContain
// imgWin10.SetMinSize(fyne.NewSize(26, 26))
// vmList
vmListDiv := widget.NewListWithData(vmStrList,
func() fyne.CanvasObject {
return container.New(layout.NewGridLayout(5),
widget.NewLabelWithStyle("主机名", fyne.TextAlignCenter, fyne.TextStyle{}),
widget.NewLabelWithStyle("系统", fyne.TextAlignCenter, fyne.TextStyle{}),
canvas.NewText("状态", nil),
container.NewHBox(
widget.NewButton("重启", nil),
widget.NewButton("关机", nil),
widget.NewButton("开机", nil)),
container.NewCenter(
widget.NewButton("登录主机", nil)),
)
},
func(item binding.DataItem, obj fyne.CanvasObject) {
vmStr, _ := item.(binding.String).Get()
vmArr := strings.Split(vmStr, "##")
// 主机名
labelHostName := obj.(*fyne.Container).Objects[0].(*widget.Label)
if len(vmArr[0]) > 14 {
nameRune := []rune(vmArr[0])
labelHostName.SetText(string(nameRune[:14]))
} else {
labelHostName.SetText(vmArr[0])
}
// 系统
obj.(*fyne.Container).Objects[1].(*widget.Label).SetText(vmArr[1])
// 状态
labelVmStatus := obj.(*fyne.Container).Objects[2].(*canvas.Text)
labelVmStatus.TextSize = 16
labelVmStatus.TextStyle = fyne.TextStyle{
Bold: true,
}
var vmStatus string
switch vmArr[2] {
case "up":
vmStatus = "已开机"
labelVmStatus.Color = colorSuccess
case "down":
vmStatus = "已关机"
labelVmStatus.Color = colorFail
case "wait_for_launch":
vmStatus = "准备中..."
labelVmStatus.Color = colorWarning
case "powering_up":
vmStatus = "开机中..."
labelVmStatus.Color = colorDanger
case "reboot_in_progress":
vmStatus = "重启中..."
labelVmStatus.Color = colorDanger
case "no_vm":
vmStatus = "没有主机记录"
labelVmStatus.Color = colorWarning
case "no_login":
vmStatus = "请先登录账号"
}
labelVmStatus.Text = vmStatus
// 操作按钮
btnReboot := obj.(*fyne.Container).Objects[3].(*fyne.Container).Objects[0].(*widget.Button)
btnShutdown := obj.(*fyne.Container).Objects[3].(*fyne.Container).Objects[1].(*widget.Button)
btnStart := obj.(*fyne.Container).Objects[3].(*fyne.Container).Objects[2].(*widget.Button)
btnConnect := obj.(*fyne.Container).Objects[4].(*fyne.Container).Objects[0].(*widget.Button)
if vmArr[2] == "down" {
btnReboot.Show()
btnShutdown.Show()
btnStart.Show()
btnConnect.Show()
btnReboot.Disable()
btnShutdown.Disable()
btnConnect.Disable()
// 开机
btnStart.Enable()
btnStart.OnTapped = func() {
startResult := application.VmStart(vmArr[0])
dialog.ShowInformation("", startResult.Message, window)
if startResult.ErrorCode <= 0 {
go application.TimerTask(5*time.Second, 20, func() {
vmListRes := application.VmList()
vmStrList.Set([]string{})
for _, vmInfo := range vmListRes.VmList {
vmStrList.Append(vmInfo.Name + "##" + vmInfo.System + "##" + vmInfo.Status + "##" + vmInfo.Description)
}
// logger.Println("定时刷新vmlist=", vmListRes.VmList)
})
}
}
} else if vmArr[2] == "up" {
btnReboot.Show()
btnShutdown.Show()
btnStart.Show()
btnConnect.Show()
btnStart.Disable()
// 重启
btnReboot := obj.(*fyne.Container).Objects[3].(*fyne.Container).Objects[0].(*widget.Button)
btnReboot.Enable()
btnReboot.OnTapped = func() {
cnf := dialog.NewConfirm("", "确认重启吗?", func(b bool) {
if b != true {
return
}
rebootResult := application.VmReboot(vmArr[0])
dialog.ShowInformation("", rebootResult.Message, window)
if rebootResult.ErrorCode <= 0 {
go application.TimerTask(5*time.Second, 20, func() {
vmListRes := application.VmList()
vmStrList.Set([]string{})
for _, vmInfo := range vmListRes.VmList {
vmStrList.Append(vmInfo.Name + "##" + vmInfo.System + "##" + vmInfo.Status + "##" + vmInfo.Description)
}
// logger.Println("定时刷新vmlist=", vmListRes.VmList)
})
}
}, window)
cnf.SetDismissText("取消")
cnf.SetConfirmText("确认")
cnf.Show()
}
btnShutdown.Enable()
btnShutdown.OnTapped = func() {
cnf := dialog.NewConfirm("", "确认关机吗?", func(b bool) {
rebootResult := application.VmShutdown(vmArr[0])
dialog.ShowInformation("", rebootResult.Message, window)
if rebootResult.ErrorCode <= 0 {
go application.TimerTask(5*time.Second, 20, func() {
vmListRes := application.VmList()
vmStrList.Set([]string{})
for _, vmInfo := range vmListRes.VmList {
vmStrList.Append(vmInfo.Name + "##" + vmInfo.System + "##" + vmInfo.Status + "##" + vmInfo.Description)
}
// logger.Println("定时刷新vmlist=", vmListRes.VmList)
})
}
}, window)
cnf.SetDismissText("取消")
cnf.SetConfirmText("确认")
cnf.Show()
}
// 登录
btnConnect := obj.(*fyne.Container).Objects[4].(*fyne.Container).Objects[0].(*widget.Button)
btnConnect.Enable()
btnConnect.OnTapped = func() {
// dialog.ShowInformation("", "登录中...", window)
connectResult := application.VmConnect(vmArr[0])
if connectResult.ErrorCode > 0 {
dialog.ShowInformation("", connectResult.Message, window)
}
}
} else if vmArr[2] == "no_login" || vmArr[2] == "no_vm" {
btnReboot.Hide()
btnShutdown.Hide()
btnStart.Hide()
btnConnect.Hide()
} else {
btnReboot.Show()
btnShutdown.Show()
btnStart.Show()
btnConnect.Show()
btnReboot.Disable()
btnShutdown.Disable()
btnStart.Disable()
btnConnect.Disable()
}
})
vmListDivContain := container.New(layout.NewGridWrapLayout(fyne.NewSize(800, 560)), vmListDiv)
vmListTopBtn := widget.NewButton("刷新", func() {
vmListResult := application.VmList()
logger.Println("手动刷新vmlistResult=", vmListResult)
if vmListResult.ErrorCode > 0 {
dialog.ShowInformation("", vmListResult.Message, window)
return
}
vmStrList.Set([]string{})
for _, vmInfo := range vmListResult.VmList {
vmStrList.Append(vmInfo.Name + "##" + vmInfo.System + "##" + vmInfo.Status + "##" + vmInfo.Description)
}
logger.Println("手动刷新vmlist=", vmListResult.VmList)
})
vmListTopContain := container.New(layout.NewHBoxLayout(), layout.NewSpacer(), widget.NewLabel("主机列表"), vmListTopBtn, layout.NewSpacer())
vmListTitleContain := container.New(layout.NewGridLayout(5),
widget.NewLabelWithStyle("主机名", fyne.TextAlignCenter, fyne.TextStyle{Bold: true}),
widget.NewLabelWithStyle("系统", fyne.TextAlignCenter, fyne.TextStyle{Bold: true}),
widget.NewLabelWithStyle("状态", fyne.TextAlignLeading, fyne.TextStyle{Bold: true}),
widget.NewLabelWithStyle("操作", fyne.TextAlignCenter, fyne.TextStyle{Bold: true}),
widget.NewLabelWithStyle("登录", fyne.TextAlignCenter, fyne.TextStyle{Bold: true}),
)
vmListWrap := container.New(layout.NewVBoxLayout(), vmListTopContain, vmListTitleContain, vmListDivContain)
// tabs.Append(container.NewTabItemWithIcon("主机", theme.ComputerIcon(), vmListWrap))
// ######################登录页面#######################
// 自动填充
if resultLoginInfo.ErrorCode <= 0 && resultLoginInfo.Info.Username != "" {
strUsername.Set(resultLoginInfo.Info.Username)
strPassword.Set(resultLoginInfo.Info.Password)
strDatacenter.Set(resultLoginInfo.Info.Datacenter)
}
entryUsername := widget.NewEntryWithData(strUsername)
entryUsername.SetPlaceHolder("请输入账号")
entryPassword := widget.NewEntryWithData(strPassword)
entryPassword.SetPlaceHolder("请输入密码")
entryDatacenter := widget.NewEntryWithData(strDatacenter)
entryDatacenter.SetPlaceHolder("请输入地址,如192.168.0.246:443")
formLogin := &widget.Form{
BaseWidget: widget.BaseWidget{},
Items: []*widget.FormItem{
{Text: "登录账号: ", Widget: entryUsername},
{Text: "登录密码: ", Widget: entryPassword},
{Text: "平台地址: ", Widget: entryDatacenter},
},
OnSubmit: func() { // optional, handle form submission
// 检查
if len(entryUsername.Text) == 0 || len(entryPassword.Text) == 0 {
dialog.ShowInformation("", "请输入账号和密码", window)
return
}
if len(entryDatacenter.Text) == 0 {
dialog.ShowInformation("", "请输入平台地址", window)
return
}
loginResult := application.Login(entryUsername.Text, entryPassword.Text, entryDatacenter.Text)
logger.Println("首次登录loginResult=", loginResult)
dialog.ShowInformation("", loginResult.Message, window)
if loginResult.ErrorCode > 0 {
return
}
vmListResult := application.VmList()
logger.Println("首次登录vmList=", vmListResult.VmList)
vmStrList.Set([]string{})
for _, vmInfo := range vmListResult.VmList {
vmStrList.Append(vmInfo.Name + "##" + vmInfo.System + "##" + vmInfo.Status + "##" + vmInfo.Description)
}
},
OnCancel: nil,
SubmitText: "保存登录账号",
CancelText: "",
}
// 测试平台
entryIp := widget.NewEntry()
entryIp.SetPlaceHolder("请输入IP, 如192.168.0.246")
entryPort := widget.NewEntry()
entryPort.SetPlaceHolder("请输入端口号, 默认443")
formDatacenter := &widget.Form{
Items: []*widget.FormItem{
{Text: "平台的IP: ", Widget: entryIp},
{Text: "平台端口: ", Widget: entryPort},
},
SubmitText: "连接平台测试",
OnSubmit: func() { // optional, handle form submission
addResult := application.DatacenterTest(entryIp.Text, entryPort.Text)
dialog.ShowInformation("", addResult.Message, window)
},
}
datacenterTopContain := container.New(layout.NewHBoxLayout(), layout.NewSpacer(), widget.NewLabel("平台连接测试"), layout.NewSpacer())
datacenterAddDiv := container.New(layout.NewGridWrapLayout(fyne.NewSize(380, 100)), formDatacenter)
datacenterWrap := container.New(layout.NewHBoxLayout(), layout.NewSpacer(), datacenterAddDiv, layout.NewSpacer())
formLoginDiv := container.New(layout.NewGridWrapLayout(fyne.NewSize(380, 230)), formLogin)
formLoginWrap := container.New(layout.NewHBoxLayout(), layout.NewSpacer(), formLoginDiv, layout.NewSpacer())
loginTopContain := container.New(layout.NewHBoxLayout(), layout.NewSpacer(), widget.NewLabel("登录账号设置"), layout.NewSpacer())
loginWrap := container.New(layout.NewVBoxLayout(), loginTopContain, formLoginWrap, datacenterTopContain, datacenterWrap)
// tabs.Append(container.NewTabItemWithIcon("登录", theme.AccountIcon(), loginWrap))
// ######################设置#######################
// settingTopContain := container.New(layout.NewHBoxLayout(), layout.NewSpacer(), widget.NewLabel("设置"), layout.NewSpacer())
// settingWrap := container.New(layout.NewVBoxLayout(), settingTopContain)
// tabs.Append(container.NewTabItemWithIcon("设置", theme.SettingsIcon(), settingWrap))
// ######################关于#######################
aboutDiv := container.New(layout.NewHBoxLayout(), widget.NewLabel("适用于oVirt4.x"), widget.NewLabel("版本: v1.0.0-20220707"), widget.NewLabel("微信: timeispower7"))
aboutTopContain := container.New(layout.NewHBoxLayout(), layout.NewSpacer(), widget.NewLabel("关于"), layout.NewSpacer())
aboutWrap := container.New(layout.NewVBoxLayout(), aboutTopContain, aboutDiv)
// 窗口内容
// 选项卡
tabs := container.NewAppTabs(
container.NewTabItemWithIcon("主机", theme.ComputerIcon(), vmListWrap),
container.NewTabItemWithIcon("登录", theme.AccountIcon(), loginWrap),
container.NewTabItemWithIcon("关于", theme.DocumentIcon(), aboutWrap),
)
if resultLoginInfo.ErrorCode > 0 {
tabs.SelectTabIndex(1)
}
tabs.SetTabLocation(container.TabLocationLeading)
tabsWithBorder := container.NewBorder(application.MakeCell(10), application.MakeCell(10), application.MakeCell(10), application.MakeCell(10), tabs)
tabsWithBg := container.New(layout.NewMaxLayout(), canvas.NewRectangle(&color.NRGBA{255, 255, 255, 255}), tabsWithBorder)
tabsWrap := container.NewCenter(tabsWithBg)
windowBgImg := canvas.NewImageFromResource(resourceStaticAppbgJpg)
windowContent := container.New(layout.NewMaxLayout(), windowBgImg, tabsWrap)
window.SetContent(windowContent)
// window.SetFullScreen(true)
window.ShowAndRun()
}
标签:widget,桌面,New,fyne,NewSpacer,编写,container,layout 来源: https://www.cnblogs.com/sentangle/p/16497726.html