Golang Fyne项目实战(含源码)
- 项目介绍
-
- 效果图
- 功能介绍
- 步骤介绍
- 代码区域
-
- 项目结构
- 下载fyne包以及Fyne工具包
- 追加静态资源,程序设置图标
- 完整代码
- 结尾
项目介绍
效果图
功能介绍
根据文本文件的内容,自动更名所选文件夹中的文件名称
步骤介绍
1.创建txt在内容程序界面中输入文本文件"目录文件"选择当前创建的文本文件 例:
2.选择需要批量更改名称的文件夹 3.选择路径后,程序单击"开始更名",自动更名 4.结果
代码区域
项目结构
下载fyne包以及Fyne工具包
命令: 1.go get fyne.io/fyne/v2 2.go get fyne.io/fyne/cmd/fyne
追加静态资源,程序设置图标
1.将静态资源编译成 go 文件 (打开终端执行自己的项目录):fyne bundle fav.png >> bundled.go 2. 打开 bundled.go 文件会看到变量 resourceFavPng 3.设置窗口图标,任务栏图标 ///主代码 a := app.New() a.SetIcon(resourceFavPng) //本地操作注释,否则报错,打包时打开此行代码
完整代码
package main import ( "YHSoft/Demo/GUIDemo/FyneDemo/02_RenameFileName/models" ////用自己的项目路径代替 "errors" "fmt" "fyne.io/fyne/v2" "fyne.io/fyne/v2/app" "fyne.io/fyne/v2/container" "fyne.io/fyne/v2/dialog" "fyne.io/fyne/v2/layout" "fyne.io/fyne/v2/storage" "fyne.io/fyne/v2/widget" "github.com/flopp/go-findfont" "io/ioutil" "log" "os" "path/filepath" "strconv" "strings" "time" "unicode" ) func main() {
///新建一个app a := app.New() //设置窗栏,任务栏图标 a.SetIcon(resourceIconPng) ///新窗口 w := a.NewWindow("自动更名程序V1.0") ///主界面框架布局 MainShow(w) //尺寸 w.Resize(fyne.Size{
Width: 500, Height: 100}) //w居中显示 w.CenterOnScreen() //循环运行 w.ShowAnRun()
err := os.Unsetenv("FYNE_FONT")
if err != nil {
return
}
}
var tileInfo string
var done = make(chan bool)
var stop = make(chan int, 1)
var num int64
// MainShow 主界面函数
func MainShow(w fyne.Window) {
//var ctrl *beep.Ctrl
title := widget.NewLabel("自动化更名程序")
hello := widget.NewLabel("目录文件:")
entry1 := widget.NewEntry() //文本输入框
//entry1.SetText("E:\\rename_temp2\\123.txt")
dia1 := widget.NewButton("打开", func() {
//回调函数:打开选择文件对话框
fd := dialog.NewFileOpen(func(reader fyne.URIReadCloser, err error) {
if err != nil {
dialog.ShowError(err, w)
return
}
if reader == nil {
log.Println("Cancelled")
return
}
entry1.SetText(reader.URI().Path()) //把读取到的路径显示到输入框中
}, w)
fd.SetFilter(storage.NewExtensionFileFilter([]string{
".txt"})) //打开的文件格式类型
fd.Show() //控制是否弹出选择文件目录对话框
})
text := widget.NewMultiLineEntry() //多行输入组件
//text.Disable() //禁用输入框,不能更改数据
labelLast := widget.NewLabel("发飙的蜗牛 ALL Right Reserved")
//labelLast := widget.NewLabel(" ")
label4 := widget.NewLabel("文件路径:")
entry2 := widget.NewEntry()
//entry2.SetText("E:\\rename_temp2\\宝岗路停车场项目幕墙施工图PDF")
dia2 := widget.NewButton("打开", func() {
dialog.ShowFolderOpen(func(list fyne.ListableURI, err error) {
if err != nil {
dialog.ShowError(err, w)
return
}
if list == nil {
log.Println("Cancelled")
return
}
//设置输入框内容
entry2.SetText(list.Path())
}, w)
})
//开始更名按钮
bt3 := widget.NewButton("开始 更名", func() {
go func() {
if (entry1.Text != "") && (entry2.Text != "") {
text.SetText("")
text.Refresh()
if num != 0 {
stop <- 1
return
} else {
err := generateTxt(entry1.Text, entry2.Text, text)
if err != nil {
dialog.ShowError(err, w)
}
text.Refresh()
}
} else {
dialog.ShowError(errors.New("读取TXT文件错误"), w)
}
}()
})
//停止更名按钮
bt4 := widget.NewButton("停止 更名", func() {
go func() {
done <- false
}()
})
head := container.NewCenter(title)
v1 := container.NewBorder(layout.NewSpacer(), layout.NewSpacer(), hello, dia1, entry1)
v4 := container.NewBorder(layout.NewSpacer(), layout.NewSpacer(), label4, dia2, entry2)
v5 := container.NewHBox(bt3, bt4)
v5Center := container.NewCenter(v5)
ctnt := container.NewVBox(head, v1, v4, v5Center, text, labelLast) //控制显示位置顺序
w.SetContent(ctnt)
}
//设置字体
func init() {
fontPaths := findfont.List()
for _, fontPath := range fontPaths {
//fmt.Println(fontPath)
//楷体:simkai.ttf
//黑体:simhei.ttf
//微软雅黑:msyh.ttc
if strings.Contains(fontPath, "simkai.ttf") {
err := os.Setenv("FYNE_FONT", fontPath)
if err != nil {
return
}
break
}
}
}
//读取数据校验数据
func generateTxt(inPath, outPath string, text *widget.Entry) error {
//标题
tileInfo += "开始处理,正在读取文件...\n"
text.SetText(tileInfo)
nameList,err := models.GetStrList(inPath)
if err != nil{
tileInfo += "读取文件出错...\n"
text.SetText(tileInfo)
}
//获取文件路径的文件
files, _ := ioutil.ReadDir(outPath)
var fileNameList []string
for _, file := range files {
// 带扩展名的文件名
fullFilename := file.Name()
//添加文件数据到切片中
fileNameList = append(fileNameList, fullFilename)
}
if len(nameList) == 0 || len(fileNameList) == 0{
tileInfo += "已停止处理...\n"
text.SetText(tileInfo) //设置多行显示控件中的内容
return errors.New("找不到路径或路径下不存在此文件")
}
if len(nameList) != len(fileNameList) {
tileInfo += "已停止处理...\n"
text.SetText(tileInfo) //设置多行显示控件中的内容
return errors.New("目录行数与文件行数不相等,请检查!")
}
renameFile(nameList, fileNameList, outPath, text)
return nil
}
//操作更名文件
func renameFile(nameList []string, fileNameList []string, outPath string, text *widget.Entry) {
var newName string
//遍历更改
for index, fullFilename := range fileNameList {
select {
case <-done: //读管道中内容,没有内容前,阻塞
//扩展名
fileExt := filepath.Ext(fullFilename)
//fmt.Println("nameList[index]=",nameList[index])
defaultName := nameList[index] //文本文件初始名称
var defaultNameCode []string
var defaultNameNewCode string
rune := []rune(defaultName)
for i := len(rune); i > 0; i-- {
if !unicode.Is(unicode.Han, rune[i-1]) {
defaultNameCode = append(defaultNameCode, string(rune[i-1]))
} else {
break
}
}
//重新编排名称编号
for i := len(defaultNameCode); i > 0; i-- {
defaultNameNewCode += defaultNameCode[i-1]
}
//fmt.Println("defaultName=", defaultName)
//重组新名称
newName = strconv.Itoa(index+1) + " " + strings.ReplaceAll(defaultName, defaultNameNewCode, "") + " " + defaultNameNewCode
// 不带扩展名的文件名
//filenameOnly := strings.TrimSuffix(fullFilename, fileExt)
//将每个文件名后面加上1,扩展名不变
err := os.Rename(outPath+`\`+fullFilename, outPath+`\`+newName+fileExt)
if err != nil {
fmt.Println("err=", err)
}
tileInfo += "处理数据文件:" + fullFilename + "\n"
time.Sleep(time.Second * 1)
text.SetText(tileInfo) //设置多行显示控件中的 内容
tileInfo += "停止更名...\n"
text.SetText(tileInfo) //设置多行显示控件中的内容
num = num + 1
<-stop //读管道中内容,没有内容前,阻塞
default:
//扩展名
fileExt := filepath.Ext(fullFilename)
//fmt.Println("nameList[index]=",nameList[index])
defaultName := nameList[index] //文本文件初始名称
var defaultNameCode []string
var defaultNameNewCode string
rune := []rune(defaultName)
for i := len(rune); i > 0; i-- {
if !unicode.Is(unicode.Han, rune[i-1]) {
defaultNameCode = append(defaultNameCode, string(rune[i-1]))
} else {
break
}
}
//重新编排名称编号
for i := len(defaultNameCode); i > 0; i-- {
defaultNameNewCode += defaultNameCode[i-1]
}
//fmt.Println("defaultName=", defaultName)
//重组新名称
newName = strconv.Itoa(index+1) + " " + strings.ReplaceAll(defaultName, defaultNameNewCode, "") + " " + defaultNameNewCode
// 不带扩展名的文件名
//filenameOnly := strings.TrimSuffix(fullFilename, fileExt)
//将每个文件名后面加上1,扩展名不变
err := os.Rename(outPath+`\`+fullFilename, outPath+`\`+newName+fileExt)
if err != nil {
fmt.Println("err=", err)
}
tileInfo += "处理数据文件:" + fullFilename + "\n"
//time.Sleep(time.Second * 1)
text.SetText(tileInfo) //设置多行显示控件中的 内容
}
}
tileInfo += "处理完毕!"
text.SetText(tileInfo) //设置多行显示控件中的内容
num = 0
}
package models
import (
"bufio"
"fmt"
"github.com/axgle/mahonia"
"io"
"os"
"strings"
)
//读取txt文件,自动判断编码格式
func GetStrList(path string)([]string,error){
f, err := os.Open(path) //打开目录路径txt文件
if err != nil {
return nil, err
}
defer f.Close() //最后关闭文件
r := bufio.NewReader(f)
buf := make([]byte, 1024)
var res string
_, err = r.Read(buf)
if err != nil && err != io.EOF {
return nil,err
}
res = GetStrCoding(buf)
if res == "UTF8" {
return GetTextContentUTF8(path),nil
} else {
return GetTextContentGbk(path),nil
}
}
//读取gbk编码格式的文件
func GetTextContentGbk(txtPath string) []string {
f, err := os.Open(txtPath) //打开目录路径txt文件
if err != nil {
fmt.Println("err=", err)
}
defer f.Close()
decoder := mahonia.NewDecoder("gbk")
r := bufio.NewReader(decoder.NewReader(f))
chunks := []byte{
}
buf := make([]byte, 1024)
for {
n, err := r.Read(buf)
if err != nil && err != io.EOF {
panic(err)
}
if 0 == n {
break
}
chunks = append(chunks, buf[:n]...)
}
nameStr := strings.ReplaceAll(string(chunks), "\r\n", ",")
return strings.Split(nameStr, ",")
}
//读取utf8编码格式的文件
func GetTextContentUTF8(txtPath string) []string {
f, err := os.Open(txtPath) //打开目录路径txt文件
if err != nil {
fmt.Println("err=", err)
}
defer f.Close()
r := bufio.NewReader(f)
chunks := []byte{
}
buf := make([]byte, 1024)
for {
n, err := r.Read(buf)
if err != nil && err != io.EOF {
panic(err)
}
if 0 == n {
break
}
chunks = append(chunks, buf[:n]...)
}
nameStr := strings.ReplaceAll(string(chunks), "\r\n", ",")
return strings.Split(nameStr, ",")
}
const (
GBK string = "GBK"
UTF8 string = "UTF8"
UNKNOWN string = "UNKNOWN"
)
//判断文本文件格式
func GetStrCoding(data []byte) string {
if isUtf8(data) == true {
return UTF8
} else if isGBK(data) == true {
return GBK
} else {
return UNKNOWN
}
}
func isGBK(data []byte) bool {
length := len(data)
var i = 0
for i < length {
if data[i] <= 0x7f {
//编码0~127,只有一个字节的编码,兼容ASCII码
i++
continue
} else {
//大于127的使用双字节编码,落在gbk编码范围内的字符
if data[i] >= 0x81 &&
data[i] <= 0xfe &&
data[i+1] >= 0x40 &&
data[i+1] <= 0xfe &&
data[i+1] != 0xf7 {
i += 2
continue
} else {
return false
}
}
}
return true
}
func preNUm(data byte) int {
var mask byte = 0x80
var num int = 0
//8bit中首个0bit前有多少个1bits
for i := 0; i < 8; i++ {
if (data & mask) == mask {
num++
mask = mask >> 1
} else {
break
}
}
return num
}
func isUtf8(data []byte) bool {
i := 0
for i < len(data) {
if (data[i] & 0x80) == 0x00 {
// 0XXX_XXXX
i++
continue
} else if num := preNUm(data[i]); num > 2 {
// 110X_XXXX 10XX_XXXX
// 1110_XXXX 10XX_XXXX 10XX_XXXX
// 1111_0XXX 10XX_XXXX 10XX_XXXX 10XX_XXXX
// 1111_10XX 10XX_XXXX 10XX_XXXX 10XX_XXXX 10XX_XXXX
// 1111_110X 10XX_XXXX 10XX_XXXX 10XX_XXXX 10XX_XXXX 10XX_XXXX
// preNUm() 返回首个字节的8个bits中首个0bit前面1bit的个数,该数量也是该字符所使用的字节数
i++
for j := 0; j < num-1; j++ {
//判断后面的 num - 1 个字节是不是都是10开头
if (data[i] & 0xc0) != 0x80 {
return false
}
i++
}
} else {
//其他情况说明不是utf-8
return false
}
}
return true
}
结尾
代码可能不是最完美的,但是能实现想要的功能,也感谢各路大神的项目案列,也让自己有了相关代码的参考! 取之于民,用之于民!最后也欢迎各位的指教,共同交流进步!