Bootstrap

Go语言入门

零、Linux下Go的安装

1.下载、解压

wget https://golang.org/dl/go1.20.5.linux-amd64.tar.gz
sudo tar -C /usr/local -xzf go1.20.5.linux-amd64.tar.gz

2.添加环境变量

(1)编辑 shell 配置文件

vim ~/.bashrc

(2)写入以下内容

export GOPATH=$HOME/go
export PATH=$PATH:/usr/local/go/bin:$GOPATH/bin

(3)运行一下

source ~/.bashrc

3.验证安装

go version

示例输出:

go version go1.20.4 linux/amd64

4.初始化Go模块

(1)cd到项目目录

确保你在包含 ui.go 文件的项目根目录下。例如:

cd ~/termui

(2)初始化模块

使用 go mod init 命令初始化模块,并指定模块路径。模块路径通常是你的代码仓库地址,但对于本地项目,你可以使用任意名称。

go mod init mytermui

在这里插入图片描述

注意:将 mytermui 替换为你项目的实际模块名。如果你有特定的仓库地址,可以使用类似 github.com/你的用户名/项目名 的格式。

go mod init github.com/yourusername/mytermui

这将在当前目录下创建一个 go.mod 文件,内容类似于:

module github.com/yourusername/mytermui

go 1.20

(3)获取依赖包

使用 go get 命令获取 termui/v3 包及其依赖:

go get github.com/gizak/termui/v3@latest

这将下载 termui 的最新版本,并更新 go.mod 和 go.sum 文件。

在这里插入图片描述


(4)清理和验证依赖

运行以下命令以确保所有依赖都已正确安装:

go mod tidy

这个命令会清理 go.mod 和 go.sum 文件,移除未使用的依赖并添加缺失的依赖。


(5)检查 go.mod 文件

确保 go.mod 文件中正确记录了 termui 依赖。例如:

module github.com/yourusername/mytermui

go 1.20

require github.com/gizak/termui/v3 v3.1.0 // 具体版本可能不同

(6)介绍 go.mod 和 go.sum 文件

(1)go.mod文件
go.mod文件用于定义当前 Go 项目的模块信息,包括模块的路径、Go 语言的版本以及项目所依赖的其他模块及其版本。它是 Go Modules 的核心配置文件,确保项目依赖的一致性和可重复构建。

module github.com/yourusername/yourproject

go 1.20

require (
    github.com/gizak/termui/v3 v3.1.0
    github.com/sirupsen/logrus v1.8.1
)

(2)go.sum文件
go.sum文件用于记录项目所依赖的每个模块及其具体版本的 校验和(checksum)。它确保在不同时间和不同环境中下载的依赖包内容一致,防止依赖包被篡改或出现版本不一致的问题,从而提高项目构建的安全性和可靠性。

github.com/gizak/termui/v3 v3.1.0 h1:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
github.com/gizak/termui/v3 v3.1.0/go.mod h1:yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
github.com/sirupsen/logrus v1.8.1 h1:zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
github.com/sirupsen/logrus v1.8.1/go.mod h1:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

5.项目目录结构

termui/               # 项目根目录
├── go.mod            # 模块定义文件
├── go.sum            # 依赖校验文件
└── ui.go             # 你的主程序文件



一、感性认识

1.从 Hello world 开始

1.代码

package main

import "fmt"

func main() {
    fmt.Println("Hello, World!")
}

fmt 是 Go 标准库中的一个包,提供了格式化I/O功能。它包含了用于格式化文本和实现基本输入输出的函数,可以方便地处理字符串、数字等类型的格式化打印以及解析。


2.运行

go run hello.go

2.加法函数

1.代码

package main

import "fmt"

// add 函数接收两个整数参数,并返回它们的和
func add(a int, b int) int {
    return a + b
}

func main() {
    sum := add(5, 3)
    fmt.Printf("5 + 3 = %d\n", sum)
}

2.运行

go run add.go



