28、交互式番茄钟应用开发指南

交互式番茄钟应用开发指南

在开发交互式应用时,界面布局的组织和交互逻辑的构建是关键步骤。本文将详细介绍如何开发一个交互式番茄钟应用,包括界面布局的设计、交互逻辑的实现以及使用 Cobra 框架初始化命令行界面。

1. 组织界面布局

当你拥有界面所需的所有小部件后,需要对它们进行逻辑组织和布局,以构建用户界面。在 Termdash 中,你可以使用 container.Container 类型的容器来定义仪表盘布局。启动应用程序至少需要一个容器,你可以使用多个容器来分割屏幕并组织小部件。

创建容器有两种不同的方法:
- 使用 container 包分割容器,形成二叉树布局。
- 使用 grid 包定义行和列的网格。

对于本应用程序,我们将使用网格方法来组织布局,因为这样更容易组织代码以实现类似番茄钟应用屏幕的布局。应用程序布局由三个主要行组成:
- 第一行分为两列,每列再分为两行。
- 第二行有两列。
- 第三行也有两列。

目前,我们将构建前两行,将第三行留作后续设计的摘要小部件的占位符。

以下是具体的操作步骤:
1. 在应用程序目录的 app 子目录下添加并编辑 grid.go 文件,添加包定义和导入部分:

package app

import (
    "github.com/mum4k/termdash/align"
    "github.com/mum4k/termdash/container"
    "github.com/mum4k/termdash/container/grid"
    "github.com/mum4k/termdash/linestyle"
    "github.com/mum4k/termdash/terminal/terminalapi"
)
  1. 定义 newGrid 函数来定义新的网格布局:
func newGrid(b *buttonSet, w *widgets,
    t terminalapi.Terminal) (*container.Container, error) {
    builder := grid.New()

    // 添加第一行
    builder.Add(
        grid.RowHeightPerc(30,
            grid.ColWidthPercWithOpts(30,
                []container.Option{
                    container.Border(linestyle.Light),
                    container.BorderTitle("Press Q to Quit"),
                },
                grid.RowHeightPerc(80,
                    grid.Widget(w.donTimer)),
                grid.RowHeightPercWithOpts(20,
                    []container.Option{
                        container.AlignHorizontal(align.HorizontalCenter),
                    },
                    grid.Widget(w.txtTimer,
                        container.AlignHorizontal(align.HorizontalCenter),
                        container.AlignVertical(align.VerticalMiddle),
                        container.PaddingLeftPercent(49),
                    ),
                ),
            ),
            grid.ColWidthPerc(70,
                grid.RowHeightPerc(80,
                    grid.Widget(w.disType, container.Border(linestyle.Light)),
                ),
                grid.RowHeightPerc(20,
                    grid.Widget(w.txtInfo, container.Border(linestyle.Light)),
                ),
            ),
        ),
    )

    // 添加第二行
    builder.Add(
        grid.RowHeightPerc(10,
            grid.ColWidthPerc(50,
                grid.Widget(b.btStart),
            ),
            grid.ColWidthPerc(50,
                grid.Widget(b.btPause),
            ),
        ),
    )

    // 添加第三行
    builder.Add(
        grid.RowHeightPerc(60),
    )

    gridOpts, err := builder.Build()
    if err != nil {
        return nil, err
    }

    c, err := container.New(t, gridOpts...)
    if err != nil {
        return nil, err
    }

    return c, nil
}
2. 构建交互式界面

现在我们已经准备好了小部件和布局,接下来将所有内容组合在一起,创建一个启动和管理界面的应用程序。Termdash 提供了两种运行仪表盘应用程序的方式:
- termdash.Run :自动启动和管理应用程序,Termdash 会定期重绘屏幕并处理调整大小。
- termdash.NewController :创建一个新的 termdash.Controller 实例,允许你手动管理应用程序的重绘和调整大小过程。

对于番茄钟应用程序,我们将使用 termdash.Controller 实例来管理应用程序,因为 termdash.Run 会在应用程序停止或暂停时持续消耗系统资源。

