通过这个简单的游戏学习 Tcl/Tk 和 Wish

这是一个简单的编码项目,帮助您开始使用 Tcl/Tk。
1 位读者喜欢这篇文章。
Programming keyboard.

Opensource.com

探索 Tcl/Tk 的基本语言结构,包括用户输入、输出、变量、条件评估、简单函数和基本事件驱动编程。

我撰写这篇文章的起因是渴望深入使用基于 Tcl 的 Expect。这些努力促成了以下两篇文章:通过编写一个简单的游戏学习 Tcl通过编写一个简单的游戏学习 Expect

我做一些 Ansible 自动化,并且随着时间的推移收集了一些本地脚本。其中一些我经常使用,以至于经历以下循环变得很烦人:

  1. 打开终端
  2. 使用 cd 进入正确的目录
  3. 输入带有选项的长命令以启动所需的自动化

我每天都使用 macOS。我真正想要的是一个菜单项或图标,弹出一个简单的 UI 来接受参数并运行我想做的事情,就像 Linux 上的 KDE 中那样

经典的 Tcl 书籍包含关于流行的 Tk 扩展的文档。因为我已经深入研究这个主题,所以我尝试对其进行编程(即 wish)。

我从来都不是 GUI 或前端开发人员,但我发现 Tcl/Tk 的脚本编写方法相当直接。我很高兴重新审视 UNIX 历史中如此古老而坚定的支柱,它在现代平台上仍然可用且有用。

安装 Tcl/Tk

在 Linux 系统上,您可以使用这个

$ sudo dnf install tcl
$ which wish
/bin/wish

在 macOS 上,使用 Homebrew 安装最新的 Tcl/Tk

$ brew install tcl-tk
$ which wish
/usr/local/bin/wish

编程概念

大多数游戏编写文章都涵盖了典型的编程语言结构,例如循环、条件、变量、函数和过程等等。

在本文中,我介绍了 事件驱动编程。使用事件驱动编程,您的可执行文件进入一个特殊的内置循环,等待特定事件发生。当达到规范时,代码被触发以产生特定的结果。

这些事件可以包括键盘输入、鼠标移动、按钮点击、定时触发,或者几乎任何您的计算机硬件可以识别的东西(甚至可能来自专用设备)。程序中的代码设置了呈现给最终用户的舞台,监听哪些类型的输入,接收到这些输入时的行为方式,然后调用事件循环等待输入。

本文的概念与我的其他 Tcl 文章相差不远。这里最大的区别是用 GUI 设置和用于处理用户输入的事件循环替换了循环结构。其他区别是制作可用的用户界面所需的 GUI 开发的各个方面。使用 Tk GUI 开发,您需要查看两个基本结构,称为 widgets 和 geometry managers(窗口小部件和几何管理器)。

Widgets(窗口小部件)是构成您看到和与之交互的视觉元素的 UI 元素。这些包括按钮、文本区域、标签和输入字段。Widgets 还提供多种选项选择,如菜单、复选框、单选按钮等等。最后,widgets 还包括其他视觉元素,如边框和分隔线。

Geometry managers(几何管理器)在布局您的 widgets 在显示窗口中的位置方面起着至关重要的作用。您可以使用几种不同类型的几何管理器。在本文中,我主要使用 grid 几何管理器以整齐的行来布局 widgets。我在本文末尾解释了一些几何管理器的差异。

使用 wish 猜数字

这个示例游戏代码与我的其他文章中的示例不同。我已将其分解为多个块,以便于解释。

首先创建基本的可执行脚本 numgame.wish

$ touch numgame.wish
$ chmod 755 numgame.wish

在您最喜欢的文本编辑器中打开文件。输入代码的第一部分

#!/usr/bin/env wish
set LOW 1
set HIGH 100
set STATUS ""
set GUESS ""
set num [expr round(rand()*100)]

第一行定义脚本可以使用 wish 执行。然后,创建几个全局变量。我决定对绑定到监视这些值的 widgets 的全局变量使用全大写变量(LOWHIGH 等)。

num 全局变量设置为您希望游戏玩家猜测的随机值。这使用 Tcl 的命令执行来导出保存到变量的值

proc Validate {var} {
    if { [string is integer $var] } {
        return 1
    }
    return 0
}

这是一个特殊的函数,用于验证用户输入的数据。它接受整数数字并拒绝其他所有内容

proc check_guess {guess num} {
    global STATUS LOW HIGH GUESS

    if { $guess < $LOW } {
        set STATUS "What?"
    } elseif { $guess > $HIGH } {
        set STATUS "Huh?"
    } elseif { $guess < $num } {
        set STATUS "Too low!"
        set LOW $guess
    } elseif { $guess > $num } {
        set STATUS "Too high!"
        set HIGH $guess
    } else {
        set LOW $guess
        set HIGH $guess
        set STATUS "That's Right!"
        destroy .guess .entry
        bind all <Return> {.quit invoke}
    }

    set GUESS ""
}

这是值猜测逻辑的主循环。global 语句允许您修改在文件开头创建的全局变量(稍后会详细介绍)。条件语句查找超出 1 到 100 范围以及超出用户已猜测值的输入。有效的猜测与随机值进行比较。LOWHIGH 猜测作为 UI 中报告的全局变量进行跟踪。在每个阶段,全局 STATUS 变量都会更新。此状态消息会自动在 UI 中报告。

在猜测正确的情况下,destroy 语句删除“Guess”按钮和输入 widget,并将 Return(或 Enter)键重新绑定以调用 Quit 按钮。