二、Go语法

1.变量的声明与初始化

(1):=:自动推断类型

1.概念
:= 允许你在声明变量的同时进行初始化,并且无需显式指定变量的类型。Go 编译器会根据赋值的值自动推断变量的类型。


2.使用范围:函数内部
:= 只能在函数内部使用,不能用于包级别的变量声明。而var可以在函数内部和包级别使用


3.代码

package main

import "fmt"

func main() {
    // 使用 := 声明并初始化变量
    message := "Hello, Go!"
    count := 10
    pi := 3.14

    fmt.Println(message)
    fmt.Println("Count:", count)
    fmt.Println("Pi:", pi)
}

(2)var:手动指定类型

1.举例

var count int = 10

2.适用范围:包级别、函数内部

package main

import "fmt"

// 包级别变量声明,必须使用 var
var globalMessage = "Hello from package level!"

func main() {
    fmt.Println(globalMessage)
}

2.基础数据类型

(1)布尔型(Boolean)

var isReady bool = true
fmt.Println(isReady) // 输出: true

(2)整型(Integer)

1.有符号整型
int, int8, int16, int32, int64

范围:
int8:-128 ~ 127
int16:-32,768 ~ 32,767
int32:-2,147,483,648 ~ 2,147,483,647
int64:-9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807
int:与系统架构相关,通常为 int32 或 int64


2.无符号整型
类型:uint, uint8, uint16, uint32, uint64

范围:
uint8(等同于字节 byte):0 ~ 255
uint16:0 ~ 65,535
uint32:0 ~ 4,294,967,295
uint64:0 ~ 18,446,744,073,709,551,615
uint:与系统架构相关

var a int = 100
var b uint8 = 255
fmt.Println(a, b) // 输出: 100 255

(3)浮点型(Floating-point)

类型:float32, float64
精度:
float32:约 6-7 位小数精度
float64:约 15-16 位小数精度

var pi float64 = 3.141592653589793
fmt.Println(pi) // 输出: 3.141592653589793

(4)复数类型(Complex)

类型:complex64, complex128
组成:
complex64:由两个 float32 组成
complex128:由两个 float64 组成

var c complex128 = 1 + 2i
fmt.Println(c)           // 输出: (1+2i)
fmt.Println(real(c))     // 输出: 1
fmt.Println(imag(c))     // 输出: 2

(5)字符串(String)

类型:string
特点:字符串是不可变的,存储 UTF-8 编码的字节序列。

var s string = "Hello, 世界"
fmt.Println(s)       // 输出: Hello, 世界
fmt.Println(len(s))  // 输出: 13(一个汉字占3字节)

3.变量、常量

(1)变量

1.使用 var 关键字

var 变量名 数据类型 = 初始值
var a int = 10
var b string = "hello"
var c bool = true

2.省略数据类型(自动推导类型)

var 变量名 = 初始值
var x = 3.14 // 自动推导为 float64
var y = "Go" // 自动推导为 string

3.简短声明(仅限函数内部)

变量名 := 初始值
z := 42 // 自动推导为 int
message := "Welcome to Go"

(2)常量

常量:用const关键字修饰

const 常量名 数据类型 =
const pi float64 = 3.14159
const language = "Go"

4.控制结构

(1)条件语句

package main

import "fmt"

func main(){
    var age int = 25
    fmt.Println("age = ", age)

    if age < 18{
        fmt.Println("未成年")
    }else if age <= 30{
        fmt.Println("青年")
    }else if age <= 60{
        fmt.Println("中年")
    }else{
        fmt.Println("老年")
    }
}

(2)循环语句

①for循环

1.Go 语言的循环控制结构非常简洁,它只有一种循环结构:for 循环。

for 初始语句; 条件语句; 后续语句 {
    // 循环体代码
}

2.也可以省略 初始语句 和 后续语句,只保留 条件语句,这种形式相当于其他语言中的 while 循环。

