From e0cbd216911281cae10efdcf780095fe9112d7fc Mon Sep 17 00:00:00 2001 From: Xingyu Wang Date: Thu, 7 Sep 2023 23:18:44 +0800 Subject: [PATCH] ATRP @wxy https://linux.cn/article-16170-1.html --- ...ï¸ Learn TclTk and Wish with this simple game.md | 226 ++++++++++++++++++ ...ï¸ Learn TclTk and Wish with this simple game.md | 219 ----------------- 2 files changed, 226 insertions(+), 219 deletions(-) create mode 100644 published/20230424.3 â­ï¸â­ï¸ Learn TclTk and Wish with this simple game.md delete mode 100644 sources/tech/20230424.3 â­ï¸â­ï¸ Learn TclTk and Wish with this simple game.md diff --git a/published/20230424.3 â­ï¸â­ï¸ Learn TclTk and Wish with this simple game.md b/published/20230424.3 â­ï¸â­ï¸ Learn TclTk and Wish with this simple game.md new file mode 100644 index 0000000000..49660a998e --- /dev/null +++ b/published/20230424.3 â­ï¸â­ï¸ Learn TclTk and Wish with this simple game.md @@ -0,0 +1,226 @@ +[#]: subject: "Learn Tcl/Tk and Wish with this simple game" +[#]: via: "https://opensource.com/article/23/4/learn-tcltk-wish-simple-game" +[#]: author: "James Farrell https://opensource.com/users/jamesf" +[#]: collector: "lkxed" +[#]: translator: "ChatGPT" +[#]: reviewer: "wxy" +[#]: publisher: "wxy" +[#]: url: "https://linux.cn/article-16170-1.html" + +通过这个简å•çš„游æˆå­¦ä¹  Tcl/Tk å’Œ Wish +====== + +![][0] + +> 以下是一个简å•çš„编程项目,能够帮助你开始学习 Tcl/Tk。 + +探索 Tcl/Tk 的基础构造,包括用户输入ã€è¾“出ã€å˜é‡ã€æ¡ä»¶è¯„ä¼°ã€ç®€å•å‡½æ•°å’ŒåŸºç¡€äº‹ä»¶é©±åŠ¨ç¼–程。 + +我写这篇文章的åˆè¡·æºäºŽæˆ‘想更深入地利用基于 Tcl çš„ Expect。这让我写下了以下两篇文章:[通过编写一个简å•çš„游æˆå­¦ä¹  Tcl][1] å’Œ [通过编写一个简å•çš„游æˆå­¦ä¹  Expect][2]。 + +我进行了一些 [Ansible][3] 自动化工作,é€æ¸ç§¯ç´¯äº†ä¸€äº›æœ¬åœ°è„šæœ¬ã€‚有些脚本我频ç¹ä½¿ç”¨ï¼Œä»¥è‡³äºŽä»¥ä¸‹å¾ªçŽ¯æ“作å˜å¾—有些烦人: + +- 打开终端 +- 使用 `cd` 命令跳转至åˆé€‚的目录 +- 输入一æ¡å¸¦æœ‰è‹¥å¹²é€‰é¡¹çš„长命令å¯åŠ¨æ‰€éœ€çš„自动化æµç¨‹ + +我日常使用的是 macOS。实际上我更希望有一个èœå•é¡¹æˆ–者一个图标,能够弹出一个简å•çš„ç•Œé¢æŽ¥å—å‚数并执行我需è¦çš„æ“作,[这就åƒåœ¨ Linux çš„ KDE 中一样][4]。 + +ç»å…¸çš„ Tcl 类书ç±éƒ½åŒ…å«äº†å…³äºŽæµè¡Œçš„ Tk 扩展的文档。既然我已ç»æ·±å…¥ç ”究了这个主题,我å°è¯•ç€å¯¹å…¶ï¼ˆå³ `wish`)进行编程。 + +虽然我并éžä¸€å GUI 或者å‰ç«¯å¼€å‘者,但我å‘现 Tcl/Tk 脚本编写的方å¼ç›¸å½“直接易懂。我很高兴能é‡æ–°å®¡è§†è¿™ä¸ª UNIX 历å²çš„å¤è€ä¸”稳定的部分,这ç§æŠ€æœ¯åœ¨çŽ°ä»£å¹³å°ä¸Šä¾ç„¶æœ‰ç”¨ä¸”å¯ç”¨ã€‚ + +### 安装 Tcl/Tk + +对于 Linux 系统,你å¯ä»¥æŒ‰ç…§ä¸‹é¢çš„æ–¹å¼å®‰è£…: + +``` +$ sudo dnf install tcl +$ which wish +/bin/wish +``` + +而在 macOS 上,你å¯ä»¥é€šè¿‡ [Homebrew][5] æ¥å®‰è£…最新版的 Tcl/Tk: + +``` +$ brew install tcl-tk +$ which wish +/usr/local/bin/wish +``` + +### 编程ç†å¿µ + +许多编写游æˆçš„教程都会介ç»åˆ°å…¸åž‹çš„编程语言结构,如循环ã€æ¡ä»¶åˆ¤æ–­ã€å˜é‡ã€å‡½æ•°å’Œè¿‡ç¨‹ç­‰ç­‰ã€‚ + +在此篇文章中,我想è¦ä»‹ç»çš„是 [事件驱动编程][6]。当你的程åºä½¿ç”¨äº‹ä»¶é©±åŠ¨ç¼–程,它会进入一个特殊的内置循环,等待特定的事件å‘生。当这个特定的事件å‘生时,相应的代ç å°±ä¼šè¢«è§¦å‘,产生预期的结果。 + +这些事件å¯ä»¥åŒ…括键盘输入ã€é¼ æ ‡ç§»åŠ¨ã€ç‚¹å‡»æŒ‰é’®ã€å®šæ—¶å™¨è§¦å‘,甚至是任何你的电脑硬件能够识别的事件(å¯èƒ½æ¥è‡ªç‰¹æ®Šçš„设备)。你的程åºä¸­çš„代ç å†³å®šäº†ç”¨æˆ·çœ‹åˆ°äº†ä»€ä¹ˆï¼Œä»¥åŠç¨‹åºéœ€è¦ç›‘å¬ä»€ä¹ˆè¾“入,当这些输入被接收åŽç¨‹åºä¼šæ€Žä¹ˆåšï¼Œç„¶åŽè¿›å…¥äº‹ä»¶å¾ªçŽ¯ç­‰å¾…输入。 + +这篇文章的ç†å¿µå¹¶æ²¡æœ‰è„±ç¦»æˆ‘之å‰çš„ Tcl 文章太远。这里最大的ä¸åŒåœ¨äºŽç”¨ GUI 设置和用于处ç†ç”¨æˆ·è¾“入的事件循环替代了循环结构。其他的ä¸åŒåˆ™æ˜¯ GUI å¼€å‘需è¦é‡‡å–çš„å„ç§æ–¹å¼æ¥åˆ¶ä½œä¸€ä¸ªå¯ç”¨çš„用户界é¢ã€‚在采用 Tk GUI å¼€å‘的时候,你需è¦äº†è§£ä¸¤ä¸ªåŸºç¡€çš„概念:部件widget和几何管ç†å™¨geometry manager。 + +部件是构æˆå¯è§†åŒ–元素的 UI 元素,通过这些元素用户å¯ä»¥ä¸Žç¨‹åºè¿›è¡Œäº¤äº’。这其中包括了按钮ã€æ–‡æœ¬åŒºåŸŸã€æ ‡ç­¾å’Œæ–‡æœ¬è¾“入框。部件还包括了一些选项选择,如èœå•ã€å¤é€‰æ¡†ã€å•é€‰æŒ‰é’®ç­‰ã€‚最åŽï¼Œéƒ¨ä»¶ä¹ŸåŒ…括了其他的å¯è§†åŒ–元素,如边框和线性分隔符。 + +几何管ç†å™¨åœ¨æ”¾ç½®éƒ¨ä»¶åœ¨æ˜¾ç¤ºçª—å£ä¸­çš„ä½ç½®ä¸Šæ‰®æ¼”ç€è‡³å…³é‡è¦çš„角色。有一些ä¸åŒçš„几何管ç†å™¨å¯ä»¥ä¾›ä½ ä½¿ç”¨ã€‚在这篇文章中,我主è¦ä½¿ç”¨äº† `grid` 几何æ¥è®©éƒ¨ä»¶åœ¨æ•´é½çš„行中进行布局。我会在这篇文章的结尾地方解释一些几何管ç†å™¨çš„ä¸åŒä¹‹å¤„。 + +### 用 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` 执行。接下æ¥ï¼Œåˆ›å»ºäº†å‡ ä¸ªå…¨å±€å˜é‡ã€‚这里我使用全部大写字æ¯å®šä¹‰å…¨å±€å˜é‡ï¼Œè¿™äº›å˜é‡å°†ç»‘定到跟踪这些值的窗å£å°éƒ¨ä»¶ï¼ˆ`LOW`ã€`HIGH`等等)。 + +全局å˜é‡ `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 {.quit invoke} + } + + set GUESS "" +} +``` + +这是主è¦çš„猜数逻辑循环。`global` 语å¥è®©æˆ‘们能够修改在文件开头创建的全局å˜é‡ï¼ˆå…³äºŽæ­¤ä¸»é¢˜åŽé¢å°†ä¼šæœ‰æ›´å¤šè§£é‡Šï¼‰ã€‚这个æ¡ä»¶åˆ¤æ–­å¯»æ‰¾å…¥åŠ›èŒƒå›´åœ¨ 1 至 100 之外以åŠå·²ç»è¢«ç”¨æˆ·çŒœè¿‡çš„值。有效的猜测和éšæœºå€¼è¿›è¡Œæ¯”较。`LOW` å’Œ `HIGH` 的猜测会被追踪,作为 UI 中的全局å˜é‡è¿›è¡ŒæŠ¥å‘Šã€‚在æ¯ä¸€æ­¥ï¼Œå…¨å±€ `STATUS` å˜é‡éƒ½ä¼šè¢«æ›´æ–°ï¼Œè¿™ä¸ªçŠ¶æ€ä¿¡æ¯ä¼šè‡ªåŠ¨åœ¨ UI 中显示。 + +对于正确的猜测,`destroy` 语å¥ä¼šç§»é™¤ “Guess†按钮以åŠè¾“入窗å£ï¼Œå¹¶é‡æ–°ç»‘定回车键,以激活 “Quit†按钮。 + +最åŽçš„è¯­å¥ `set GUESS ""` 用于在下一个猜测之å‰æ¸…空输入窗å£ã€‚ + +``` +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 {.guess invoke} +``` + +这是设置用户界é¢çš„部分。å‰å…­ä¸ªæ ‡ç­¾è¯­å¥åœ¨ä½ çš„ UI 上创建了ä¸åŒçš„文本展示元素,`-textvariable` 选项监控给定的å˜é‡ï¼Œå¹¶è‡ªåŠ¨æ›´æ–°æ ‡ç­¾çš„值,这展示了全局å˜é‡ `LOW`ã€`HIGH`ã€`STATUS` 的绑定。 + +`button` 行创建了 “Guess†和 “Quit†按钮, `-command` 选项设定了当按钮被按下时è¦æ‰§è¡Œçš„æ“作。按下 “Guess†按钮执行了上é¢çš„ `check_guess` 函数以检查用户输入的值。 + +`entry` 部件更有趣。它创建了一个三字符宽的输入框,并将输入绑定到 `GUESS` 全局å˜é‡ã€‚它还通过 `-validatecommand` 选项设置了验è¯ï¼Œé˜»æ­¢è¾“入部件接收除数字以外的任何内容。 + +`focus` 命令是用户界é¢çš„一项改进,使程åºå¯åŠ¨æ—¶è¾“入部件处于激活状æ€ã€‚没有此命令,你需è¦å…ˆç‚¹å‡»è¾“入部件æ‰å¯ä»¥è¾“入。 + +`bind` 命令å…许你在按下回车键时自动点击 “Guess†按钮。如果你记得 `check_guess` 中的内容,猜测正确之åŽä¼šé‡æ–°ç»‘定回车键到 “Quit†按钮。 + +最åŽï¼Œè¿™éƒ¨åˆ†è®¾å®šäº†å›¾å½¢ç”¨æˆ·ç•Œé¢çš„布局: + +``` +grid .inst +grid .low .dash .high +grid .status .result +grid .guess .entry +grid .quit +``` + +`grid` 几何管ç†å™¨è¢«é€æ­¥è°ƒç”¨ï¼Œä»¥é€æ¸æž„建出预期的用户体验。它主è¦è®¾ç½®äº†äº”行部件。å‰ä¸‰è¡Œæ˜¯æ˜¾ç¤ºä¸åŒå€¼çš„标签,第四行是 “Guess†按钮和 `entry` 部件,最åŽæ˜¯ “Quit†按钮。 + +程åºåˆ°æ­¤å·²ç»åˆå§‹åŒ–完毕,`wish` shell 进入事件循环,等待用户输入整数并按下按钮。基于其在被监视的全局å˜é‡ä¸­æ‰¾åˆ°çš„å˜åŒ–,它会更新标签。 + +注æ„,输入光标开始就在输入框中,而且按下回车键将调用适当且å¯ç”¨çš„按钮。 + +è¿™åªæ˜¯ä¸€ä¸ªåˆçº§çš„例å­ï¼ŒTcl/Tk 有许多å¯ä»¥è®©é—´éš”ã€å­—体ã€é¢œè‰²å’Œå…¶ä»–用户界é¢æ–¹é¢æ›´å…·æœ‰å¸å¼•åŠ›çš„é€‰é¡¹ï¼Œè¿™è¶…å‡ºäº†æœ¬æ–‡ä¸­ç®€å• UI 的示例。 + +è¿è¡Œè¿™ä¸ªåº”用,你å¯èƒ½ä¼šæ³¨æ„到这些部件看起æ¥å¹¶ä¸å¾ˆç²¾è‡´æˆ–现代。这是因为我正在使用原始的ç»å…¸éƒ¨ä»¶é›†ï¼Œå®ƒä»¬è®©äººå›žå¿†èµ· X Windows Motif 的时代。ä¸è¿‡ï¼Œè¿˜æœ‰ä¸€äº›é»˜è®¤çš„部件扩展,被称为主题部件,它们å¯ä»¥è®©ä½ çš„应用程åºæœ‰æ›´çŽ°ä»£ã€æ›´ç²¾è‡´çš„外观和感觉。 + +### å¯åŠ¨æ¸¸æˆï¼ + +ä¿å­˜æ–‡ä»¶ä¹‹åŽï¼Œåœ¨ç»ˆç«¯ä¸­è¿è¡Œå®ƒï¼š + +``` +$ ./numgame.wish +``` + +在这ç§æƒ…况下,我无法给出控制å°çš„输出,因此这里有一个动画 GIF æ¥å±•ç¤ºå¦‚何玩这个游æˆï¼š + +![用 Wish 编写的猜数游æˆ][7] + +### 进一步了解 Tcl + +Tcl 支æŒå‘½å空间的概念,所以在这里使用的å˜é‡å¹¶ä¸å¿…须是全局的。你å¯ä»¥æŠŠç»‘定的部件å˜é‡ç»„织进ä¸åŒçš„命å空间。对于åƒè¿™æ ·çš„简å•ç¨‹åºï¼Œå¯èƒ½å¹¶ä¸å¤ªéœ€è¦è¿™ä¹ˆåšã€‚但对于更大规模的项目,你å¯èƒ½ä¼šè€ƒè™‘è¿™ç§æ–¹æ³•ã€‚ + +`proc check_guess` 函数体内有一行 `global` 代ç æˆ‘之å‰æ²¡æœ‰è§£é‡Šã€‚在 Tcl 中,所有å˜é‡éƒ½æŒ‰å€¼ä¼ é€’,函数体内引用的å˜é‡çš„范围是局部的。在这个情况下,我希望修改的是全局å˜é‡ï¼Œè€Œä¸æ˜¯å±€éƒ¨èŒƒå›´çš„版本。Tcl æ供了许多方法æ¥å¼•ç”¨å˜é‡ï¼Œåœ¨æ‰§è¡Œå †æ ˆçš„更高级别执行代ç ã€‚在一些情况下,åƒè¿™æ ·çš„简å•å¼•ç”¨å¯èƒ½å¸¦æ¥ä¸€äº›å¤æ‚性和错误,但是调用堆栈的æ“作éžå¸¸æœ‰åŠ›ï¼Œå…许 Tcl 实现那些在其他语言中实现起æ¥å¯èƒ½è¾ƒä¸ºå¤æ‚çš„æ–°çš„æ¡ä»¶å’Œå¾ªçŽ¯ç»“构。 + +最åŽï¼Œåœ¨è¿™ç¯‡æ–‡ç« ä¸­ï¼Œæˆ‘没有æ到几何管ç†å™¨ï¼Œå®ƒä»¬ç”¨äºŽä»¥ç‰¹å®šçš„顺åºå±•ç¤ºéƒ¨ä»¶ã€‚åªæœ‰è¢«æŸç§å‡ ä½•ç®¡ç†å™¨ç®¡ç†çš„部件æ‰èƒ½æ˜¾ç¤ºåœ¨å±å¹•ä¸Šã€‚grid 管ç†å™¨ç›¸å½“简æ´ï¼Œå®ƒæŒ‰ç…§ä»Žå·¦åˆ°å³çš„æ–¹å¼æ”¾ç½®éƒ¨ä»¶ã€‚我使用了五个 grid 定义æ¥åˆ›å»ºäº†äº”行。å¦å¤–还有两个几何管ç†å™¨ï¼šplace å’Œ pack。pack 管ç†å™¨å°†éƒ¨ä»¶å›´ç»•çª—å£è¾¹ç¼˜æŽ’列,而 place 管ç†å™¨å…许固定部件的ä½ç½®ã€‚除这些几何管ç†å™¨å¤–,还有一些特殊的部件,如 `canvas` ,`text` å’Œ `panedwindow`,它们å¯ä»¥å®¹çº³å¹¶ç®¡ç†å…¶ä»–部件。你å¯ä»¥åœ¨ç»å…¸çš„ Tcl/Tk å‚考指å—ï¼Œä»¥åŠ [Tk 命令][8] 文档页上找到这些部件的全é¢æ述。 + +### 继续学习编程 + +Tcl å’Œ Tk æ供了一个简å•æœ‰æ•ˆçš„方法æ¥æž„建图形用户界é¢å’Œäº‹ä»¶é©±åŠ¨åº”用程åºã€‚这个简å•çš„猜数游æˆåªæ˜¯ä½ èƒ½ç”¨è¿™äº›å·¥å…·åšåˆ°çš„事情的起点。通过继续学习和探索 Tcl å’Œ Tk,你å¯ä»¥æ‰“开构建强大且用户å‹å¥½çš„应用程åºçš„æ— æ•°å¯èƒ½æ€§ã€‚继续å°è¯•ï¼Œç»§ç»­å­¦ä¹ ï¼Œçœ‹çœ‹ä½ æ–°ä¹ å¾—çš„ Tcl å’Œ Tk 技能能带你到哪里。 + +*(题图:MJ/40621c50-6577-4033-9f3c-8013bd0286f1)* + +-------------------------------------------------------------------------------- + +via: https://opensource.com/article/23/4/learn-tcltk-wish-simple-game + +作者:[James Farrell][a] +选题:[lkxed][b] +译者:ChatGPT +校对:[wxy](https://github.com/wxy) + +本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) è£èª‰æŽ¨å‡º + +[a]: https://opensource.com/users/jamesf +[b]: https://github.com/lkxed/ +[1]: https://opensource.com/article/23/2/learn-tcl-writing-simple-game +[2]: https://opensource.com/article/23/2/learn-expect-automate-simple-game +[3]: https://www.redhat.com/en/technologies/management/ansible/what-is-ansible?intcmp=7013a000002qLH8AAM +[4]: https://opensource.com/article/23/2/linux-kde-desktop-ansible +[5]: https://opensource.com/article/20/6/homebrew-mac +[6]: https://developers.redhat.com/topics/event-driven/all?intcmp=7013a000002qLH8AAM +[7]: https://opensource.com/sites/default/files/2023-03/numgame-wish.gif +[8]: https://tcl.tk/man/tcl8.7/TkCmd/index.html +[0]: https://img.linux.net.cn/data/attachment/album/202309/07/231710i7u72ttuzlt4thhr.jpg \ No newline at end of file diff --git a/sources/tech/20230424.3 â­ï¸â­ï¸ Learn TclTk and Wish with this simple game.md b/sources/tech/20230424.3 â­ï¸â­ï¸ Learn TclTk and Wish with this simple game.md deleted file mode 100644 index aeec32b279..0000000000 --- a/sources/tech/20230424.3 â­ï¸â­ï¸ Learn TclTk and Wish with this simple game.md +++ /dev/null @@ -1,219 +0,0 @@ -[#]: subject: "Learn Tcl/Tk and Wish with this simple game" -[#]: via: "https://opensource.com/article/23/4/learn-tcltk-wish-simple-game" -[#]: author: "James Farrell https://opensource.com/users/jamesf" -[#]: collector: "lkxed" -[#]: translator: " " -[#]: reviewer: " " -[#]: publisher: " " -[#]: url: " " - -Learn Tcl/Tk and Wish with this simple game -====== - -Explore the basic language constructs of Tcl/Tk, which include user input, output, variables, conditional evaluation, simple functions, and basic event driven programming. - -My path to writing this article started with a desire to make advanced use of Expect which is based on Tcl. Those efforts resulted in these two articles: [Learn Tcl by writing a simple game][1] and [Learn Expect by writing a simple game][2]. - -I do a bit of [Ansible][3] automation and, over time have collected a number of local scripts. Some of them I use often enough that it becomes annoying to go through the cycle of: - -- Open terminal -- Use `cd` to get to the right place -- Type a long command with options to start the desired automation - -I use macOS on a daily basis. What I really wanted was a menu item or an icon to bring up a simple UI to accept parameters and run the thing I wanted to do, [like in KDE on Linux][4]. - -The classic Tcl books include documentation on the popular Tk extensions. Since I was already deep into researching this topic, I gave programming it (that is `wish`) a try. - -I've never been a GUI or front-end developer, but I found the Tcl/Tk methods of script writing fairly straight forward. I was pleased to revisit such a venerable stalwart of UNIX history, something still available and useful on modern platforms. - -### Install Tcl/Tk - -On a Linux system, you can use this: - -``` -$ sudo dnf install tcl -$ which wish -/bin/wish -``` - -On macOS, use [Homebrew][5] to install the latest Tcl/Tk: - -``` -$ brew install tcl-tk -$ which wish -/usr/local/bin/wish -``` - -### Programming concepts - -Most game-writing articles cover the typical programming language constructs such as loops, conditionals, variables, functions and procedures, and so on. - -In this article, I introduce [event-driven programming][6]. With event-driven programming, your executable enters into a special built-in loop as it waits for something specific to happen. When the specification is reached, the code is triggered to produce a certain outcome. - -These events can consist of things like keyboard input, mouse movement, button clicks, timing triggers, or nearly anything your computer hardware can recognize (perhaps even from special-purpose devices). The code in your program sets the stage from what it presents to the end user, what kinds of inputs to listen for, how to behave when these inputs are received, and then invokes the event loop waiting for input. - -The concept for this article is not far off from my other Tcl articles. The big difference here is the replacement of looping constructs with GUI setup and an event loop used to process the user input. The other differences are the various aspects of GUI development needed to make a workable user interface. With Tk GUI development, you need to look at two fundamental constructs called widgets and geometry managers. - -Widgets are UI elements that make up the visual elements you see and interact with. These include buttons, text areas, labels, and entry fields. Widgets also offer several flavors of option selections like menus, check boxes, radio buttons, and so on. Finally, widgets include other visual elements like borders and line separators. - -Geometry managers play a critical role in laying out where your widgets sit in the displayed window. There are a few different kinds of geometry managers you can use. In this article, I mainly use `grid` geometry to lay widgets out in neat rows. I explain some of the geometry manager differences at the end of this article. - -### Guess the number using wish - -This example game code is different from the examples in my other articles. I've broken it up into chunks to facilitate the explanation. - -Start by creating the basic executable script `numgame.wish`: - -``` -$ touch numgame.wish -$ chmod 755 numgame.wish -``` - -Open the file in your favorite text editor. Enter the first section of the code: - -``` -#!/usr/bin/env wish -set LOW 1 -set HIGH 100 -set STATUS "" -set GUESS "" -set num [expr round(rand()*100)] -``` - -The first line defines that the script is executable with `wish`. Then, several global variables are created. I've decided to use all upper-case variables for globals bound to widgets that watch these values (`LOW`, `HIGH` and so on). - -The `num` global is the variable set to the random value you want the game player to guess. This uses Tcl's command execution to derive the value saved to the variable: - -``` -proc Validate {var} { - if { [string is integer $var] } { - return 1 - } - return 0 -} -``` - -This is a special function to validate data entered by the user. It accepts integer numbers and rejects everything else: - -``` -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 {.quit invoke} - } - - set GUESS "" -} -``` - -This is the main loop of the value guessing logic. The `global` statement allows you to modify the global variables created at the beginning of the file (more on this topic later). The conditional looks for input that is out of bounds of 1 through 100 and also outside of values the user has already guessed. Valid guesses are compared against the random value. The `LOW` and `HIGH` guesses are tracked as global variables reported in the UI. At each stage, the global `STATUS` variable is updated. This status message is automatically reported in the UI. - -In the case of a correct guess, the `destroy` statement removes the "Guess" button and the entry widget, and re-binds the **Return** (or **Enter**) key to invoke the **Quit** button. - -The last statement `set GUESS ""` is used to clear the entry widget for the next guess: - -``` -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 {.guess invoke} -``` - -This is the section where the user interface is set up.  The first six label statements create various bits of text that display on your UI. The option `-textvariable` watches the given variable and updates the label's value automatically. This displays the bindings to global variables `LOW`, `HIGH`, and `STATUS`. - -The `button` lines set up the **Guess** and **Quit** buttons, with the `-command` option specifying what to do when the button is pressed. The **Guess** button invokes the `check_guess` procedure logic above to check the users entered value. - -The `entry` widget gets more interesting. It sets up a three-character wide input field, and binds its input to `GUESS` global. It also configures validation with the `-validatecommand` option. This prevents the entry widget from accepting anything other than numbers. - -The `focus` command is a UI polish that starts the program with the entry widget active for input. Without this, you need to click into the entry widget before you can type. - -The `bind` command is an additional UI polish that automatically clicks the **Guess** button when the **Return** key is pressed. If you remember from above in `check_guess`, guessing the correct value re-binds **Return** to the "Quit" button. - -Finally, this section defines the GUI layout: - -``` -grid .inst -grid .low .dash .high -grid .status .result -grid .guess .entry -grid .quit -``` - -The `grid` geometry manager is called in a series of steps to incrementally build up the desired user experience. It essentially sets up five rows of widgets. The first three are labels displaying various values, the fourth is the **Guess** button and `entry` widget, then finally, the **Quit** button. - -At this point, the program is initialized and the `wish` shell enters into the event loop. It waits for the user to enter integer values and press buttons. It updates labels based on changes it finds in watched global variables. - -Notice that the input cursor starts in the entry field and that pressing **Return** invokes the appropriate and available button. - -This was a simple and basic example. Tcl/Tk has a number of options that can make the spacing, fonts, colors, and other UI aspects much more pleasing than the simple UI demonstrated in this article. - -When you launch the application, you may notice that the widgets aren't very fancy or modern. That is because I'm using the original classic widget set, reminiscent of the X Windows Motif days. There are default widget extensions, called themed widgets, which can give your application a more modern and polished look and feel. - -### Play the game! - -After saving the file, run it in the terminal: - -``` -$ ./numgame.wish -``` - -In this case, I can't give console output, so here's an animated GIF to demonstrate how the game is played: - -![A guessing game written in Wish][7] - -### More about Tcl - -Tcl supports the notion of namespaces, so the variables used here need not be global. You can organize your bound widget variables into alternate namespaces. For simple programs like this, it's probably not worth it. For much larger projects, you might want to consider this approach. - -The `proc check_guess` body contains a `global` line I didn't explain. All variables in Tcl are passed by value, and variables referenced within the body are in a local scope. In this case, I wanted to modify the global variable, not a local scoped version. Tcl has a number of ways of referencing variables and executing code in execution stacks higher in the call chain. In some ways, it makes for complexities (and mistakes) for a simple reference like this. But the call stack manipulation is very powerful and allows for Tcl to implement new forms of conditional and loop constructs that would be cumbersome in other languages. - -Finally, in this article, I skipped the topic of geometry managers which are used to take widgets and place them in a specific order. Nothing can be displayed to the screen unless it is managed by some kind of geometry manager. The grid manager is fairly simple. It places widgets in a line, from left to right. I used five grid definitions to create five rows. There are two other geometry managers: place and pack. The pack manager arranges widgets around the edges of the window, and the place manager allows for fixed placement. In addition to these geometry managers, there are special widgets called `canvas`, `text`, and `panedwindow` that can hold and manage other widgets. A full description of all these can be found in the classic Tcl/Tk reference guides, and on the [Tk commands][8] documentation page. - -### Keep learning programming - -Tcl and Tk provide a straightforward and effective approach to building graphical user interfaces and event-driven applications. This simple guessing game is just the beginning when it comes to what you can accomplish with these tools. By continuing to learn and explore Tcl and Tk, you can unlock a world of possibilities for building powerful, user-friendly applications. Keep experimenting, keep learning, and see where your newfound Tcl and Tk skills can take you. - --------------------------------------------------------------------------------- - -via: https://opensource.com/article/23/4/learn-tcltk-wish-simple-game - -作者:[James Farrell][a] -选题:[lkxed][b] -译者:[译者ID](https://github.com/译者ID) -校对:[校对者ID](https://github.com/校对者ID) - -本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) è£èª‰æŽ¨å‡º - -[a]: https://opensource.com/users/jamesf -[b]: https://github.com/lkxed/ -[1]: https://opensource.com/article/23/2/learn-tcl-writing-simple-game -[2]: https://opensource.com/article/23/2/learn-expect-automate-simple-game -[3]: https://www.redhat.com/en/technologies/management/ansible/what-is-ansible?intcmp=7013a000002qLH8AAM -[4]: https://opensource.com/article/23/2/linux-kde-desktop-ansible -[5]: https://opensource.com/article/20/6/homebrew-mac -[6]: https://developers.redhat.com/topics/event-driven/all?intcmp=7013a000002qLH8AAM -[7]: https://opensource.com/sites/default/files/2023-03/numgame-wish.gif -[8]: https://tcl.tk/man/tcl8.7/TkCmd/index.html \ No newline at end of file