以下是具体的操作步骤:
1. 确保你位于应用程序目录的 app 子目录下:

$ cd $HOME/pragprog.com/rggo/interactiveTools/pomo/app
  1. 创建并编辑 app.go 文件,添加包定义和导入部分:
package app

import (
    "context"
    "image"
    "time"

    "github.com/mum4k/termdash"
    "github.com/mum4k/termdash/terminal/tcell"
    "github.com/mum4k/termdash/terminal/terminalapi"
    "pragprog.com/rggo/interactiveTools/pomo/pomodoro"
)

type App struct {
    ctx        context.Context
    controller *termdash.Controller
    redrawCh    chan bool
    errorCh     chan error
    term       *tcell.Terminal
    size       image.Point
}

func New(config *pomodoro.IntervalConfig) (*App, error) {
    ctx, cancel := context.WithCancel(context.Background())

    quitter := func(k *terminalapi.Keyboard) {
        if k.Key == 'q' || k.Key == 'Q' {
            cancel()
        }
    }

    redrawCh := make(chan bool)
    errorCh := make(chan error)

    w, err := newWidgets(ctx, errorCh)
    if err != nil {
        return nil, err
    }

    b, err := newButtonSet(ctx, config, w, redrawCh, errorCh)
    if err != nil {
        return nil, err
    }

    term, err := tcell.New()
    if err != nil {
        return nil, err
    }

    c, err := newGrid(b, w, term)
    if err != nil {
        return nil, err
    }

    controller, err := termdash.NewController(term, c,
        termdash.KeyboardSubscriber(quitter))
    if err != nil {
        return nil, err
    }

    return &App{
        ctx:        ctx,
        controller: controller,
        redrawCh:   redrawCh,
        errorCh:    errorCh,
        term:       term,
    }, nil
}

func (a *App) resize() error {
    if a.size.Eq(a.term.Size()) {
        return nil
    }

    a.size = a.term.Size()
    if err := a.term.Clear(); err != nil {
        return err
    }

    return a.controller.Redraw()
}

func (a *App) Run() error {
    defer a.term.Close()
    defer a.controller.Close()

    ticker := time.NewTicker(2 * time.Second)
    defer ticker.Stop()

    for {
        select {
        case <-a.redrawCh:
            if err := a.controller.Redraw(); err != nil {
                return err
            }
        case err := <-a.errorCh:
            if err != nil {
                return err
            }
        case <-a.ctx.Done():
            return nil
        case <-ticker.C:
            if err := a.resize(); err != nil {
                return err
            }
        }
    }
}
3. 界面布局和交互逻辑流程图
graph TD;
    A[开始] --> B[组织界面布局];
    B --> C[构建交互式界面];
    C --> D[初始化 CLI 界面];
    D --> E[运行应用程序];
    E --> F[结束];
4. 总结

通过以上步骤,我们完成了番茄钟应用程序的界面布局和交互逻辑的开发。接下来,我们将使用 Cobra 框架初始化命令行界面,以便用户可以通过命令行参数和配置文件来启动应用程序。

交互式番茄钟应用开发指南(续)

5. 初始化 CLI 界面

现在界面和后端代码都已准备就绪,需要一种方式来启动应用程序。我们将使用 Cobra 框架来处理命令行参数和配置文件。

以下是具体的操作步骤:
1. 切换回应用程序的根目录,并使用 Cobra 框架生成初始样板代码:

$ cd $HOME/pragprog.com/rggo/interactiveTools/pomo
$ cobra init --pkg-name pragprog.com/rggo/interactiveTools/pomo
  1. go.mod 中添加对 Cobra v1.1.3 的依赖,并下载所需的依赖项:
$ go mod edit --require github.com/spf13/cobra@v1.1.3
$ go mod tidy
  1. cmd 子目录下创建并编辑 repoinmemory.go 文件,添加包定义和导入部分,并定义 getRepo 函数:
package cmd

import (
    "pragprog.com/rggo/interactiveTools/pomo/pomodoro"
    "pragprog.com/rggo/interactiveTools/pomo/pomodoro/repository"
)

