我正在尝试将与其关联的菜单按钮和功能抽象为单个proc
调用(下面的addMenus
功能)。以下代码正确构建了菜单按钮,但在按下按钮(比如打开)时,它出错为:
错误:无效的命令名称“myputs Open”
menubutton
或menu
命令?proc myputs { label } {
puts $label
}
proc addMenus { mbar myargs } {
foreach { arg } $myargs {
foreach { button options } $arg {
set x ${mbar}.[string tolower ${button}]
set y ${x}.menu
menubutton $x -text $button -menu $y
pack $x -side left
set mdropoff [menu $y -tearoff 0]
foreach { label command } $options {
$mdropoff add command -label $label -command $command
}
}
}
}
#----------------------------------------
# main script
#----------------------------------------
wm title . "My Gui"
# build the frame which contains menu options
set mbar .mbar
frame $mbar -relief raised -bd 2
pack $mbar -side top -fill x
# text box as a filler
text .myout -width 40 -height 20
pack .myout -side top -fill both -expand true
# file menu
set myargs {
{
File {
"Open ..." { [list myputs "Open"] }
"New ..." { [list myputs "New"] }
"Save ..." { [list myputs "Save"] }
"Save As ..." { [list myputs "Save As"] }
}
}
{
Edit {
"Cut" { [list myputs "Cut"] }
"Copy" { [list myputs "Copy"] }
"Paste" { [list myputs "Paste"] }
}
}
}
addMenus $mbar $myargs
答案 0 :(得分:3)
该命令是一个在回调时评估的脚本。您的代码将“打开”菜单项的命令回调设置为[list myputs "Open"]
,如果您在shell中输入将给出相同的错误消息。
在这样的小部件回调中使用[list]
通常是一种很好的做法,因为它使您不需要在普通字符串中进行各种反斜杠转义。但在这里,没有必要。 myargs可以简单地
....
File {
"Open ..." { myputs "Open" }
"New ..." { myputs "New" }
....
如果您希望命令包含要扩展的某个变量或要运行的命令,那么在某些时候您需要在正确的时间和正确的范围内进行扩展。例如,如果您的菜单定义类似于
File {
"Open ..." { myputs [getString Open] }
"New ..." { myputs [getString New] }
}
其中getString
是一个返回字符串的命令,那么你可以把你的菜单添加为
$mdropoff add command -label $label -command [uplevel #0 subst $command]
您将如何执行此操作的具体细节取决于您要传递的变量类型(是本地变量,命名空间变量还是全局变量)以及何时应该展开它们(您希望在定义菜单时扩展它们吗? ,或何时被调用?)
答案 1 :(得分:2)
问题恰恰在于Tcl没有对嵌套在这样的数据结构中的脚本进行任何扩展(它不能;在你告诉之前它不知道它们是什么)。处理它有几种可能性:
myputs "Open"
代替[list myputs "Open"]
)。在整个过程中使用list
构建数据:
set myargs [list [list "File" \
[list "Open ..." [list myputs "Open"]] \
[list "New ..." [list myputs "New"]] \
[list "Save ..." [list myputs "Save"]] \
[list "Save As ..." [list myputs "Save As"]] \
] [list "Edit" \
...
好的,它会给你反斜视(或很长的行)。
对uplevel
和subst
使用一些技巧。从addMenus
...内部
foreach { label command } $options {
set command [uplevel 1 [list subst $command]]
$mdropoff add command -label $label -command $command
}
这将使您的代码看起来与您期望的一样(并且在调用上下文中展开任何嵌入变量,这通常是您想要的;如果您从不在菜单描述中使用变量 - 或者复杂的命名空间处理 - 您可以使用更简单的set command [subst $command]
)。但是当你从简单的调用转移到更多基于模板的东西时,它比你之前的任何东西都显得显着。
如果你想要在同一时间发生一些替换,而在另一个时候发生其他替换,那么是时候使用辅助程序:你的大脑(以及维护代码的任何人)都会感谢你。