Tcl / Tk: абстракция создания кнопок и команд меню: не могу вызывать команды - PullRequest
0 голосов
/ 19 июля 2011

Я пытаюсь абстрагировать свои кнопки меню и связанные с ним функции в один вызов proc (функция addMenus ниже). Следующий код правильно строит кнопки меню, но при нажатии кнопок (скажем, Open) он выдает ошибку как:

Ошибка: неверное имя команды "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

Ответы [ 2 ]

3 голосов
/ 19 июля 2011

Команда представляет собой скрипт, который оценивается во время обратного вызова. Ваш код устанавливает для команды обратного вызова для пункта меню «Открыть» значение [list myputs "Open"], которое при вводе в оболочке выдаст то же сообщение об ошибке.

Использование [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]

Особенности того, как вы это сделаете, зависят от того, какие переменные вы хотите передать (локальные, пространственные или глобальные) или когда они должны быть расширены (хотите ли вы, чтобы они были расширены во время определения вашего меню или когда он вызывается?)

2 голосов
/ 19 июля 2011

Проблема именно в том, что 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. Используйте немного хитрости с 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]). Но это значительно сложнее, чем когда-либо прежде, когда вы переходите от простых вызовов к чему-то более основанному на шаблонах.

И если вы хотите, чтобы некоторые замены происходили одновременно, а другие - в другое, пришло время использовать вспомогательные процедуры: ваш мозг (и все, кто поддерживает код) будет вам благодарен.

...