构建一个Markdown编辑器:Fyne综合案例

news2024/9/22 17:28:39

在本文中,我们将通过一个完整的案例来介绍如何使用Go语言的Fyne库来构建一个简单的Markdown编辑器。Fyne是一个易于使用的库,它允许开发者使用Go语言来创建跨平台的GUI应用程序。

1. 项目结构

首先,我们需要创建一个Go项目,并引入Fyne库。我们的项目结构如下:
在这里插入图片描述

2. 编写代码

接下来,我们将编写main.go文件,实现Markdown编辑器的基本功能。

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"
	"io"
	"strings"
)

// config 结构体用于存储应用程序的配置
type config struct {
	Edit        *widget.Entry
	Preview     *widget.RichText
	CurrentFile fyne.URI
	MenuItem    *fyne.MenuItem
	BaseTitle   string
}

var cfg config                                                      // 定义一个全局的config变量
var filter = storage.NewExtensionFileFilter([]string{".md", ".MD"}) // 定义一个文件过滤器,只允许.md文件

// main 函数是程序的入口点
func main() {
	a := app.NewWithID("01") // 创建一个新的Fyne应用程序
	// 加载自定义字体,并设置为应用程序主题字体
	customFont := fyne.NewStaticResource("NotoSansHans.ttf", loadFont("NotoSansHans-Regular.ttf"))
	a.Settings().SetTheme(&myTheme{font: customFont})
	// 创建一个新的窗口,并设置窗口的标题
	w := a.NewWindow("Markdown编辑器")
	cfg.BaseTitle = "Markdown编辑器"
	// 创建UI组件,并保存到config结构体中
	edit, preview := cfg.makeUI()
	// 创建窗口的菜单,并绑定相关功能
	cfg.createMenu(w)
	// 设置窗口的内容为编辑和预览的分割布局
	w.SetContent(container.NewHSplit(edit, preview))
	// 设置窗口的初始大小,并居中显示在屏幕上
	w.Resize(fyne.NewSize(800, 600))
	w.CenterOnScreen()
	// 显示窗口,并启动应用程序的事件循环
	w.ShowAndRun()
}

// makeUI 方法用于创建Markdown编辑器的UI组件
func (cfg *config) makeUI() (*widget.Entry, *widget.RichText) {
	// 创建一个多行输入框用于编辑Markdown
	edit := widget.NewMultiLineEntry()
	// 创建一个富文本组件用于显示Markdown预览
	preview := widget.NewRichTextFromMarkdown("")

	// 将编辑和预览组件保存到config结构体中
	cfg.Edit = edit
	cfg.Preview = preview

	// 当编辑区域内容变化时,更新预览区域
	edit.OnChanged = preview.ParseMarkdown

	return edit, preview // 返回编辑和预览组件
}

// createMenu 方法用于创建窗口的菜单项
func (cfg *config) createMenu(win fyne.Window) {
	// 创建“打开”、“保存”、“另存为”菜单项,并绑定相关功能
	open := fyne.NewMenuItem("打开", cfg.openFunc(win))
	save := fyne.NewMenuItem("保存", cfg.saveFunc(win))
	cfg.MenuItem = save
	cfg.MenuItem.Disabled = true
	saveAs := fyne.NewMenuItem("另存为...", cfg.saveAsFunc(win))
	// 创建“文件”菜单,并包含上述菜单项
	fileMenu := fyne.NewMenu("文件", open, save, saveAs)
	// 创建窗口的主要菜单,并设置到窗口中
	menu := fyne.NewMainMenu(fileMenu)
	win.SetMainMenu(menu)
}

// saveAsFunc 方法用于实现“另存为”功能的逻辑
// 参数:
//
//	win - fyne.Window类型,表示当前窗口对象
//
// 返回值:
//
//	一个func()类型的闭包函数,用于在调用时触发文件保存逻辑
func (cfg *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 {
				// 如果用户取消保存,直接返回
				return
			}
			if !strings.HasSuffix(strings.ToLower(write.URI().String()), ".md") {
				// 确保文件具有.md扩展名,否则提示用户
				dialog.ShowInformation("错误", "必须是.md扩展名", win)
				return
			}
			// 将编辑器的内容写入文件
			write.Write([]uint8(cfg.Edit.Text))
			// 更新当前文件路径
			cfg.CurrentFile = write.URI()
			// 关闭文件写入器
			defer write.Close()
			// 更新窗口标题
			win.SetTitle(cfg.BaseTitle + "_" + write.URI().Name())
			// 禁用菜单项,防止重复保存
			cfg.MenuItem.Disabled = true
		}, win)
		// 设置对话框的默认文件名和过滤器
		saveDialog.SetFileName("未命名.md")
		saveDialog.SetFilter(filter)
		// 显示对话框
		saveDialog.Show()
	}
}