最后一个语句 set GUESS "" 用于清除输入 widget 以进行下一次猜测

label .inst -text "Enter a number between: "
label .low -textvariable LOW
label .dash -text "-"
label .high -textvariable HIGH
label .status -text "Status:"
label .result -textvariable STATUS
button .guess -text "Guess" -command { check_guess $GUESS $num }
entry .entry -width 3 -relief sunken -bd 2 -textvariable GUESS -validate all \
    -validatecommand { Validate %P }
focus .entry
button .quit -text "Quit" -command { exit }
bind all <Return> {.guess invoke}

这是设置用户界面的部分。 前六个 label 语句创建在您的 UI 上显示的各种文本位。选项 -textvariable 监视给定的变量并自动更新标签的值。这显示了与全局变量 LOWHIGHSTATUS 的绑定。

button 行设置了 GuessQuit 按钮,-command 选项指定按下按钮时要执行的操作。Guess 按钮调用上面的 check_guess 过程逻辑来检查用户输入的值。

entry widget 变得更有趣。它设置了一个三字符宽的输入字段,并将其输入绑定到 GUESS 全局变量。它还使用 -validatecommand 选项配置验证。这防止输入 widget 接受除数字以外的任何内容。

focus 命令是一种 UI 修饰,它在启动程序时使输入 widget 处于活动状态以进行输入。如果没有这个,您需要单击输入 widget 才能输入。

bind 命令是一个额外的 UI 修饰,当按下 Return 键时,它会自动单击 Guess 按钮。如果您还记得上面 check_guess 中的内容,猜测正确的值会将 Return 重新绑定到“Quit”按钮。

最后,本节定义了 GUI 布局

grid .inst
grid .low .dash .high
grid .status .result
grid .guess .entry
grid .quit

grid 几何管理器在一系列步骤中被调用,以逐步构建所需的用户体验。它基本上设置了五行 widgets。前三行是显示各种值的标签,第四行是 Guess 按钮和 entry widget,最后是 Quit 按钮。

此时,程序已初始化,wish shell 进入事件循环。它等待用户输入整数值并按下按钮。它根据在监视的全局变量中找到的更改来更新标签。

请注意,输入光标从输入字段开始,并且按下 Return 键会调用适当且可用的按钮。

这是一个简单而基本的示例。Tcl/Tk 有许多选项可以使间距、字体、颜色和其他 UI 方面比本文中演示的简单 UI 更令人愉悦。

当您启动应用程序时,您可能会注意到 widgets 不是非常精美或现代。那是因为我正在使用原始的经典 widget 集,让人想起 X Windows Motif 时代。有一些默认的 widget 扩展,称为主题 widgets,可以使您的应用程序具有更现代和精致的外观和感觉。

玩游戏!

保存文件后,在终端中运行它

$ ./numgame.wish

在这种情况下,我无法给出控制台输出,所以这是一个动画 GIF,演示了游戏的玩法

A guessing game written in Wish

(James Farrell,CC BY-SA 4.0)

更多关于 Tcl

Tcl 支持命名空间的概念,因此此处使用的变量不必是全局变量。您可以将绑定的 widget 变量组织到备用命名空间中。对于像这样的简单程序,可能不值得这样做。对于更大的项目,您可能需要考虑这种方法。

proc check_guess 主体包含一个我没有解释的 global 行。Tcl 中的所有变量都是按值传递的,并且主体中引用的变量都在本地作用域中。在这种情况下,我想修改全局变量,而不是本地作用域版本。Tcl 有多种方法可以引用变量并在调用链中更高执行堆栈中执行代码。在某些方面,对于像这样的简单引用,这会造成复杂性(和错误)。但是调用堆栈操作非常强大,并允许 Tcl 实现新的条件和循环结构形式,这在其他语言中会很麻烦。

最后,在本文中,我跳过了几何管理器的主题,几何管理器用于获取 widgets 并将它们按特定顺序放置。除非由某种几何管理器管理,否则任何内容都无法显示到屏幕上。grid 管理器相当简单。它将 widgets 从左到右排列成一行。我使用了五个 grid 定义来创建五行。还有另外两个几何管理器:place 和 pack。pack 管理器将 widgets 排列在窗口的边缘周围,而 place 管理器允许固定放置。除了这些几何管理器之外,还有称为 canvastextpanedwindow 的特殊 widgets,它们可以容纳和管理其他 widgets。所有这些的完整描述都可以在经典的 Tcl/Tk 参考指南以及 Tk 命令 文档页面中找到。

继续学习编程

Tcl 和 Tk 提供了一种直接有效的方法来构建图形用户界面和事件驱动的应用程序。当谈到您可以使用这些工具完成什么时,这个简单的猜谜游戏仅仅是开始。通过继续学习和探索 Tcl 和 Tk,您可以解锁构建强大、用户友好的应用程序的无限可能。继续实验,继续学习,看看您新获得的 Tcl 和 Tk 技能能带您走向何方。

标签
James Farrell
我是一位资深的 UNIX 系统管理员和开源倡导者。近年来,我的主要关注点一直是 Linux 和 FreeBSD 系统管理、网络、电信以及 SAN/存储管理。我热爱构建基础设施、将系统连接在一起、创建流程以及将人们聚集在一起以支持他们的技术努力。

评论已关闭。

Creative Commons License本作品根据 Creative Commons Attribution-Share Alike 4.0 International License 获得许可。
© . All rights reserved.