2011-07-18 31 views
0

我想抽取我的菜单按钮和与之相关的功能给一个单独的proc调用(addMenus功能如下)。下面的代码生成菜单按钮正常,但在按下按钮(说开)就出现了错误为:Tcl/Tk:抽象菜单按钮和命令的创建:不能调用命令

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

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

问题恰恰在于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" \ 
        ... 
    

    OK,这将让你反斜杠(或者很长的行)。

  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])。但是,当你从简单的调用转移到更多基于模板的东西时,它会比以前任何时候都要复杂的多。

如果你想一些替代一次和别人发生在另一个,它的时间来使用助手程序:你的大脑(和任何人保持代码)会感谢你。

+0

感谢您的详细解释。有没有什么文章可以链接到哪个解释这个和相关材料的细节?我没有很好的处理这个变量替代。我通常被困在“引用地狱”中。 – Anand

3

的命令是在其回调时计算的脚本。您的代码是将“打开”菜单项的命令回调设置为[list myputs "Open"],如果您在shell中输入该命令,则会给出相同的错误消息。

在窗口小部件回调中使用[list]这样的做法通常是很好的做法,因为它使您无需使用普通字符串进行各种反斜杠转义。但在这里,这不是必要的。 myargs可以简单地

.... 
File { 
"Open ..."  { myputs "Open" } 
"New ..."  { myputs "New" } 
.... 

如果你想命令包括一些变量进行扩展或要运行的命令,那么在某些时候,你需要使与扩张的情况发生,在正确的时间和正确的范围。例如,如果你的菜单定义是像

File { 
"Open ..."  { myputs [getString Open] } 
"New ..."  { myputs [getString New] } 
} 

其中getString一些命令返回一个字符串,那么你可以把作为你的菜单中选择Add行

$mdropoff add command -label $label -command [uplevel #0 subst $command] 

,你会怎么做的细节这取决于你想要传递什么类型的变量(它们是本地的,名称空间的还是全局的)以及它们何时应该被扩展(当你的菜单被定义时,或者当它被调用时,你希望它们被扩展吗?)

+0

我的变量通常是本地的。当它们被调用时它们应该被扩展。 – Anand