func getRepo() (pomodoro.Repository, error) {
    return repository.NewInMemoryRepo(), nil
}
  1. 打开 Cobra 生成的 cmd/root.go 文件,更新导入部分:
import (
    "fmt"
    "io"
    "os"
    "time"

    "github.com/spf13/cobra"
    "pragprog.com/rggo/interactiveTools/pomo/app"
    "pragprog.com/rggo/interactiveTools/pomo/pomodoro"

    homedir  "github.com/mitchellh/go-homedir"
    "github.com/spf13/viper"
)
  1. 编辑 init 函数,添加三个命令行参数,允许用户自定义番茄钟、短休息和长休息的间隔时长:
func init() {
    cobra.OnInitialize(initConfig)

    rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "",
        "config file (default is $HOME/.pomo.yaml)")

    rootCmd.Flags().DurationP("pomo", "p", 25*time.Minute,
                              "Pomodoro duration")
    rootCmd.Flags().DurationP("short", "s", 5*time.Minute,
                              "Short break duration")
    rootCmd.Flags().DurationP("long", "l", 15*time.Minute,
                              "Long break duration")

    viper.BindPFlag("pomo", rootCmd.Flags().Lookup("pomo"))
    viper.BindPFlag("short", rootCmd.Flags().Lookup("short"))
    viper.BindPFlag("long", rootCmd.Flags().Lookup("long"))
}
  1. 更新 rootCmd 命令定义,自定义命令描述,并添加 RunE 属性:
var rootCmd = &cobra.Command{
    Use:    "pomo",
    Short:  "Interactive Pomodoro Timer",
    RunE:  func (cmd *cobra.Command, args []string) error {
        repo, err := getRepo()
        if err != nil {
            return err
        }

        config := pomodoro.NewConfig(
            repo,
            viper.GetDuration("pomo"),
            viper.GetDuration("short"),
            viper.GetDuration("long"),
        )
        return rootAction(os.Stdout, config)
    },
}
  1. 定义 rootAction 函数来启动应用程序:
func rootAction(out io.Writer, config *pomodoro.IntervalConfig) error {
    a, err := app.New(config)
    if err != nil {
        return err
    }

    return a.Run()
}
6. 命令行参数和配置文件说明
参数 缩写 描述 默认值
–config 配置文件路径 $HOME/.pomo.yaml
–pomo -p 番茄钟时长 25 分钟
–short -s 短休息时长 5 分钟
–long -l 长休息时长 15 分钟

用户可以通过命令行参数来临时修改这些值,也可以在配置文件(如 $HOME/.pomo.yaml )中永久修改。例如,以下是一个配置文件示例:

pomo: 10m
short: 2m
long: 4m
7. 启动和测试应用程序

完成上述步骤后,我们可以构建并运行应用程序:
1. 切换回应用程序的根目录,使用 go build 构建应用程序:

$ cd $HOME/pragprog.com/rggo/interactiveTools/pomo
$ go build
  1. 运行应用程序:
$ ./pomo
  1. 若要查看所有选项,可以使用 --help 参数:
$ ./pomo --help
8. 应用程序运行流程图
graph TD;
    A[启动应用程序] --> B[解析命令行参数和配置文件];
    B --> C[获取存储库];
    C --> D[创建 Pomodoro 配置];
    D --> E[创建 App 实例];
    E --> F[运行 App];
    F --> G[处理重绘和调整大小];
    G --> H[等待用户输入];
    H --> I{是否退出};
    I -- 是 --> J[关闭应用程序];
    I -- 否 --> G;
9. 总结

通过以上步骤,我们完成了交互式番茄钟应用程序的开发,包括界面布局、交互逻辑和命令行界面的初始化。用户可以通过命令行参数和配置文件来定制番茄钟、短休息和长休息的间隔时长。由于使用了内存存储库,每次启动应用程序时,间隔都会重置为番茄钟间隔。后续可以将历史记录保存到 SQL 数据库中,以进一步提升应用程序的功能。同时,用户可以通过调整终端窗口大小来测试应用程序的调整大小功能,应用程序会相应地进行调整。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值