// openFunc 生成一个用于打开文件的函数。
// 该函数在被调用时,会显示一个文件选择对话框,允许用户选择一个文件进行打开。
// 参数win是用于父窗口的引用,用于关联打开对话框和错误对话框。
func (cfg *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 {
				// 如果用户取消了操作,read将为nil,直接返回
				return
			}
			// 读取文件内容
			data, err := io.ReadAll(read)
			if err != nil {
				// 如果读取文件时发生错误,显示错误对话框
				dialog.ShowError(err, win)
				return
			}
			// 设置编辑区域的文本为打开的文件内容
			cfg.Edit.SetText(string(data))
			// 更新当前文件的路径
			cfg.CurrentFile = read.URI()
			// 更新窗口标题为文件名
			win.SetTitle(cfg.BaseTitle + "-" + read.URI().Name())
			// 允许菜单项的操作
			cfg.MenuItem.Disabled = false
			// 确保读取器在使用后关闭
			defer read.Close()
		}, win)
		// 设置文件过滤器
		openDialog.SetFilter(filter)
		// 显示文件打开对话框
		openDialog.Show()
	}
}

// saveFunc 生成一个用于保存配置的函数。
// 该函数接受一个 fyne.Window 参数,但实际保存操作并不依赖于窗口参数本身。
// 主要用于统一保存配置的处理逻辑,无论配置是否关联到了一个特定的文件。
// 如果配置关联到了一个文件(CurrentFile 不为 nil),则会尝试将配置内容写入该文件。
// 如果写入过程中出现错误,将通过对话框展示错误信息。
// 返回的函数无参数,无返回值。
func (cfg *config) saveFunc(win fyne.Window) func() {
	return func() {
		// 检查是否有当前文件
		if cfg.CurrentFile != nil {
			// 尝试获取当前文件的写入器
			write, err := storage.Writer(cfg.CurrentFile)
			// 如果获取写入器过程中出现错误,则展示错误信息并返回
			if err != nil {
				dialog.ShowError(err, win)
				return
			}
			// 写入配置文本到文件
			write.Write([]byte(cfg.Edit.Text))
			// 确保写入器关闭
			defer write.Close()
		}
	}
}

3. 运行应用程序

要运行我们的Markdown编辑器,我们需要在终端中执行以下命令:

go run main.go

这将启动我们的应用程序,并显示一个具有编辑和预览功能的Markdown编辑器窗口。

4. 扩展功能

虽然我们的编辑器目前只支持基本的Markdown编辑和预览功能,但Fyne库的强大功能允许我们轻松扩展更多特性,例如:

  • 支持更多的Markdown扩展语法。
  • 实现语法高亮。
  • 添加导出为PDF或其他格式的功能。
  • 实现云同步功能。

5. 结语

通过本文的案例,我们可以看到Fyne库在构建跨平台GUI应用程序方面的强大能力。Markdown编辑器只是一个起点,Fyne可以帮助我们构建更复杂的应用程序。

希望这个案例能够启发你使用Fyne来创建你自己的应用程序。如果你有任何问题或需要进一步的帮助,请随时联系我们。

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

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

相关文章

基于vllm部署大模型

VLLM(非常大的语言模型)在中文中通常指的是经过大量文本数据训练的神经网络模型,能够理解和生成类似人类语言的文本。这类模型是许多先进AI系统的核心,比如ChatGPT、GPT-4等。 VLLM 可以执行各种任务,如自然语言处理、…

【实用工具】使用Chrome插件搭建第二大脑!SuperMemory大语言模型登场,开源、免费、保存你需要的所有网站!——含入门安装教程

文章目录 项目简介项目搭建主要功能How do I use this?本地部署 项目简介 最近,有一款Github项目十分火爆,它专注于用超级内存打造自己的第二大脑。它是书签的 ChatGPT,基于Chrome 浏览器扩展导入推文或保存网站和内容,你可以访…

RTD2739 8K60Hz/4K 240Hz

RTD2739支持4K240Hz/80K60Hz。 RTD2739 supports input format up to 3840 x 2160 240Hz RTD2739 supports 3 ports of Ultra-High Speed Receiver can support DisplayPort1.4 In DisplayPort mode, four link layer speed HBR3 (8.1GHz), HBR2 (5.4GHz), HBR(2.7GHz), R…

理解Linux中的作业控制:详解fg、bg和jobs命令

理解Linux中的作业控制:详解fg、bg和jobs命令 文章目录 理解Linux中的作业控制:详解fg、bg和jobs命令1. 前言2. jobs 命令3. bg 命令4. fg 命令5. 简要总结6. 一图概览 1. 前言 ​ Linux系统中,作业控制是一个非常重要的概念,尤其…

成为Python砖家(1): 在本地查询Python HTML文档

本地是 macOS 系统。 下载 python html 格式文档 https://docs.python.org/3/download.html 下载logo png图像 https://www.python.org/community/logos/ 创建 PythonDoc.sh, 用于创建 PythonDoc.app: #!/bin/bash# This script generates /Application/Python…

【MySQL】order by 的排序策略