for 条件语句 {
    // 循环体代码
}

3.也可以写一个无限循环,通常在某些特定条件下需要手动跳出循环。Go 语言通过 for 语句支持无限循环。

for {
    // 无限循环体
}

②range 循环:用于遍历数组、切片、字符串、map 等数据结构

range 循环:Go 语言还提供了 range 关键字,可以在循环中迭代数组、切片、字符串、map 或通道等集合类型。


5.函数

进行函数封装

package main

import "fmt"

func printBool(b bool){
    fmt.Println(b)
}

func printInts(a int, b uint8){
    fmt.Println(a, b)
}

func printFloat(f float64){
    fmt.Println(f)
}

func printComplex(c complex128){
    fmt.Println(c);
    fmt.Println(real(c))
    fmt.Println(imag(c))
}

func printString(s string){
    fmt.Println(s)
    fmt.Println(len(s))
}


func main(){
    var isReady bool = true

    var a int = 100
    var b uint8 = 255

    var pi float64 = 3.14159265358979

    var c complex128 = 1 + 2i

    var s string = "Hello, 世界"

    printBool(isReady)
    printInts(a, b)
    printFloat(pi)
    printComplex(c)
    printString(s)
}



三、Go的数据结构

(一) 内置数据结构

1.数组 (Array)

数组是具有固定长度且元素类型相同的序列。在 Go 中,数组的长度是类型的一部分,这意味着 [5]int 和 [10]int 是不同的类型。

特点:
固定长度,编译时确定。
值类型,数组赋值会复制所有元素。

package main

import "fmt"

func main() {
    var arr [3]int
    arr[0] = 10
    arr[1] = 20
    arr[2] = 30

    fmt.Println(arr)       // 输出: [10 20 30]
    fmt.Println(len(arr))  // 输出: 3
}

2.切片 (Slice)

切片是对数组的一个动态窗口,具有可变长度和容量。切片是 Go 中使用最广泛的数据结构之一。

特点:
动态大小,基于底层数组实现。
引用类型,切片之间共享底层数组。
内置的 append 函数用于添加元素

package main

import "fmt"

func main() {
    // 创建切片
    s := []int{1, 2, 3, 4, 5}
    fmt.Println(s)        // 输出: [1 2 3 4 5]
    fmt.Println(len(s))   // 输出: 5
    fmt.Println(cap(s))   // 输出: 5

    // 添加元素
    s = append(s, 6)
    fmt.Println(s)        // 输出: [1 2 3 4 5 6]
    fmt.Println(len(s))   // 输出: 6
    fmt.Println(cap(s))   // 根据内存情况,可能增加到10

    // 切片的截取
    sub := s[1:4]
    fmt.Println(sub)      // 输出: [2 3 4]
}

3.映射 (Map)

映射是一种键值对(key-value)的无序集合,类似于其他语言中的哈希表或字典。

特点:
无序,基于哈希实现。
键必须是可比较的类型(如字符串、整数、指针等)。
动态大小。

package main

import "fmt"

func main() {
    // 创建映射
    m := make(map[string]int)

    // 添加键值对
    m["Alice"] = 25
    m["Bob"] = 30

    // 访问元素
    fmt.Println(m["Alice"]) // 输出: 25

    // 检查键是否存在
    age, exists := m["Charlie"]
    if exists {
        fmt.Println(age)
    } else {
        fmt.Println("Charlie 不存在")
    }

    // 删除键值对
    delete(m, "Bob")
    fmt.Println(m) // 输出: map[Alice:25]
}

4.结构体 (Struct)

结构体是由一组字段组成的复合数据类型,类似于其他语言中的类或记录。

特点:
支持嵌套和匿名字段。
可以组合不同类型的数据。

package main

import "fmt"

// 定义结构体
type Person struct {
    Name string
    Age  int
}

