2011-07-18 34 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