Tcl/Tk:抽象菜单按钮和命令的创建:Can';不要调用命令

Tcl/Tk:抽象菜单按钮和命令的创建:Can';不要调用命令,tcl,tk,Tcl,Tk,我试图将菜单按钮和与之相关的函数抽象为一个proc调用(addMenus函数如下)。以下代码正确构建菜单按钮,但按下按钮(如打开)时出错为: 错误:无效的命令名“myputs Open” 我想我没有正确使用引号。有没有关于解决这个问题的建议 还有关于改进代码的建议吗,特别是如果我想将参数传递到menubutton或菜单命令 proc myput{label}{ 放置$label } proc addMenus{mbar myargs}{ foreach{arg}$myargs{ foreac

我试图将菜单按钮和与之相关的函数抽象为一个
proc
调用(
addMenus
函数如下)。以下代码正确构建菜单按钮,但按下按钮(如打开)时出错为:

错误:无效的命令名“myputs Open”

  • 我想我没有正确使用引号。有没有关于解决这个问题的建议
  • 还有关于改进代码的建议吗,特别是如果我想将参数传递到
    menubutton
    菜单
    命令
proc myput{label}{
放置$label
}
proc addMenus{mbar myargs}{
foreach{arg}$myargs{
foreach{button options}$arg{
设置x${mbar}。[字符串到较低的${button}]
设置y${x}。菜单
菜单按钮$x-文本$button-菜单$y
包装$x-左侧
设置mdropoff[菜单$y-tearoff 0]
foreach{label command}$选项{
$mdropoff添加命令-标签$label-命令$command
}
}
}
}
#----------------------------------------
#主脚本
#----------------------------------------
wm标题。“我的Gui”
#构建包含菜单选项的框架
设定毫巴,毫巴
帧$mbar-提升的卸压-bd 2
包装$mbar-侧面顶部-填充x
#文本框作为填充
text.myout-宽40-高20
pack.myout-侧面顶部-两侧填充-展开为真
#文件菜单
设置myargs{
{ 
文件{
“打开…”{[列出myputs“打开”]}
“新建…”{[list myputs“新建”]}
“保存…”{[列出myputs“保存”]}
“另存为…”{[列出myputs“另存为”]}
} 
}
{ 
编辑{
“剪切”{[list myputs“剪切”]}
“复制”{[list myputs“复制”]}
“粘贴”{[list myputs“粘贴”]}
} 
}
}
添加菜单$mbar$myargs

该命令是在回调时计算的脚本。您的代码正在将“打开”菜单项的命令回调设置为
[list myput“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]

如何执行此操作的具体细节取决于要传递的变量类型(它们是本地变量、命名空间变量还是全局变量)以及何时展开(是在定义菜单时展开,还是在调用菜单时展开?

此命令是在回调时计算的脚本。您的代码正在将“打开”菜单项的命令回调设置为
[list myput“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]

具体的操作方法取决于要传递的变量类型(它们是本地变量、命名空间变量还是全局变量)以及何时展开(是在定义菜单时展开,还是在调用菜单时展开?)

问题恰恰在于Tcl不会像那样对嵌套在数据结构中的脚本进行任何扩展(它不能;除非您告诉它,否则它不知道它们是什么)。有几种处理方法:

  • 编写不需要的代码(在数据中使用普通的
    myput“Open”
    而不是
    [列出myput“Open”]
  • 在整个过程中使用
    列表构建数据:

    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
    使用一些技巧。从内部添加菜单

    foreach { label command } $options {
      set command [uplevel 1 [list subst $command]]
      $mdropoff add command -label $label -command $command
    }
    
    这将使您的代码看起来与您期望的一样(并扩展调用上下文中的任何嵌入变量,这通常是您想要的;如果您从未在菜单描述中使用变量–或复杂的命名空间处理–您可以使用更简单的
    set命令[subst$command]
    )。但是,当您从简单的调用转移到更基于模板的调用时,它比您以前的任何调用都要复杂得多


  • 如果您希望在某个时间发生一些替换,而在另一个时间发生一些替换,那么是时候使用helper过程了:您的大脑(以及维护代码的任何人)会感谢您。

    问题恰恰是Tcl不会对嵌套在数据结构中的脚本进行任何扩展(它不可能;在你告诉它之前,它不知道它们是什么)。有几种处理它的可能性:

  • 编写不需要的代码(在数据中使用普通的
    myput“Open”
    而不是
    [列出myput“Open”]
  • 在整个过程中使用
    列表构建数据:

    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
    }
    
    这将使您的代码看起来像li