func main() {
    // 创建结构体实例
    p := Person{Name: "Alice", Age: 30}
    fmt.Println(p) // 输出: {Alice 30}

    // 访问字段
    fmt.Println(p.Name) // 输出: Alice
    fmt.Println(p.Age)  // 输出: 30

    // 修改字段
    p.Age = 31
    fmt.Println(p.Age) // 输出: 31
}

5.指针 (Pointer)

指针存储变量的内存地址,允许直接操作内存中的数据。

指针类型以 * 表示。
允许在函数间传递引用,避免复制大量数据。

package main

import "fmt"

func main() {
    var a int = 10
    var p *int = &a

    fmt.Println(p)  // 输出: 内存地址,如 0xc0000140b8
    fmt.Println(*p) // 输出: 10

    *p = 20
    fmt.Println(a) // 输出: 20
}

(二) Go库提供的数据结构

1.container/list:双向链表

package main

import (
    "container/list"
    "fmt"
)

func main() {
    l := list.New()
    l.PushBack("Go")
    l.PushBack("is")
    l.PushBack("awesome")

    for e := l.Front(); e != nil; e = e.Next() {
        fmt.Println(e.Value)
    }
}

2.container/heap:堆/优先队列

package main

import (
    "container/heap"
    "fmt"
)

// IntHeap 是一个最小堆
type IntHeap []int

func (h IntHeap) Len() int           { return len(h) }
func (h IntHeap) Less(i, j int) bool { return h[i] < h[j] }
func (h IntHeap) Swap(i, j int)      { h[i], h[j] = h[j], h[i] }

func (h *IntHeap) Push(x interface{}) {
    *h = append(*h, x.(int))
}

func (h *IntHeap) Pop() interface{} {
    old := *h
    n := len(old)
    x := old[n-1]
    *h = old[0 : n-1]
    return x
}

func main() {
    h := &IntHeap{5, 3, 2, 4}
    heap.Init(h)
    heap.Push(h, 1)
    fmt.Printf("最小值: %d\n", (*h)[0])

    for h.Len() > 0 {
        fmt.Printf("%d ", heap.Pop(h))
    }
    // 输出: 最小值: 1
    // 1 2 3 4 5
}

3.container/ring:环形链表

package main

import (
    "container/ring"
    "fmt"
)

func main() {
    r := ring.New(5)
    for i := 0; i < r.Len(); i++ {
        r.Value = i
        r = r.Next()
    }

    r.Do(func(x interface{}) {
        fmt.Println(x)
    })
}



四、Go的三种编译方式

Go 语言是静态编译型语言,确实需要编译。与一些解释型语言不同,Go 源代码在执行之前必须先通过编译器转换成机器码,生成可以直接由计算机硬件执行的二进制文件。编译过程会检查代码中的语法错误和其他编译时错误,并将源代码优化为高效的机器码。

Go 提供了简单的工具链来管理和编译项目,主要的编译命令包括:go run、go build、go install。

当你编写 Go 应用程序时,你确实需要使用这些命令之一来编译你的代码。不过,Go 的编译速度非常快,这得益于其设计和实现,使得开发体验更加流畅,有时候甚至让人感觉像是在使用解释型语言。

总结来说,Go 语言不仅需要编译,而且它有着高效的编译工具链,让开发者可以快速地从编写代码到运行程序。


1.go run

编译并立即运行指定的 Go 文件,不会留下编译后的二进制文件


2.go build

go build:编译当前包或指定的 Go 文件。如果包含 package main 和 main 函数,则会生成一个可执行文件。


3.go install

go install:类似于 go build,但它还会将生成的二进制文件复制到 $GOPATH/bin 或 $GOBIN 环境变量所指定的目录中,使得可以从任何地方运行该程序。



五、Go与C++混合编程

1.原理

Go 提供了 cgo 工具,允许 Go 代码调用 C 函数。
由于 C++ 与 C 兼容性较高,通过 extern "C" 使 Go 调用 C++ 函数。

链接机制:将 C++ 代码编译为静态库(如 .a 或 .lib),然后在 Go 中链接使用。


