您现在的位置是:首页 >学无止境 >Go GUI项目实战:基于fyne开发简易MarkDown网站首页学无止境
Go GUI项目实战:基于fyne开发简易MarkDown
简介Go GUI项目实战:基于fyne开发简易MarkDown
Go GUI项目实战:基于fyne开发简易MarkDown
fyne是一个用go编写的GUI框架,它上手简单,语法清晰,界面美观。
教程地址:
- https://pkg.go.dev/fyne.io/fyne/v2#section-readme
- https://www.topgoer.cn/docs/goday/goday-1crdp17nj4v6p
项目地址
:https://gitee.com/Zifasdfa/ziyiMarkDown
1 基础框架搭建
首先需要有go的环境【我的本地环境是1.17】
- go环境搭建过程:https://editor.csdn.net/md/?articleId=130175889
package main
import (
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/app"
"fyne.io/fyne/v2/container"
_ "fyne.io/fyne/v2/dialog"
_ "fyne.io/fyne/v2/storage"
"fyne.io/fyne/v2/widget"
"github.com/flopp/go-findfont"
_ "io/ioutil"
"os"
"strings"
)
/*
用fyne实现简易版typora
*/
func init() {
//设置中文字体:解决中文乱码问题
fontPaths := findfont.List()
for _, path := range fontPaths {
if strings.Contains(path, "msyh.ttf") || strings.Contains(path, "simhei.ttf") || strings.Contains(path, "simsun.ttc") || strings.Contains(path, "simkai.ttf") {
os.Setenv("FYNE_FONT", path)
break
}
}
}
type config struct {
EditWidget *widget.Entry //编辑框
PreviewWidget *widget.RichText //预览框【富文本】
CurrentFile fyne.URI
SaveMenuItem *fyne.MenuItem
}
var cfg config
func main() {
a := app.New()
win := a.NewWindow("Markdown")
edit, preview := cfg.makeUI()
cfg.createMenuItems(win)
//布局选择左右布局,一边是编辑,一边是预览
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) {
edit := widget.NewMultiLineEntry()
preview := widget.NewRichTextFromMarkdown("")
app.EditWidget = edit
app.PreviewWidget = preview
edit.OnChanged = preview.ParseMarkdown
return edit, preview
}
添加过滤器,只能打开.md结尾文件
//var filter = storage.NewExtensionFileFilter([]string{".md", ".MD"})
//
//创建主菜单
func (app *config) createMenuItems(win fyne.Window) {
openMenuItem := fyne.NewMenuItem("open...", func() {})
saveMenuItem := fyne.NewMenuItem("Save...", func() {})
app.SaveMenuItem = saveMenuItem
app.SaveMenuItem.Disabled = true
saveAsMenuItem := fyne.NewMenuItem("Save as...", func() {})
fileMenu := fyne.NewMenu("File", openMenuItem, saveMenuItem, saveAsMenuItem)
menu := fyne.NewMainMenu(fileMenu)
win.SetMainMenu(menu)
}
2 实现另存为功能
- 添加过滤器
- 实现另存为
package main
import (
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/app"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/dialog"
"fyne.io/fyne/v2/storage"
"fyne.io/fyne/v2/widget"
"github.com/flopp/go-findfont"
_ "io/ioutil"
"os"
"strings"
)
/*
用fyne实现简易版typora
*/
func init() {
//设置中文字体:解决中文乱码问题
fontPaths := findfont.List()
for _, path := range fontPaths {
if strings.Contains(path, "msyh.ttf") || strings.Contains(path, "simhei.ttf") || strings.Contains(path, "simsun.ttc") || strings.Contains(path, "simkai.ttf") {
os.Setenv("FYNE_FONT", path)
break
}
}
}
type config struct {
EditWidget *widget.Entry //编辑框
PreviewWidget *widget.RichText //预览框【富文本】
CurrentFile fyne.URI
SaveMenuItem *fyne.MenuItem
}
var cfg config
func main() {
a := app.New()
win := a.NewWindow("Markdown")
edit, preview := cfg.makeUI()
cfg.createMenuItems(win)
//布局选择左右布局,一边是编辑,一边是预览
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) {
edit := widget.NewMultiLineEntry()
preview := widget.NewRichTextFromMarkdown("")
app.EditWidget = edit
app.PreviewWidget = preview
edit.OnChanged = preview.ParseMarkdown
return edit, preview
}
//添加过滤器,只能打开.md结尾文件
var filter = storage.NewExtensionFileFilter([]string{".md", ".MD"})
//创建主菜单
func (app *config) createMenuItems(win fyne.Window) {
openMenuItem := fyne.NewMenuItem("open...", func() {})
//openMenuItem := fyne.NewMenuItem("open...", app.openFunc(win))
saveMenuItem := fyne.NewMenuItem("Save...", func() {})
//saveMenuItem := fyne.NewMenuItem("Save...", app.saveFunc(win))
app.SaveMenuItem = saveMenuItem
app.SaveMenuItem.Disabled = true
//另存为
saveAsMenuItem := fyne.NewMenuItem("Save as...", app.saveAsFunc(win))
fileMenu := fyne.NewMenu("File", openMenuItem, saveMenuItem, saveAsMenuItem)
menu := fyne.NewMainMenu(fileMenu)
win.SetMainMenu(menu)
}
//【另存为】功能实现
func (app *config) saveAsFunc(win fyne.Window) func() {
return func() {
saveDialog := dialog.NewFileSave(func(write fyne.URIWriteCloser, err error) {
if err != nil {
dialog.ShowError(err, win)
return
}
if write == nil {
//user canceled
return
}
if !strings.HasPrefix(strings.ToLower(write.URI().String()), ".md") {
dialog.ShowInformation("Error", "Please name your file with a .md extension!", win)
return
}
//save file
write.Write([]byte(app.EditWidget.Text))
app.CurrentFile = write.URI()
defer write.Close()
win.SetTitle(win.Title() + "-" + write.URI().Name())
app.SaveMenuItem.Disabled = false
}, win)
saveDialog.SetFileName("untitled.md")
saveDialog.SetFilter(filter)
saveDialog.Show()
}
}
3 实现打开文件功能
package main
import (
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/app"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/dialog"
"fyne.io/fyne/v2/storage"
"fyne.io/fyne/v2/widget"
"github.com/flopp/go-findfont"
"io/ioutil"
"os"
"strings"
)
/*
用fyne实现简易版typora
*/
func init() {
//设置中文字体:解决中文乱码问题
fontPaths := findfont.List()
for _, path := range fontPaths {
if strings.Contains(path, "msyh.ttf") || strings.Contains(path, "simhei.ttf") || strings.Contains(path, "simsun.ttc") || strings.Contains(path, "simkai.ttf") {
os.Setenv("FYNE_FONT", path)
break
}
}
}
type config struct {
EditWidget *widget.Entry //编辑框
PreviewWidget *widget.RichText //预览框【富文本】
CurrentFile fyne.URI
SaveMenuItem *fyne.MenuItem
}
var cfg config
func main() {
a := app.New()
win := a.NewWindow("Markdown")
edit, preview := cfg.makeUI()
cfg.createMenuItems(win)
//布局选择左右布局,一边是编辑,一边是预览
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) {
edit := widget.NewMultiLineEntry()
preview := widget.NewRichTextFromMarkdown("")
app.EditWidget = edit
app.PreviewWidget = preview
edit.OnChanged = preview.ParseMarkdown
return edit, preview
}
//添加过滤器,只能打开.md结尾文件
var filter = storage.NewExtensionFileFilter([]string{".md", ".MD"})
//创建主菜单
func (app *config) createMenuItems(win fyne.Window) {
//打开文件
openMenuItem := fyne.NewMenuItem("open...", app.openFunc(win))
saveMenuItem := fyne.NewMenuItem("Save...", func() {})
//saveMenuItem := fyne.NewMenuItem("Save...", app.saveFunc(win))
app.SaveMenuItem = saveMenuItem
app.SaveMenuItem.Disabled = true
//另存为
saveAsMenuItem := fyne.NewMenuItem("Save as...", app.saveAsFunc(win))
fileMenu := fyne.NewMenu("File", openMenuItem, saveMenuItem, saveAsMenuItem)
menu := fyne.NewMainMenu(fileMenu)
win.SetMainMenu(menu)
}
//【打开文件】功能
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)
//添加.md过滤器
openDialog.SetFilter(filter)
openDialog.Show()
}
}
//【另存为】功能实现
func (app *config) saveAsFunc(win fyne.Window) func() {
return func() {
saveDialog := dialog.NewFileSave(func(write fyne.URIWriteCloser, err error) {
if err != nil {
dialog.ShowError(err, win)
return
}
if write == nil {
//user canceled
return
}
if !strings.HasPrefix(strings.ToLower(write.URI().String()), ".md") {
dialog.ShowInformation("Error", "Please name your file with a .md extension!", win)
return
}
//save file
write.Write([]byte(app.EditWidget.Text))
app.CurrentFile = write.URI()
defer write.Close()
win.SetTitle(win.Title() + "-" + write.URI().Name())
app.SaveMenuItem.Disabled = false
}, win)
saveDialog.SetFileName("untitled.md")
saveDialog.SetFilter(filter)
saveDialog.Show()
}
}
4 实现保存功能
package main
import (
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/app"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/dialog"
"fyne.io/fyne/v2/storage"
"fyne.io/fyne/v2/widget"
"github.com/flopp/go-findfont"
"io/ioutil"
"os"
"strings"
)
/*
用fyne实现简易版typora
*/
func init() {
//设置中文字体:解决中文乱码问题
fontPaths := findfont.List()
for _, path := range fontPaths {
if strings.Contains(path, "msyh.ttf") || strings.Contains(path, "simhei.ttf") || strings.Contains(path, "simsun.ttc") || strings.Contains(path, "simkai.ttf") {
os.Setenv("FYNE_FONT", path)
break
}
}
}
type config struct {
EditWidget *widget.Entry //编辑框
PreviewWidget *widget.RichText //预览框【富文本】
CurrentFile fyne.URI
SaveMenuItem *fyne.MenuItem
}
var cfg config
func main() {
a := app.New()
win := a.NewWindow("Markdown")
edit, preview := cfg.makeUI()
cfg.createMenuItems(win)
//布局选择左右布局,一边是编辑,一边是预览
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) {
edit := widget.NewMultiLineEntry()
preview := widget.NewRichTextFromMarkdown("")
app.EditWidget = edit
app.PreviewWidget = preview
edit.OnChanged = preview.ParseMarkdown
return edit, preview
}
//添加过滤器,只能打开.md结尾文件
var filter = storage.NewExtensionFileFilter([]string{".md", ".MD"})
//创建主菜单
func (app *config) createMenuItems(win fyne.Window) {
//打开文件
openMenuItem := fyne.NewMenuItem("open...", app.openFunc(win))
//保存文件
saveMenuItem := fyne.NewMenuItem("Save...", app.saveFunc(win))
app.SaveMenuItem = saveMenuItem
app.SaveMenuItem.Disabled = true
//另存为
saveAsMenuItem := fyne.NewMenuItem("Save as...", app.saveAsFunc(win))
fileMenu := fyne.NewMenu("File", openMenuItem, saveMenuItem, saveAsMenuItem)
menu := fyne.NewMainMenu(fileMenu)
win.SetMainMenu(menu)
}
//【保存文件】功能
func (app *config) saveFunc(win fyne.Window) func() {
return func() {
if app.CurrentFile != nil {
write, err := storage.Writer(app.CurrentFile)
if err != nil {
dialog.ShowError(err, win)
return
}
write.Write([]byte(app.EditWidget.Text))
defer write.Close()
}
}
}
//【打开文件】功能
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)
//添加.md过滤器
openDialog.SetFilter(filter)
openDialog.Show()
}
}
//【另存为】功能实现
func (app *config) saveAsFunc(win fyne.Window) func() {
return func() {
saveDialog := dialog.NewFileSave(func(write fyne.URIWriteCloser, err error) {
if err != nil {
dialog.ShowError(err, win)
return
}
if write == nil {
//user canceled
return
}
if !strings.HasPrefix(strings.ToLower(write.URI().String()), ".md") {
dialog.ShowInformation("Error", "Please name your file with a .md extension!", win)
return
}
//save file
write.Write([]byte(app.EditWidget.Text))
app.CurrentFile = write.URI()
defer write.Close()
win.SetTitle(win.Title() + "-" + write.URI().Name())
app.SaveMenuItem.Disabled = false
}, win)
saveDialog.SetFileName("untitled.md")
saveDialog.SetFilter(filter)
saveDialog.Show()
}
}
//安装打包程序
//go install fyne.io/fyne/v2/cmd/fyne@latest
//fyne package -appVersion 1.0.0 --name MarkDown -appID=.41234 -releas
5 全部代码
package main
import (
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/app"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/dialog"
"fyne.io/fyne/v2/storage"
"fyne.io/fyne/v2/widget"
"github.com/flopp/go-findfont"
"io/ioutil"
"os"
"strings"
)
/*
用fyne实现简易版typora
*/
func init() {
//设置中文字体:解决中文乱码问题
fontPaths := findfont.List()
for _, path := range fontPaths {
if strings.Contains(path, "msyh.ttf") || strings.Contains(path, "simhei.ttf") || strings.Contains(path, "simsun.ttc") || strings.Contains(path, "simkai.ttf") {
os.Setenv("FYNE_FONT", path)
break
}
}
}
type config struct {
EditWidget *widget.Entry //编辑框
PreviewWidget *widget.RichText //预览框【富文本】
CurrentFile fyne.URI
SaveMenuItem *fyne.MenuItem
}
var cfg config
func main() {
a := app.New()
win := a.NewWindow("Markdown")
edit, preview := cfg.makeUI()
cfg.createMenuItems(win)
//布局选择左右布局,一边是编辑,一边是预览
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) {
edit := widget.NewMultiLineEntry()
preview := widget.NewRichTextFromMarkdown("")
app.EditWidget = edit
app.PreviewWidget = preview
edit.OnChanged = preview.ParseMarkdown
return edit, preview
}
//添加过滤器,只能打开.md结尾文件
var filter = storage.NewExtensionFileFilter([]string{".md", ".MD"})
//创建主菜单
func (app *config) createMenuItems(win fyne.Window) {
openMenuItem := fyne.NewMenuItem("open...", app.openFunc(win))
saveMenuItem := fyne.NewMenuItem("Save...", app.saveFunc(win))
app.SaveMenuItem = saveMenuItem
app.SaveMenuItem.Disabled = true
saveAsMenuItem := fyne.NewMenuItem("Save as...", app.saveAsFunc(win))
fileMenu := fyne.NewMenu("File", openMenuItem, saveMenuItem, saveAsMenuItem)
menu := fyne.NewMainMenu(fileMenu)
win.SetMainMenu(menu)
}
//【保存文件】功能
func (app *config) saveFunc(win fyne.Window) func() {
return func() {
if app.CurrentFile != nil {
write, err := storage.Writer(app.CurrentFile)
if err != nil {
dialog.ShowError(err, win)
return
}
write.Write([]byte(app.EditWidget.Text))
defer write.Close()
}
}
}
//【打开文件】功能
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)
//添加.md过滤器
openDialog.SetFilter(filter)
openDialog.Show()
}
}
//【另存为】功能实现
func (app *config) saveAsFunc(win fyne.Window) func() {
return func() {
saveDialog := dialog.NewFileSave(func(write fyne.URIWriteCloser, err error) {
if err != nil {
dialog.ShowError(err, win)
return
}
if write == nil {
//user canceled
return
}
if !strings.HasPrefix(strings.ToLower(write.URI().String()), ".md") {
dialog.ShowInformation("Error", "Please name your file with a .md extension!", win)
return
}
//save file
write.Write([]byte(app.EditWidget.Text))
app.CurrentFile = write.URI()
defer write.Close()
win.SetTitle(win.Title() + "-" + write.URI().Name())
app.SaveMenuItem.Disabled = false
}, win)
saveDialog.SetFileName("untitled.md")
saveDialog.SetFilter(filter)
saveDialog.Show()
}
}
//安装打包程序
//go install fyne.io/fyne/v2/cmd/fyne@latest
//fyne package -appVersion 1.0.0 --name MarkDown -appID=.41234 -releas
6 打包
//安装打包程序
go install fyne.io/fyne/v2/cmd/fyne@latest
//执行打包命令
fyne package -appVersion 1.0.0 --name MarkDown -appID=.41234 -release
//运行app
.MarkDown
7 问题解决(fyne中文乱码问题)
//安装依赖库
go get "github.com/flopp/go-findfont"
在main.go中添加初始化代码:
func init() {
//设置中文字体:解决中文乱码问题
fontPaths := findfont.List()
for _, path := range fontPaths {
if strings.Contains(path, "msyh.ttf") || strings.Contains(path, "simhei.ttf") || strings.Contains(path, "simsun.ttc") || strings.Contains(path, "simkai.ttf") {
os.Setenv("FYNE_FONT", path)
break
}
}
}
8 发布
对于mac系统的朋友来说,如果要想让打包之后的软件可以发给别人运行而不报错,则需要注册一个苹果开发者账号,并且使用付费版(一年大概100$),然后下载一个XCode,通过配置让XCode给我们打包好的软件签名。
- 其他版本的os类似
风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。