Tcl / Tk:抽象菜单按钮和命令的创建:无法调用命令

时间:2011-07-18 21:02:27

标签: tcl tk

我正在尝试将与其关联的菜单按钮和功能抽象为单个proc调用(下面的addMenus功能)。以下代码正确构建了菜单按钮,但在按下按钮(比如打开)时,它出错为:

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

  • 我认为我没有正确使用引号。关于解决这个问题的任何指示?
  • 还有关于改进代码的任何建议,特别是如果我想将参数传递给menubuttonmenu命令?
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

2 个答案:

答案 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没有对嵌套在这样的数据结构中的脚本进行任何扩展(它不能;在你告诉之前它不知道它们是什么)。处理它有几种可能性:

  1. 将您的代码编写为不需要(在数据中使用普通myputs "Open"代替[list myputs "Open"])。
  2. 在整个过程中使用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" \
        ...
    

    好的,它会给你反斜视(或很长的行)。

  3. uplevelsubst使用一些技巧。从addMenus ...内部

    foreach { label command } $options {
      set command [uplevel 1 [list subst $command]]
      $mdropoff add command -label $label -command $command
    }
    

    这将使您的代码看起来与您期望的一样(并且在调用上下文中展开任何嵌入变量,这通常是您想要的;如果您从不在菜单描述中使用变量 - 或者复杂的命名空间处理 - 您可以使用更简单的set command [subst $command])。但是当你从简单的调用转移到更多基于模板的东西时,它比你之前的任何东西都显得显着

  4. 如果你想要在同一时间发生一些替换,而在另一个时候发生其他替换,那么是时候使用辅助程序:你的大脑(以及维护代码的任何人)都会感谢你。