2.步骤

1.创建头文件 (hello.h)
为了让 cgo 正确识别并调用 C++ 函数,建议创建一个头文件 hello.h,其中声明了 HelloWorld 函数

#ifndef HELLO_H
#define HELLO_H

#ifdef __cplusplus
extern "C" {
#endif

void HelloWorld();

#ifdef __cplusplus
}
#endif

#endif // HELLO_H

2.编写 C++ 源文件 (hello.cpp)

#include <iostream>
#include "hello.h"

using std::cout;
using std::endl;

extern "C" void HelloWorld(){
    cout << "Hello World from C++!" << endl;
    cout << "Just for test!" << endl;
    cout << "Cpp with GO!" << endl;
}

3.编写 Go 代码 (main.go)
在 main.go 中使用 cgo 调用 C++ 的 HelloWorld 函数

注意:Go中的这段注释是必要的,指明了链接器。
注意:注释块要和import "C"是连着的,不能空行。否则 go build -o main main.go 会报错 could not determine kind of name for C.HelloWorld

package main

/*
#cgo CXXFLAGS: -std=c++11
#cgo LDFLAGS: -L. -lhello -lstdc++
#include "hello.h"
*/
import "C"

func main() {
    C.HelloWorld()
}

4.编译 C++ 代码为目标文件
将 hello.cpp 编译为目标文件 hello.o。

g++ -c hello.cpp -o hello.o

-c:只编译,不链接
-o:指定输出文件名
在本例中,hello.o 是指定的目标文件名。如果不使用 -o 选项,默认情况下编译器会给目标文件命名为源文件名去掉扩展名再加上 .o 扩展名


5.创建静态库.a
将目标文件打包成静态库 libhello.a

ar rcs libhello.a hello.o

ar:GNU 的归档工具,用于创建、修改和提取静态库 (.a 文件)
rcs:选项。
①r选项:告诉 ar 将指定的目标文件插入到静态库中。如果静态库不存在,则会创建它;如果静态库已经存在,并且其中已有同名的目标文件,那么该选项将替换旧的目标文件。
②c选项:表示如果静态库尚不存在,则创建一个新的静态库。当与 r 一起使用时,确保即使静态库不存在也会被创建。
③s选项:指示 ar 为静态库创建索引。这个索引可以帮助链接器更快地找到库中的符号。这对于大型库来说尤其有用,因为它可以加速链接过程。


6.编译 Go 代码

go build -o main main.go

go官方文档推荐的顺序:go build -o output_filename source_files…
若go build 源文件名,不加-o 输出文件名。则会输出 源文件名.go中的源文件名。例如 go build main.go输出的可执行程序就是main。


7.运行Go程序

./main

8.预期输出

Hello World from C++!



六、termui

1.官网

termui是Go语言的终端ui

官网库:https://github.com/gizak/termui

在这里插入图片描述


2.代码及效果

(1)HelloWorld框

在这里插入图片描述

package main

import (
        "log"

        ui "github.com/gizak/termui/v3"
        "github.com/gizak/termui/v3/widgets"
)

func main() {
        if err := ui.Init(); err != nil {
                log.Fatalf("failed to initialize termui: %v", err)
        }
        defer ui.Close()

        p := widgets.NewParagraph()
        p.Text = "Hello World!"
        p.SetRect(0, 0, 25, 5)

        ui.Render(p)

        for e := range ui.PollEvents() {
                if e.Type == ui.KeyboardEvent {
                        break
                }
        }
}

(2)彩色文字框

在这里插入图片描述

package main

import (
    ui "github.com/gizak/termui/v3"
    "github.com/gizak/termui/v3/widgets"
    "log"
)

