标签: Go

  • 🎯 一键推送:如何在 Caddy 2 中推送所有的 JS 和 CSS 文件

    在现代 web 开发中,加载速度是用户体验的重要组成部分。Caddy 2 提供的 HTTP/2 推送功能,能够让我们在用户请求页面时,主动将 JavaScript 和 CSS 文件推送给他们。这样就像是给用户送上了一份精致的套餐,让他们在享用主菜的同时,也能提前品尝到美味的配菜。今天,我们就来看看如何配置 Caddy 2 推送所有的 JS 和 CSS 文件。

    📂 步骤一:打开 Caddyfile

    首先,我们需要找到并打开 Caddy 的配置文件 Caddyfile。这个文件通常位于 /etc/caddy/Caddyfile,你可以使用以下命令打开:

    sudo nano /etc/caddy/Caddyfile

    ✏️ 步骤二:修改 Caddyfile 配置

    Caddyfile 中,你可以使用通配符来匹配所有的 JS 和 CSS 文件,并进行推送。以下是一个示例配置:

    example.com {
        root * /var/www/html
        file_server
    
        # 推送所有的 CSS 文件
        @css {
            path_regexp css ^.*\.css$
        }
        push @css
    
        # 推送所有的 JS 文件
        @js {
            path_regexp js ^.*\.js$
        }
        push @js
    }

    在这个配置中:

    • @css@js 是两个匹配器,用于匹配所有以 .css.js 结尾的文件。
    • path_regexp 是一个正则表达式匹配器,它能够灵活地匹配文件路径。
    • push 指令则会将匹配到的文件进行推送。

    🔄 步骤三:重启 Caddy 服务

    完成 Caddyfile 的修改后,别忘了重启 Caddy 服务,使更改生效:

    sudo systemctl restart caddy

    🔍 步骤四:验证推送效果

    要验证推送是否成功,你可以使用浏览器的开发者工具:

    1. 打开你的网页,右键点击选择“检查”或按 F12
    2. 切换到“网络”选项卡。
    3. 刷新页面,查看加载的资源。

    在加载的资源列表中,你应该能看到 .css.js 文件被标记为 “Pushed”,这说明推送功能已经成功运行。

    ⚠️ 注意事项

    1. 推送的频率:推送过多的文件可能会导致网络拥堵,因此建议根据实际情况合理选择推送的文件。
    2. 浏览器支持:确保目标用户的浏览器支持 HTTP/2 推送,尽量避免在老旧浏览器中出现兼容性问题。
    3. 性能监测:建议在推送后进行性能测试,观察整体的加载时间和用户体验的变化。

    ✅ 总结

    通过以上步骤,你已经成功配置了 Caddy 2 以推送所有的 JavaScript 和 CSS 文件。这样的配置不仅提升了网页的加载速度,还能为用户带来更流畅的浏览体验。希望这篇指南能够帮助你更好地利用 Caddy 2 的强大功能,让你的网站如虎添翼!如有任何疑问,欢迎随时交流!

  • 🖱️ BubbleZone:为你的终端应用注入交互活力

    🤯 终端应用开发的苦恼

    想象一下,你正在用 BubbleTea 和 Lipgloss 构建一个炫酷的终端应用。你已经熟练地使用它们抽象出布局、颜色、事件等元素,轻而易举地创建出一个用户友好的界面。你甚至用上了 BubbleTea 的鼠标事件支持,实现了像按钮点击、区域悬停等功能。

    一切看起来都很美好,直到你的应用开始变得复杂起来。多个组件层层嵌套,子组件又有自己的子组件,就像一个错综复杂的迷宫。这时,你想要实现一个简单的功能:点击某个按钮触发特定操作。然而,你需要先定位鼠标点击的位置,然后一层层地向上遍历组件树,判断哪个组件才是被点击的目标。这就像你要在迷宫里找到特定的一粒沙子,费时费力,让人头疼不已。

    ✨ BubbleZone:化解复杂,精准定位

    BubbleZone 正是为了解决这个问题而诞生的。它就像是在迷宫里为每一粒沙子都做了标记,让你能够轻松地找到它们。

    BubbleZone 的工作原理是什么呢?

    1. 标记区域: 使用 zone.Mark() 函数,你可以为任何想要监听鼠标事件的组件添加一个独特的 ID,就像给它们贴上了一张隐形的标签。
    2. 扫描区域: 在根组件的 View() 函数中,使用 zone.Scan() 函数包裹整个应用的输出。这个函数会扫描所有被标记的区域,记录它们的位置信息,并生成一个区域地图。
    3. 精准定位: 当鼠标事件发生时,BubbleZone 会根据区域地图快速定位到被点击的区域,并触发相应的操作。

    🚀 BubbleZone 的优势

    • 高效: BubbleZone 的设计目标之一就是速度快。它在每次渲染时都需要处理区域信息,因此开发者非常注重性能优化,力求将性能影响降到最低。
    • 精准: BubbleZone 能够精准地定位到被点击的区域,即使是在复杂的组件嵌套结构中也能准确无误。
    • 易用: BubbleZone 使用起来非常简单,你只需要几行代码就能为你的应用添加鼠标事件支持。

    💡 使用技巧

    为了帮助你更好地使用 BubbleZone,以下是一些实用技巧:

    • 避免区域重叠: 为了避免不同组件的区域 ID 发生冲突,建议使用 NewPrefix() 函数生成一个唯一的区域前缀。
    • 使用 lipgloss.Width() 计算宽度: BubbleZone 专门针对 lipgloss.Width() 函数进行了优化,确保区域标记不会影响宽度计算结果。
    • 谨慎使用 MaxHeight()MaxWidth() 这两个函数会对组件进行硬裁剪,如果区域标记被裁剪掉,就会导致区域定位失效。
    • 只在根组件中使用 zone.Scan() zone.Scan() 函数应该只在根组件的 View() 函数中使用一次,否则可能会导致区域定位错误。
    • 注意非矩形区域的边界问题: BubbleZone 的区域边界是矩形的,如果你的组件是非矩形的,例如圆形,需要确保区域标记足够大,能够完全覆盖整个组件。

    🎉 让你的终端应用充满活力

    BubbleZone 为你的终端应用带来了前所未有的交互体验,让你的应用不再是冰冷的命令行工具,而是充满活力的交互式应用。

    参考文献:

  • 🍵 一杯代码冲泡的珍珠奶茶:Go 语言 Bubble Tea 初探

    “在命令行里工作很酷,会让你感觉自己像黑客电影里的英雄。”

    此言不虚!但是,面对单调乏味的文字瀑布,或者令人望而生畏的命令行参数、美元符号前缀,以及各种莫名其妙的错误信息,我们也难免会感到力不从心。

    好在,终端世界里除了冰冷的命令行,还有生机勃勃的 TUI(终端用户界面)。TUI 就像一杯珍珠奶茶,在复古的外表下,蕴藏着现代化的体验。而在 Go 语言的世界里, Bubble Tea 库的出现,让制作 TUI 应用变得像泡一杯奶茶一样简单。

    🤔 为什么选择 Bubble Tea?

    想象一下,你是一位经验丰富的茶饮师,想要调制一杯独一无二的珍珠奶茶。这时,你会选择什么样的工具呢?

    Bubble Tea 就如同一个功能强大的工具箱,它提供了以下几个不可抗拒的优势:

    • 熟悉的 Elm 架构: 如果你已经习惯了 React、Vue 或 Elm 等前端框架,那么 Bubble Tea 的 Elm 架构对你来说一定不会陌生。这种架构就像奶茶的配方,清晰易懂,让你轻松上手。
    • 模块化的代码组织: Elm 架构不仅易于理解,而且非常适合构建模块化的 UI 代码。你可以从一个简单的应用开始,逐步添加功能,就像在奶茶中加入珍珠、椰果一样,让你的应用逐渐丰满起来。
    • 简洁易懂的 Go 语言: Go 语言以其简洁一致的语法著称,这使得阅读和理解他人的代码变得轻而易举。就像品尝一杯精心调制的奶茶,你能清晰地感受到每一种成分的味道。

    🚀 从 Hello World 开始

    俗话说,万事开头难。让我们从一个简单的 “Hello World” 应用开始,体验一下 Bubble Tea 的魅力吧!

    首先,创建一个名为 code-journal 的目录,并在终端中运行以下命令:

    go mod init
    go get github.com/charmbracelet/bubbletea

    然后,创建一个名为 app.go 的文件,并添加以下代码:

    package main
    
    import (
        tea "github.com/charmbracelet/bubbletea"
    )
    
    func main() {
        p := tea.NewProgram(
            newSimplePage(
                "This app is under construction",
            ),
        )
        if err := p.Start(); err != nil {
            panic(err)
        }
    }

    接下来,创建另一个名为 simple_page.go 的文件,其中包含我们的第一个 UI,一个只显示一些文本的简单页面:

    package main
    
    import (
    	"fmt"
    	"strings"
    
    	tea "github.com/charmbracelet/bubbletea"
    )
    
    // MODEL DATA
    type simplePage struct {
    	text string
    }
    
    func newSimplePage(text string) simplePage {
    	return simplePage{
    		text: text,
    	}
    }
    
    func (s simplePage) Init() tea.Cmd {
    	return nil
    }
    
    // VIEW
    func (s simplePage) View() string {
    	textLen := len(s.text)
    	topAndBottomBar := strings.Repeat("*", textLen+4)
    	return fmt.Sprintf("%s\n* %s *\n%s\n\nPress Ctrl+C to exit", topAndBottomBar, s.text, topAndBottomBar)
    }
    
    // UPDATE
    func (s simplePage) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
    	switch msg.(type) {
    	case tea.KeyMsg:
    		switch msg.(tea.KeyMsg).String() {
    		case "ctrl+c":
    			return s, tea.Quit
    		}
    	}
    	return s, nil
    }

    在终端中运行以下命令,编译并运行我们的应用:

    go build ./code-journal

    你将会看到如下输出:

    ************************
    * This app is under construction *
    ************************
    
    Press Ctrl+C to exit

    恭喜你!你已经成功运行了你的第一个 Bubble Tea 应用。

    🔬 剖析代码

    现在,让我们像品尝奶茶一样,细细品味一下这段代码。

    🧋 Model:Bubble Tea 的核心

    main 函数通过创建一个新的 simplePage 模型来启动程序。

    p := tea.NewProgram(
        newSimplePage(
            "This app is under construction",
        ),
    )

    我们调用了 tea.NewProgram 函数,它的函数签名如下:

    func NewProgram(initialModel Model) *Program

    然后调用该程序的 Start 方法来启动我们的应用程序。但是 initialModel 是什么呢?

    ModelBubble Tea 的核心接口,它定义了三个方法:

    type Model interface {
        Init() Cmd
        Update(msg Msg) (Model, Cmd)
        View() string
    }
    • Init() 方法在应用程序启动时被调用,并返回一个 tea.CmdCmd 可以理解为 “幕后发生的事情”,例如加载数据或计时器。在本教程中,我们没有任何后台操作,因此 Init 方法只返回 nil
    func (s simplePage) Init() tea.Cmd {
        return nil
    }
    • View() 方法是 Bubble Tea 中一个很酷的抽象,它将整个 UI 的显示都表示为一个字符串!在 View 方法中,你需要构建并返回这个字符串。
    func (s simplePage) View() string {
        textLen := len(s.text)
        topAndBottomBar := strings.Repeat("*", textLen+4)
        return fmt.Sprintf("%s\n* %s *\n%s\n\nPress Ctrl+C to exit", topAndBottomBar, s.text, topAndBottomBar)
    }

    在上面的代码中,我们将 simplePage 的文本放在一个由星号组成的盒子里,并在底部显示一条消息 “Press Ctrl+C to exit”。

    • Update() 方法负责处理用户输入。
    func (s simplePage) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
        switch msg.(type) {
        case tea.KeyMsg:
            switch msg.(tea.KeyMsg).String() {
            case "ctrl+c":
                return s, tea.Quit
            }
        }
        return s, nil
    }

    Update() 方法接收一个 tea.Msg 并返回一个新的 tea.Model,有时还会返回一个 tea.Cmd(例如,如果某个操作导致检索数据或计时器启动)。

    tea.Msg 的类型签名如下:

    type Msg interface{}

    所以它可以是任何类型,并携带你需要的数据。它有点像 JavaScript 中的浏览器事件;计时器事件不携带任何数据,点击事件会告诉你点击了什么,等等。

    我们正在处理的消息类型是 tea.KeyMsg,它表示键盘输入。我们检查用户是否按下了 Ctrl+C. 如果是,则返回 tea.Quit 命令,该命令属于 tea.Cmd 类型,并告诉 Bubble Tea 退出应用程序。

    对于任何其他类型的输入,我们不做任何处理,只是按原样返回模型。但是,如果我们正在执行 UI 导航等操作,则会更改模型上的一些字段,然后返回它,从而导致 UI 更新。

    ⏭️ 下一站:菜单组件

    在下一篇文章中,我们将学习如何创建一个菜单组件,并让我们的应用程序更加生动有趣!敬请期待!

  • 🕵️‍♂️ Go语言GUI框架迷局:寻找RAD工具的“真命天子”

    自上世纪80年代以来,我一直徜徉在代码的世界里。近年来,我一直在探索用Go语言编写GUI程序的各种可能性,尤其希望能找到合适的工具来构建跨平台桌面GUI软件。当然,如果能用同一套技术开发移动版本,那就更令人兴奋了,毕竟,节省开发人员宝贵时间和精力的技术,谁不爱呢?

    然而,一番探索下来,我却发现了一个残酷的事实:Go语言目前还没有一个真正好用的GUI框架! 这实在令人惋惜,因为除了GUI这一点,Go语言几乎完美契合了我对易用、快速的应用程序开发工具的所有想象,就像我曾经钟爱的REALBasic(现在更名为价格不菲的Xojo)那样。遗憾的是,到目前为止,还没有任何一个框架或语言+IDE+框架的组合能够超越REALBasic带给我的高效开发体验。

    我并非没有尝试过其他的选择。Lazarus和Object Pascal也曾进入我的视野,但Object Pascal毕竟年岁已高,语言本身的局限性难以忽视,而且有时还需要手动管理内存,这对于桌面GUI应用程序来说简直是噩梦。我也曾考虑过其他我比较熟悉且喜欢的语言。

    Racket拥有非常完善的GUI工具包,我曾经用它编写和维护过多年的GUI应用程序。然而,用Racket开发的GUI应用程序反应迟钝,启动时间也令人捉急,这使得它在开发“轻快敏捷”的小型GUI应用程序时显得力不从心。

    Ada给我的感觉就像是在不断积累技术债务,而且缺乏第三方库的支持。它唯一还算过得去的GUI选项是GTK3。我还尝试过CommonLisp,但它总是给我一种“拼拼凑凑”的感觉,而且现有的第三方库缺乏文档,可靠性也值得怀疑。至于Rust,我已经学习了它的语法,甚至从头到尾啃完了Klabnik和Nichols合著的那本“巨著”,但我不得不承认,Rust给我的感觉是过度设计,并不吸引我。它是否真的适合开发高完整性系统,我持保留意见(我仍然推荐Ada/Spark来完成这类任务)。此外,Rust在GUI编程方面没有任何优势,而且它的社区氛围也让人不敢恭维。

    所以,在一番权衡之后,我还是选择了Go语言来开发一个大型项目,而GUI库的选择也提上了日程。以下是我对Go语言GUI框架的个人看法,仅供参考。

    🎭 各显神通:Go语言GUI框架大比拼

    在Go语言的世界里,GUI框架虽然数量不多,但也称得上是“百花齐放”。下面,我将逐一介绍这些框架的特点、优缺点以及适用场景,希望能帮助读者朋友们找到适合自己的那一款。

    1️⃣ Fyne:叫好不叫座?

    • 官方网站: https://fyne.io/
    • 测试情况: 经过广泛测试
    • 评分: 5/10
    • 更新/修复频率: 频繁且迅速
    • 推荐指数: 仅推荐用于移动应用程序开发

    Fyne基本上是一个人带着一群合作者的成果。我的感受是,Fyne的开发者们更擅长推广他们的框架,而不是编写GUI框架。Fyne在设计上存在一些根本性的缺陷。其中最大的问题是,他们试图用完全相同的代码来开发桌面和移动应用程序。要知道,手机和平板电脑的尺寸和交互方式与桌面电脑截然不同,这种“一刀切”的想法显然行不通。桌面应用程序与移动应用程序有着本质的区别,这是Fyne开发者们需要正视的问题。

    由于Fyne的设计理念存在偏差,他们做出了一系列令人费解的选择。例如,他们将布局与控件分离,这使得一切都变得异常复杂,使用起来一点也不简单。Fyne提供的布局机制倾向于将其内容最小化,控件只存储最小尺寸(所有控件的默认值),而没有首选尺寸或最大尺寸。此外,Fyne的控件没有锁定顶部、左侧、底部、右侧或其他合理的机制来控制它们在嵌入窗口或面板大小调整时的行为。Fyne还没有Z-order机制来控制控件之间的切换顺序。

    另一个大问题是,Fyne缺乏合理的文件路径抽象。开发者们坚持认为所有东西都应该是URL,显然没有意识到URL标准并没有以跨平台的方式指定包含卷信息的绝对路径。虽然URI方案有一些扩展可以使用盘符,Fyne也使用了一些他们认为合适的方案,但这些基于/\的本地文件系统路径转换存在很多问题。例如,如何处理可移动驱动器?如何处理Windows上的盘符?如何构造路径、遍历文件系统?到目前为止,Fyne并没有提供任何帮助,甚至坚持认为每个URI都代表一个任意的资源,你不应该假设它指向一个文件,它也可能是一个网络资源。难道网络访问和文件访问真的可以一概而论吗?

    总而言之,Fyne并不适合开发桌面应用程序,尽管它在移动平台上可能还不错。用Fyne很难设计出复杂的桌面应用程序界面。默认情况下,Fyne应用程序会违反所有平台上的人机界面指南,而开发者们似乎对此感到非常自豪。你将不得不花费数天甚至数周的时间来为一些简单的事情创建自己的专用布局算法,而这些事情在其他合理的、标准的布局系统中只需几分钟或几小时就能完成。

    2️⃣ Gio:未来可期

    • 官方网站: https://gioui.org/
    • 测试情况: 未经测试
    • 评分: 6/10
    • 更新/修复频率: 一般,速度较慢
    • 推荐指数: 很有潜力,但我还没有对其进行详细测试

    Gio是一个非常有前途的立即模式GUI框架,它也包含一些状态ful控件。一些人认为它是Fyne的良好替代品,它拥有你在应用程序中期望的大多数控件。在我看来,Gio的使用方法似乎有些复杂和繁琐——如果你查看示例代码,就会发现即使是简单的入门程序也需要编写大量的样板代码。然而,Gio最大的缺点是缺乏文档,这使得它很难上手,你在一开始可能会迷失方向。尽管如此,我仍然计划在将来找个时间试用一下Gio,至少将其作为GIU的替代方案进行测试。我还没有测试过它对编辑器控件的支持程度——框架是否能够支持复杂的文本编辑(多行文本、富文本甚至嵌入图像)是衡量GUI框架优劣的试金石之一,因为多行文本编辑是目前为止最难正确实现的功能。我还没有仔细研究过Gio的代码,但我猜测它在这方面的表现应该与Giu类似。

    3️⃣ Giu:小项目利器

    • 官方网站: https://github.com/AllenDang/giu
    • 测试情况: 经过广泛测试
    • 评分: 6/10
    • 更新/修复频率: 一般,速度较慢
    • 推荐指数: 推荐用于小型项目,但在投入使用前请仔细检查其局限性

    Giu是一个不太为人知的立即模式GUI框架,它基于imgui的Go语言分支,使用了imgui的C语言库。Giu非常易于使用,在我测试过的所有GUI框架中,它可能是让我最快获得满意结果的框架,尽管它的速度比不上Lazarus这样的传统RAD工具。Giu拥有你可能需要的大多数控件,但有两个明显的缺陷:

    • 缺少文件对话框: Giu没有提供文件和文件夹选择/保存对话框,以及其他一些常见的简单对话框。我已经设法实现了一些替代方案,足以满足我的使用场景,但你应该意识到,没有原生的文件对话框是一个严重的限制,可能会带来其他问题(例如,缺乏与“记住上次文件访问”功能、拖放功能等的集成)。
    • 多行文本框不支持自动换行: 我已经找到了解决这个问题的方法,但还没有在Github上公开代码。

    因此,你应该意识到这可能是一个相当大的限制。然而,与Fyne不同的是,Giu的所有默认设置都很合理,而且用它很容易就能创建出简单到中等复杂度的GUI,例如包含列表、一些编辑字段、按钮、组合框或单选按钮、菜单以及一两个窗口拆分器的界面。Giu的外观和感觉一点也不原生,但总体来说还不错。你可以访问底层的imgui库,但要注意,imgui本身非常简陋,Go语言版本没有像C语言版本那样提供高级的状态ful控件。

    4️⃣ GTK:老牌劲旅

    • 官方网站:
      • GTK3: https://github.com/gotk3/gotk3
      • GTK4: https://github.com/diamondburned/gotk4
    • 测试情况: 测试过GTK3
    • 评分: 7/10
    • 更新频率: 自动生成
    • 推荐指数: GTK4可能是目前开发大型复杂桌面应用程序的最佳选择,这类应用程序通常使用Qt来开发

    5️⃣ IUP:简单易用

    • 官方网站: https://github.com/matwachich/iup
    • 测试情况: 未经测试
    • 评分: 5/10
    • 更新频率: 从未更新
    • 推荐指数: IUP在Linux和Windows平台上口碑不错,它可能是一个比GTK更容易部署的替代方案,值得一试

    6️⃣ Qt:受 licensing 限制

    • 官方网站: https://github.com/therecipe/qt
    • 测试情况: 未经测试
    • 评分: 对于没有商业许可的专有软件来说,Qt的得分为4/10,但对于GPL软件来说,它可能是一个不错的选择
    • 更新/修复频率: 很少更新,速度较慢
    • 推荐指数: 由于许可证问题,Qt只适合开发GPL软件

    7️⃣ Unison:值得关注

    • 官方网站: https://github.com/richardwilkes/unison
    • 测试情况: 未经测试
    • 评分: 无法评分(演示程序在编译skia时出错,因此无法提供截图)
    • 更新/修复频率: 正在开发中

    我最近才发现Unison,目前还无法对其做出评价。Unison看起来是一个不错的非原生GUI框架,它提供了许多标准控件,并且正在为作者的个人项目进行开发。Unison的代码看起来非常清晰易懂,所以我建议你尝试一下。我以后也会对它进行更广泛的测试。当然,Unison目前还不能用于生产环境,尤其是大型GUI项目。

    8️⃣ Wails:Web 开发者的福音

    • 官方网站: https://wails.app/
    • 测试情况: 未经测试
    • 评分: 7/10
    • 更新/修复频率: 频繁且迅速
    • 推荐指数: 如果你本来就擅长Web开发,那么我强烈推荐你尝试一下Wails

    Wails是这份清单中唯一的基于Web的GUI框架,因为它不是我的首要选择。Wails允许你使用Go语言编写后端代码,并使用任何你喜欢的Web框架来开发GUI。Wails会自动将Go代码与HTML中的Javascript代码链接起来,并渲染HTML/JS代码。最终的产物是一个功能齐全的桌面应用程序。我在很多地方都看到过人们推荐Wails,并将其与各种前端库结合使用。

    如果你本来就擅长开发Web应用程序,那么我强烈推荐你尝试一下Wails。我的目标是使用一个允许我完全使用Go语言开发所有组件的GUI库,因此Wails不在我的考虑范围之内。然而,基于Web的界面可以做得非常漂亮,而且熟悉Web设计的开发者可能会发现Wails比任何非原生的、纯Go语言的GUI库(通常基于Open GL后端渲染引擎)都要好用。如果你希望你的应用程序看起来像一个网页(或者希望保留将其变成真正的Web应用程序的选项),那么Wails可能是最容易实现这一目标的选择。

    🏆 总结:路漫漫其修远兮

    总的来说,Go语言的GUI框架选择还是比较有限的。我选择Giu来开发一个项目,但其实选择Gio也无妨。但我必须承认,我并不喜欢立即模式GUI。如果你的GUI比较复杂,你最终将不得不编写额外的“布局缓冲区”粘合代码来维护状态。例如,我需要从sqlite数据库中获取和设置数据,但Giu的文本字段只支持字符串,所以我必须单独维护字符串,并在适当的时候从数据库中获取/更新数据。也许GTK4是更好的选择,我最终可能会改用GTK4。我将来也可能会再给Fyne一次机会,但我不抱太大希望。Fyne的设计理念过于固执,而且方向也不对。我还考虑过在正式项目中尝试Wails。对我来说,学习Wails的成本会很高,因为我的Web设计知识还停留在上世纪90年代末。然而,Wails在灵活性和重新设计方面的优势可能值得我为之付出努力。

    📚 参考文献

    • Fyne: https://fyne.io/
    • Gio: https://gioui.org/
    • Giu: https://github.com/AllenDang/giu
    • GTK:
      • GTK3: https://github.com/gotk3/gotk3
      • GTK4: https://github.com/diamondburned/gotk4
    • IUP: https://github.com/matwachich/iup
    • Qt: https://github.com/therecipe/qt
    • Unison: https://github.com/richardwilkes/unison
    • Wails: https://wails.app/

    原文链接: https://medium.com/@balle.johannes/no-good-go-gui-9f54813e9bf

  • 🌟 探索 giu:一个即时模式 GUI 框架

    在当今软件开发的世界中,用户界面的设计与实现是至关重要的。传统的保留模式(如 Qt 和 WxWidget)虽然功能强大,但却常常给开发者带来不小的复杂性。今天,我们将深入探索 giu,一个基于 Go 语言的即时模式 GUI 框架,它以其简洁性和高效性为开发者提供了一种全新的选择。

    🛠️ 什么是即时模式 GUI?

    即时模式 GUI 的核心理念是“控件不保留任何内部状态”。与之相对的保留模式 GUI 需要控件管理自己的状态,这通常使得界面更新和状态管理变得复杂。通过使用即时模式,开发者可以更直观地构建用户界面,简化了状态管理的负担。

    📐 简单易懂的布局系统

    在 giu 中,布局系统是声明式的,采用了简单易用的接口设计。每个控件都实现了 Widget 接口,并包含一个 Build() 方法来构建控件。你可以通过 giu.Layout 来管理多个控件,如下所示:

    type Widget interface {
        Build()
    }

    这使得创建动态 UI 部分变得异常简单,只需创建一个返回 giu.Layoutgiu.Widget 的函数即可。

    📏 如何在同一行中排列控件?

    如果你希望将多个控件并排放置,可以使用 giu.Row 控件。这个方法极大地简化了布局设计,使得 UI 的构建更加灵活和高效。

    giu.Row(
        giu.Button("按钮1"),
        giu.Button("按钮2"),
    )

    🧩 扩展控件的使用

    大多数控件的使用通过函数签名即可轻松理解。对于特定控件的详细用法,可以查看 examples/widgets/,其中包含了所有控件的用法示例。如果需要创建自定义控件,可以参考 examples/customwidget

    例如,创建一个自定义控件并保持状态的过程如下:

    type MyCustomWidget struct {
        state int
    }
    
    func (w *MyCustomWidget) Build() {
        giu.Button("Click Me").OnClick(func() {
            w.state++
        })
    }

    📌 控件 ID 的重要性

    在 giu 中,每个可交互控件(如按钮、输入框等)都必须有一个唯一的 ID。这是因为 ID 用于标识控件并确保事件的正确处理。例如,下面的代码展示了如何为按钮设置 ID:

    giu.Button("按钮").ID("unique_button")

    如果你尝试为两个按钮设置相同的 ID,将会导致第二个按钮的点击事件无法触发。因此,确保每个控件的 ID 唯一是至关重要的。

    🎉 事件处理机制

    为了处理鼠标和键盘事件,我们可以在控件下方放置 giu.Event() 来捕获这些事件。例如:

    giu.Button("点击我").OnClick(func() {
        fmt.Println("按钮被点击")
    })
    giu.Event().OnHover(func() {
        // 处理鼠标悬停事件
    })

    这样,我们就能轻松处理用户的交互行为,提升用户体验。

    🛠️ 多线程处理

    giu 框架的设计使得只有在用户事件发生时才会重绘 GUI。因此,如果在 goroutine 中更新变量,需要调用 giu.Update() 来立即通知 GUI 进行重绘。

    go func() {
        // 更新变量
        giu.Update()
    }()

    这种设计使得 giu 能够在性能和响应性之间取得良好的平衡。

    🗨️ 消息框和自定义对话框

    使用 giu.Msgbox 创建消息框非常简单。只需在窗口布局的最后嵌入 giu.PrepareMsgbox,就可以在任何地方调用 giu.Msgbox 来显示消息框。

    giu.SingleWindow().Layout(
        // 你的布局
        giu.PrepareMsgbox(),
    )

    如果需要更复杂的对话框,可以使用 giu.PopupModal 来构建自定义对话框,并通过 giu.OpenPopupgiu.CloseCurrentPopup 来控制它。

    🎨 样式和绘图

    GIU 还允许使用多种样式来自定义应用程序的外观。通过 giu.StyleSetter,你可以轻松设置样式参数。例如:

    giu.Style().SetColor("背景颜色", giu.Color{R. 255, G: 255, B: 255, A: 255})

    此外,框架还提供了绘图功能,借助 examples/canvas 可以实现各种绘图需求。

    🚀 部署:从 macOS 构建 Windows 可执行文件

    构建跨平台应用程序时,部署是一个重要的环节。下面是从 macOS 构建 Windows 可执行文件的基本步骤:

    1. 安装 mingw-w64
       brew install mingw-w64
    1. 创建构建脚本 build_win.sh
       cat > YourExeName.rc << EOL
       id ICON "./res/app_win.ico"
       GLFW_ICON ICON "./res/app_win.ico"
       EOL
    1. 执行构建命令:
       GOOS=windows GOARCH=amd64 go build -o YourExeName.exe

    通过这些简单的步骤,你就可以在不同的平台上轻松部署你的应用。

    📝 结论

    giu 框架以其简洁的设计和强大的功能为开发者提供了一种高效的 GUI 开发方式。无论是处理复杂的用户交互,还是实现灵活的布局,giu 都能够轻松应对。通过即时模式的理念,开发者能够专注于业务逻辑,而不必被繁琐的状态管理所困扰。

    📚 参考文献

    1. AllenDang. (2023). giu Wiki. GitHub. Retrieved from GitHub
    2. Go Programming Language. (2023). Retrieved from Go
    3. Qt Framework. (2023). Retrieved from Qt
    4. WxWidgets. (2023). Retrieved from WxWidgets
    5. GitHub Copilot. (2023). Retrieved from GitHub Copilot

    通过这些知识的积累,相信你会在 GUI 开发的道路上走得更加顺畅!

  • 🖼️ Go语言的图形魔法:GUI项目大揭秘

    在计算机世界的舞台上,编程语言如同魔法师挥舞着他们的法杖。而Go语言,这位年轻而充满活力的魔法师,正在用它独特的魔法为我们带来令人惊叹的图形体验。今天,让我们一同揭开Go语言GUI项目的神秘面纱,探索这个充满创意和可能性的领域。

    🌈 原生GUI:与操作系统的亲密对话

    想象一下,如果能让Go语言直接与操作系统对话,会是怎样的场景?这就是原生GUI绑定的魅力所在。

    🍎 与苹果的对话

    在Mac世界里,DarwinKit(曾用名MacDriver)犹如一位精通苹果语言的翻译官。它让Go开发者能够轻松驾驭Apple框架,编织出纯正的Mac应用。这就像是给Go穿上了一件苹果风格的魔法袍,让它能够自如地在苹果园中漫步。

    🪟 Windows的魔法窗口

    而在Windows王国,Walk和Windigo则是两位出色的向导。Walk,这位”Windows应用程序库套件”,就像是为Go打造的一套量身定制的Windows礼服。穿上它,Go就能以最地道的Windows风格亮相。Windigo则更像是一位精通Windows古老魔法的智者,它用地道的Win32 API为Go打开了通往Windows核心的大门。

    🐧 Linux的开放世界

    在自由开放的Linux大陆,GTK+的魔法尤为强大。gotk3和gotk4就像是两本详尽的GTK+魔法书,让Go能够掌握从GTK+3到GTK+4的全套咒语。有了它们,Go就能在Linux世界里随心所欲地呼风唤雨。

    🌍 跨平台的桥梁

    但魔法世界的精彩不止于此。一些强大的法器能让Go跨越不同的操作系统,在多个世界间自由穿梭。

    fyne就像是一把能够开启任何世界大门的万能钥匙。它用OpenGL描绘出的图形界面,能在各大主流平台上完美呈现。使用fyne,就如同给Go装上了一对翱翔于多个操作系统天空的翅膀。

    package main
    
    import (
        "fyne.io/fyne/v2"
        "fyne.io/fyne/v2/app"
        "fyne.io/fyne/v2/widget"
    )
    
    func main() {
        myApp := app.New()
        myWindow := myApp.NewWindow("Hello")
        myWindow.SetContent(widget.NewLabel("Hello Fyne!"))
        myWindow.ShowAndRun()
    }

    这段简洁的代码就能召唤出一个在任何平台上都能运行的图形窗口,是不是很神奇?

    而go-sciter则像是一位通晓HTML/CSS/JavaScript的魔法师。它让Go能够用网页技术绘制出绚丽的用户界面,同时保持着惊人的轻盈。这就像是在Go的魔法中注入了一股网络的活力,让它能够以网页的姿态出现在桌面应用中。

    🌠 HTML的魔法画布

    说到网页,我们不得不提到一类特殊的GUI魔法 – 基于HTML的GUI框架。这些框架就像是一座座连接Go与Web世界的彩虹桥,让Go能够踏上HTML的绚丽舞台。

    🚀 Electron的Go版本

    在这个领域里,go-astilectron就像是Go世界的Electron。它让开发者能够用Go搭建出框架,然后用HTML/CSS/JavaScript来装点内容。这就像是用Go搭建了一座坚固的城堡,然后用Web技术来装饰其中的每一个房间。结果就是,我们得到了既有Go的高效,又有Web的美观的应用程序。

    🌐 轻量级的Web GUI

    如果说go-astilectron是一座宏伟的城堡,那么Lorca就像是一间精巧的小屋。它不会捆绑庞大的Chromium,而是巧妙地利用用户机器上已安装的Chrome。这就像是Go学会了不带行李远行的技巧,能够以最轻盈的姿态展现Web的魅力。

    package main
    
    import (
        "github.com/zserge/lorca"
    )
    
    func main() {
        ui, _ := lorca.New("", "", 480, 320)
        defer ui.Close()
    
        ui.Bind("add", func(a, b int) int {
            return a + b
        })
    
        ui.Load("data:text/html,"+
            "<html><body><h1>Hello, World!</h1>"+
            "<script>"+
            "console.log('2 + 3 =', add(2, 3))"+
            "</script></body></html>")
    
        <-ui.Done()
    }

    看,只需要这么几行代码,我们就能创建一个轻量级的Web GUI,还能在JavaScript和Go之间传递数据,是不是很酷?

    🏗️ 渐进式Web应用的Go方案

    而Go-app则开辟了一条全新的道路。它让Go能够构建渐进式Web应用(PWA)。这就像是给了Go一双千里眼和顺风耳,让它能够同时照顾到Web和本地应用的需求。用Go-app构建的应用,不仅能在浏览器中运行,还能像原生应用一样安装在用户的设备上。这简直就是Go版的”一次编写,到处运行”!

    🎨 自绘GUI:Go的艺术画笔

    除了借助现有的GUI工具箱,Go还有能力自己拿起画笔,描绘出独一无二的图形界面。这就是自定义GUI库的魅力所在。

    🖌️ 即时模式GUI的魔法

    在这个领域里,Gio就像是一位善于速写的画家。它采用即时模式GUI的理念,每一帧都重新绘制整个界面。这种方式虽然看似繁琐,但却能带来极高的灵活性和性能。使用Gio,就像是给Go配备了一支能够瞬间完成整幅画作的神奇画笔。

    package main
    
    import (
        "gioui.org/app"
        "gioui.org/io/system"
        "gioui.org/layout"
        "gioui.org/widget/material"
    )
    
    func main() {
        go func() {
            w := app.NewWindow()
            th := material.NewTheme()
            for e := range w.Events() {
                switch e := e.(type) {
                case system.FrameEvent:
                    gtx := layout.NewContext(&e.Queue, e.Size)
    
                    title := material.H1(th, "Hello Gio")
                    title.Layout(gtx)
    
                    e.Frame(gtx.Ops)
                }
            }
        }()
        app.Main()
    }

    这段代码展示了Gio的核心理念:每一帧都重新计算和绘制所有元素。这种方法虽然看似重复,但却能带来惊人的灵活性和性能。

    🎭 Dear ImGui的Go化身

    而对于那些钟情于Dear ImGui的开发者来说,imgui-go则是一个完美的选择。它就像是为Go量身定制的一套戏服,让Go能够轻松扮演Dear ImGui的角色。通过imgui-go,开发者可以用Go来创建那些我们在游戏开发中常见的高效调试界面和工具面板。

    📊 二维图形:Go的艺术画廊

    在二维图形的世界里,Go更是展现出了惊人的才华。这里,每一个库都像是一位独具特色的画家,用自己的方式诠释着图形的魅力。

    🎨 绘图大师draw2d

    draw2d就像是一位多才多艺的画家。无论是在画布(images)上挥毫,还是在PDF上作画,亦或是在OpenGL的画布上创作,它都能游刃有余。使用draw2d,就像是给Go配备了一套全能的绘图工具,让它能在各种”画布”上自由发挥。

    🌈 简约而不简单的gg

    如果说draw2d是多才多艺,那么gg则是一位崇尚简约的艺术家。它用最简洁的API,却能创作出令人惊叹的二维图形。使用gg,就像是用最基本的颜料,却能调配出最绚丽的色彩。

    package main
    
    import (
        "github.com/fogleman/gg"
    )
    
    func main() {
        dc := gg.NewContext(1000, 1000)
        dc.DrawCircle(500, 500, 400)
        dc.SetRGB(0, 0, 0)
        dc.Fill()
        dc.SavePNG("circle.png")
    }

    看,只需要几行代码,我们就能创建出一个完美的圆。gg的简洁和强大可见一斑。

    📈 数据可视化的利器

    在数据可视化领域,go-chart和gonum/plot则是两位出色的数据艺术家。go-chart专注于时间序列和连续线图的绘制,而gonum/plot则提供了更广泛的绘图功能。有了它们,Go就能将枯燥的数据转化为生动直观的图表,让数据说话变得如此简单。

    🎮 游戏开发:Go的欢乐世界

    游戏开发,这个充满想象力和创造力的领域,同样少不了Go的身影。在这里,Go不仅能够创造出精彩的游戏,还能为游戏开发提供强大的工具支持。

    🕹️ 简单而强大的Ebiten

    Ebiten就像是一位游戏界的魔术师。它用简单的API,却能变出各种精彩的2D游戏。使用Ebiten,开发者可以专注于游戏逻辑和创意,而不必被繁琐的底层细节所困扰。

    package main
    
    import (
        "github.com/hajimehoshi/ebiten/v2"
    )
    
    type Game struct{}
    
    func (g *Game) Update() error {
        // 更新游戏逻辑
        return nil
    }
    
    func (g *Game) Draw(screen *ebiten.Image) {
        // 绘制游戏画面
    }
    
    func (g *Game) Layout(outsideWidth, outsideHeight int) (screenWidth, screenHeight int) {
        return 320, 240
    }
    
    func main() {
        ebiten.SetWindowSize(640, 480)
        ebiten.SetWindowTitle("Hello, World!")
        if err := ebiten.RunGame(&Game{}); err != nil {
            panic(err)
        }
    }

    这段代码展示了Ebiten的基本结构。通过实现几个简单的接口,我们就能创建一个基本的游戏框架。Ebiten的魔力就在于,它把复杂的游戏开发过程,简化成了这样几个直观的函数。

    🌟 与Godot的完美结合

    对于那些喜欢使用成熟游戏引擎的开发者来说,godot-go无疑是一个完美的选择。它就像是在Go和Godot引擎之间搭建了一座桥梁,让开发者能够用Go的方式来驾驭Godot的强大功能。这种结合,既保留了Go的高效和简洁,又获得了Godot丰富的游戏开发资源和工具。

    📸 图像处理:Go的魔法相机

    在图像处理领域,Go同样展现出了惊人的才华。标准库中的image包就像是一台功能齐全的数码相机,能够轻松处理各种基本的图像操作。而image子仓库则提供了更多高级功能,就像是为这台相机增加了各种专业镜头,让Go在图像处理领域的能力更上一层楼。

    package main
    
    import (
        "image"
        "image/color"
        "image/png"
        "os"
    )
    
    func main() {
        width, height := 200, 200
        img := image.NewRGBA(image.Rect(0, 0, width, height))
    
        for x := 0; x < width; x++ {
            for y := 0; y < height; y++ {
                img.Set(x, y, color.RGBA{uint8(x), uint8(y), 100, 255})
            }
        }
    
        f, _ := os.Create("gradient.png")
        png.Encode(f, img)
    }

    这段代码展示了如何使用Go的标准库创建一个简单的渐变图像。看似简单的几行代码,却蕴含了强大的图像处理能力。

    🖥️ 终端UI:Go的字符艺术

    最后,让我们把目光转向一个独特的领域 – 终端UI。在这个看似古老的领域里,Go同样绽放出了独特的魅力。

    tcell和tview就像是两位终端世界的魔术师。它们能够在朴素的字符界面上,变出各种精美的用户界面。使用它们,开发者可以创建出既复古又现代,既简约又功能强大的终端应用。

    package main
    
    import (
        "github.com/rivo/tview"
    )
    
    func main() {
        app := tview.NewApplication()
        textView := tview.NewTextView().
            SetDynamicColors(true).
            SetRegions(true).
            SetChangedFunc(func() {
                app.Draw()
            })
    
        textView.SetText("[yellow]Hello, [red]colorful [green]world!")
    
        if err := app.SetRoot(textView, true).Run(); err != nil {
            panic(err)
        }
    }

    这段代码展示了如何使用tview创建一个简单而美观的终端UI。通过简单的标记语言,我们就能在终端中呈现出丰富多彩的文本效果。

    🌟 结语:Go的图形魔法之旅

    从原生GUI到Web技术,从2D图形到游戏开发,从图像处理到终端UI,Go语言在图形和GUI领域展现出了令人惊叹的多样性和能力。这不仅仅是技术的进步,更是创意和想象力的胜利。每一个库,每一个框架,都像是Go魔法世界中的一颗璀璨明珠,闪耀着独特的光芒。

    作为开发者,我们有幸生活在这样一个精彩的时代,能够利用这些强大的工具去创造,去实现我们的想象。Go语言的图形魔法之旅才刚刚开始,未来还有更多的可能性等待我们去探索。让我们一起,在Go的魔法世界里,继续这场激动人心的冒险吧!

    参考文献

    1. Go Graphics Projects Repository. (2023). GitHub. https://github.com/go-graphics/go-gui-projects
    2. Fyne – Cross platform GUI in Go inspired by Material Design. (2023). Fyne.io. https://fyne.io/
    3. Gio – Immediate Mode GUI in Go. (2023). Gioui.org. https://gioui.org/
    4. Ebiten – A dead simple 2D game library for Go. (2023). GitHub. https://github.com/hajimehoshi/ebiten
    5. Go image package documentation. (2023). Golang.org. https://golang.org/pkg/image/
人生梦想 - 关注前沿的计算机技术 acejoy.com