目录 一、全字段排序 二、磁盘临时文件辅助排序 三、RowId 排序 四、优化 order by 在前面的文章中我们分析了 explain 执行计划的各个字段,其中有一个 Extra 字段。 在执行计划中,Extra 字段如有 Using filesort 则表示使用到了排序。 而 MySQL 的…

小白做毕设——HTTP扫盲

工具下载 Postman(发送 http 请求的工具) 官网(下载速度比较慢):https://www.postman.com/downloads/ Http 请求格式 请求地址请求方法状态码同源策略请求头响应头 接口类型 Post 接口(新增数据&#x…

ubuntu20.04挂载机械硬盘

环境说明 1.基于清华源地址下载的ubuntu20.04制作的系统盘,然后安装在PC上(固态硬盘) 2.机械硬盘无法看见 目的 挂载机械硬盘,开机就能自动启动/挂载 参考链接 https://blog.csdn.net/qq_35624642/article/details/137713143…

web技术1——http详解(重要)

什么是http 超文本: 超过文本的,不止文本,就是不是文本的也能传,比如图片,音乐,视频等。 https: 带s,是安全的意思 两个时代 1.0访问一次,连接就断开了,再访问要重新请…

低代码平台工具 —— 公式编辑器

导读 公式编辑器:公式编辑用于字段值来源于其他字段或是来源于函数计算结果都可由公式编辑来完成 公式编辑器主要需要解决三件事: 合适的编辑器强大的函数库合适的事件监听 技术选型 excel函数库 formulajs,这个函数库可以让JavaScript支…

pql语言学习

转自:https://yunlzheng.gitbook.io/prometheus-book/parti-prometheus-ji-chu/promql/prometheus-query-language //非常全面易懂的教程 1.语法 当我们直接使用监控指标名称查询时,可以查询该指标下的所有时间序列,只会返回瞬时向量表达式…

TypeScript系列之--有趣理解函数类型泛型

函数类型 TS 定义函数类型需要定义输入参数类型和输出类型。 输出类型也可以忽略,因为 TS 能够根据返回语句自动推断出返回值类型。 function add(x:number, y:number):number { return x y } add(1,2) 函数没有明确返回值,默认返回 Void 类型 fu…

flink车联网项目:业务实现2(维表开发)(第68天)

系列文章目录 3.2 维表开发 3.2.1 创建库 3.2.2 示例 3.2.2.1 类型转换 3.2.2.2 创建mysql映射表 3.2.2.3 创建paimon映射表 3.2.2.4 从mysql插入到paimon表 3.2.2.5 结果查看 3.2.2.6 测试 3.2.3 其他表开发 3.2.4 部署 文章目录 系列文章目录前言3.2 维表开发3.2.1 创建库3.…

C:每日一练:单身狗(2.0版本)

前言: 今天在刷题的时候突然看到一道题,疑似一位故题。仔细一看,欸!这不是就是单身狗的升级版吗?我想那必须再安排一篇,不过由于本篇文章与上一篇单身狗文章所涉及的知识点基本相同,所以还请大…

小型企业客户关系管理系统pf

TOC springboot457小型企业客户关系管理系统pf 绪论 1.1 研究背景 当前社会各行业领域竞争压力非常大,随着当前时代的信息化,科学化发展,让社会各行业领域都争相使用新的信息技术,对行业内的各种相关数据进行科学化&#xff0…

推送本地windows环境镜像到阿里云镜像仓库

说明:从dockerhub拉取了apache/kafka3.7.0镜像到本地windwos操作系统上,再将该镜像推送到阿里云镜像仓库,记录了本次操作过程。 1、启动本地的docker desktop,搜索官方镜像 将搜索到的apache/kafka官方镜像拉取到本地 镜像拉取…

代码随想录算法训练营_day18

题目信息 530. 二叉搜索树的最小绝对差 题目链接: https://leetcode.cn/problems/minimum-absolute-difference-in-bst/description/题目描述: 给你一个二叉搜索树的根节点 root ,返回 树中任意两不同节点值之间的最小差值 。 差值是一个正数,其数值等…

SpringBoot基础(二):配置文件详解

SpringBoot基础系列文章 SpringBoot基础(一):快速入门 SpringBoot基础(二):配置文件详解 目录 一、配置文件分类二、配置文件优先级1、不同版本优先级2、不同位置优先级 三、配置文件格式1、yml和yaml格式1.1、字符串1.2、布尔类型1.3、整数型1.4、浮点…

论文阅读笔记:The Graph Neural Network Model

论文来源 IEEE Transactions on Neural Networks,Volume: 20 Issue: 1 背景 图神经网络模型本身具有广泛的使用背景,由于我个人研究交通流量预测的需要,此处仅考虑深度学习领域。图结构指的是由节点node和若干个连接的边edge组成的一种数据…

【Docker】Docker Compose(容器编排)

一、什么是 Docker Compose docker-compose 是 Docker 官方的开源项目,使用 python 编写,实现上调用了 Docker 服务的 API 进行容器管理及编排,其官方定义为定义和运行多个 Docker 容器的应用。 docker-compose 中有两个非常重要的概念&…