func main() {
    if err := ui.Init(); err != nil {
        log.Fatalf("failed to initialize termui: %v", err)
    }
    defer ui.Close()

    // 创建一个新的段落(Paragraph)组件
    p := widgets.NewParagraph()
    p.Text = "Press q to quit demo"
    p.SetRect(0, 0, 50, 3) // 设置位置和大小
    p.TextStyle.Fg = ui.ColorWhite
    p.Border = true
    p.BorderStyle.Fg = ui.ColorCyan
    p.Title = "Text Box"

    ui.Render(p)

    // 事件循环
    uiEvents := ui.PollEvents()
    for {
        e := <-uiEvents
        if e.Type == ui.KeyboardEvent {
            switch e.ID {
            case "q", "<C-c>":
                return
            }
        }
    }
}

(3)实时数据动态变化

在这里插入图片描述

package main

import (
    "log"
    "math/rand"
    "time"

    ui "github.com/gizak/termui/v3"
    "github.com/gizak/termui/v3/widgets"
)

func main() {
    // 初始化 termui
    if err := ui.Init(); err != nil {
        log.Fatalf("无法初始化 termui: %v", err)
    }
    defer ui.Close()

    // 创建标题
    title := widgets.NewParagraph()
    title.Text = "🎉 欢迎使用炫酷的 TermUI 应用 🎉"
    title.TextStyle = ui.NewStyle(ui.ColorYellow, ui.ColorClear, ui.ModifierBold)
    title.SetRect(0, 0, 50, 3)
    title.Border = false

    // 创建段落
    paragraph := widgets.NewParagraph()
    paragraph.Text = "这是一个使用 TermUI 构建的炫酷终端界面示例。按任意键退出。"
    paragraph.SetRect(0, 3, 50, 7)
    paragraph.TextStyle = ui.NewStyle(ui.ColorWhite)
    paragraph.Border = false

    // 创建条形图
    barchart := widgets.NewBarChart()
    barchart.Title = "实时数据"
    barchart.SetRect(0, 7, 50, 20)
    barchart.BarWidth = 6
    barchart.BarGap = 2
    barchart.BarColors = []ui.Color{ui.ColorGreen, ui.ColorYellow, ui.ColorRed}
    barchart.LabelStyles = []ui.Style{ui.NewStyle(ui.ColorBlack)}
    barchart.NumStyles = []ui.Style{ui.NewStyle(ui.ColorBlack)}
    barchart.BorderStyle = ui.NewStyle(ui.ColorCyan)

    // 创建列表
    list := widgets.NewList()
    list.Title = "功能列表"
    list.Rows = []string{
        "• 显示标题和段落",
        "• 实时更新条形图",
        "• 显示功能列表",
        "• 支持键盘事件退出",
    }
    list.SetRect(50, 0, 80, 10)
    list.TextStyle = ui.NewStyle(ui.ColorWhite)
    list.BorderStyle = ui.NewStyle(ui.ColorMagenta)

    // 设置初始条形图数据
    barchart.Data = generateRandomData()

    // 渲染初始界面
    ui.Render(title, paragraph, barchart, list)

    // 启动一个定时器,每一秒更新一次条形图数据
    ticker := time.NewTicker(time.Second).C
    go func() {
        for {
            select {
            case <-ticker:
                barchart.Data = generateRandomData()
                ui.Render(barchart)
            }
        }
    }()

    // 监听键盘事件,按任意键退出
    for e := range ui.PollEvents() {
        if e.Type == ui.KeyboardEvent {
            break
        }
    }
}

// 生成随机数据用于条形图
func generateRandomData() []float64 {
    rand.Seed(time.Now().UnixNano())
    data := make([]float64, 5)
    for i := range data {
        data[i] = rand.Float64() * 100
    }
    return data
}

(4)折线图、打印系统信息

在这里插入图片描述

package main

import (
    "fmt"
    "log"
    "math/rand"
    "runtime"
    "time"

    ui "github.com/gizak/termui/v3"
    "github.com/gizak/termui/v3/widgets"
)

// padCenter 用于手动居中文本
func padCenter(text string, width int) string {
    if len(text) >= width {
        return text
    }
    padding := (width - len(text)) / 2
    return fmt.Sprintf("%*s%s%*s", padding, "", text, padding, "")
}

func main() {
    // 初始化 termui
    if err := ui.Init(); err != nil {
        log.Fatalf("无法初始化 termui: %v", err)
    }
    defer ui.Close()

    // 获取初始终端尺寸
    termWidth, termHeight := ui.TerminalDimensions()

    // 创建标题
    title := widgets.NewParagraph()
    title.Text = padCenter("🚀 炫酷的 TermUI 仪表盘示例 🚀", termWidth)
    title.TextStyle = ui.NewStyle(ui.ColorCyan, ui.ColorBlack, ui.ModifierBold)
    title.SetRect(0, 0, termWidth, 3)
    title.Border = false

    // 创建段落
    paragraph := widgets.NewParagraph()
    paragraph.Text = "欢迎使用 TermUI 构建的炫酷终端界面示例。按 'q' 键退出。"
    paragraph.SetRect(0, 3, termWidth, 6)
    paragraph.TextStyle = ui.NewStyle(ui.ColorWhite)
    paragraph.Border = false

    // 创建条形图
    barchart := widgets.NewBarChart()
    barchart.Title = "实时数据"
    barchart.SetRect(0, 6, termWidth/2, 18) // 调整高度
    barchart.BarWidth = 6
    barchart.BarGap = 2
    barchart.BarColors = []ui.Color{ui.ColorGreen, ui.ColorYellow, ui.ColorRed}
    barchart.LabelStyles = []ui.Style{ui.NewStyle(ui.ColorBlack)}
    barchart.NumStyles = []ui.Style{ui.NewStyle(ui.ColorBlack)}
    barchart.BorderStyle = ui.NewStyle(ui.ColorMagenta)
    barchart.Data = generateRandomData()

    // 创建折线图
    linechart := widgets.NewPlot()
    linechart.Title = "数据趋势"
    linechart.SetRect(termWidth/2, 6, termWidth, 18) // 调整高度
    linechart.AxesColor = ui.ColorWhite
    linechart.LineColors[0] = ui.ColorYellow
    linechart.Data = make([][]float64, 1)
    linechart.Data[0] = generateLineData()

    // 创建仪表盘
    gauge := widgets.NewGauge()
    gauge.Title = "CPU 使用率"
    gauge.SetRect(0, 18, termWidth/2, 21) // 调整高度
    gauge.BarColor = ui.ColorRed
    gauge.BorderStyle = ui.NewStyle(ui.ColorMagenta)
    gauge.LabelStyle = ui.NewStyle(ui.ColorWhite)
    gauge.Percent = 0

    // 创建列表
    list := widgets.NewList()
    list.Title = "功能列表"
    list.Rows = []string{
        "• 显示标题和段落",
        "• 实时更新条形图",
        "• 显示数据趋势折线图",
        "• 显示 CPU 使用率仪表盘",
        "• 显示功能列表和数据表",
        "• 支持键盘事件退出",
    }
    list.SetRect(termWidth/2, 18, termWidth, 21) // 调整高度
    list.TextStyle = ui.NewStyle(ui.ColorWhite)
    list.BorderStyle = ui.NewStyle(ui.ColorGreen)

    // 创建表格
    table := widgets.NewTable()
    table.Title = "系统信息"
    table.Rows = [][]string{
        {"名称", "值"},
        {"操作系统", "Linux"},
        {"架构", "x86_64"},
        {"Go 版本", runtimeVersion()},
        {"TermUI 版本", termuiVersion()},
    }
    table.TextStyle = ui.NewStyle(ui.ColorWhite)
    table.RowSeparator = false
    table.FillRow = true
    table.SetRect(0, 21, termWidth, termHeight-2) // 调整高度,避免最底部留白
    table.BorderStyle = ui.NewStyle(ui.ColorBlue)

    // 创建 Grid 布局
    grid := ui.NewGrid()
    grid.SetRect(0, 0, termWidth, termHeight)
    grid.Set(
        ui.NewRow(0.07,
            ui.NewCol(1.0, title),
        ),
        ui.NewRow(0.07,
            ui.NewCol(1.0, paragraph),
        ),
        ui.NewRow(0.45,
            ui.NewCol(0.5, barchart),
            ui.NewCol(0.5, linechart),
        ),
        ui.NewRow(0.15,
            ui.NewCol(0.5, gauge),
            ui.NewCol(0.5, list),
        ),
        ui.NewRow(0.16, // 减少表格占比
            ui.NewCol(1.0, table),
        ),
    )

    // 渲染初始界面
    ui.Render(grid)

    // 初始化数据更新通道
    ticker := time.NewTicker(time.Second).C

    // 数据趋势折线图数据缓冲
    lineData := make([]float64, 50)
    for i := range lineData {
        lineData[i] = rand.Float64() * 100
    }

    // 启动一个 goroutine 进行数据更新
    go func() {
        for {
            select {
            case <-ticker:
                // 更新条形图数据
                barchart.Data = generateRandomData()

                // 更新折线图数据
                newValue := rand.Float64() * 100
                lineData = append(lineData[1:], newValue)
                linechart.Data[0] = lineData

                // 更新仪表盘数据
                gauge.Percent = int(newValue)

                // 重新渲染界面
                ui.Render(grid)
            }
        }
    }()

    // 监听键盘事件
    uiEvents := ui.PollEvents()
    for {
        e := <-uiEvents
        switch e.ID {
        case "q", "<C-c>":
            return
        case "<Resize>":
            // 获取新的终端尺寸
            termWidth, termHeight = ui.TerminalDimensions()

            // 更新标题文本居中
            title.Text = padCenter("🚀 炫酷的 TermUI 仪表盘示例 🚀", termWidth)

            // 更新所有小部件的 SetRect 参数
            title.SetRect(0, 0, termWidth, 3)
            paragraph.SetRect(0, 3, termWidth, 6)
            barchart.SetRect(0, 6, termWidth/2, 18)
            linechart.SetRect(termWidth/2, 6, termWidth, 18)
            gauge.SetRect(0, 18, termWidth/2, 21)
            list.SetRect(termWidth/2, 18, termWidth, 21)
            table.SetRect(0, 21, termWidth, termHeight-2) // 调整高度

            // 重新设置 Grid 布局比例
            grid.SetRect(0, 0, termWidth, termHeight)
            grid.Set(
                ui.NewRow(0.07,
                    ui.NewCol(1.0, title),
                ),
                ui.NewRow(0.07,
                    ui.NewCol(1.0, paragraph),
                ),
                ui.NewRow(0.45,
                    ui.NewCol(0.5, barchart),
                    ui.NewCol(0.5, linechart),
                ),
                ui.NewRow(0.15,
                    ui.NewCol(0.5, gauge),
                    ui.NewCol(0.5, list),
                ),
                ui.NewRow(0.16, // 减少表格占比
                    ui.NewCol(1.0, table),
                ),
            )

            // 重新渲染界面
            ui.Clear()
            ui.Render(grid)
        }
    }
}

// generateRandomData 生成随机条形图数据
func generateRandomData() []float64 {
    data := make([]float64, 5)
    for i := range data {
        data[i] = rand.Float64() * 100
    }
    return data
}

// generateLineData 生成初始折线图数据
func generateLineData() []float64 {
    data := make([]float64, 50)
    for i := range data {
        data[i] = rand.Float64() * 100
    }
    return data
}

// runtimeVersion 获取 Go 版本
func runtimeVersion() string {
    return fmt.Sprintf("%s", runtime.Version())
}

// termuiVersion 获取 termui 版本
func termuiVersion() string {
    return "v3.1.0" // 请根据实际使用的 termui 版本进行修改
}
;